diff --git a/.bzrignore b/.bzrignore index 35db3c5d5ba..f3bd9d071ef 100644 --- a/.bzrignore +++ b/.bzrignore @@ -23,6 +23,7 @@ .vimrc 50 =6 +BUILD/compile-pentium-maintainer BitKeeper/etc/config BitKeeper/etc/csets BitKeeper/etc/csets-in @@ -30,6 +31,8 @@ BitKeeper/etc/csets-out BitKeeper/etc/gone BitKeeper/etc/level BitKeeper/etc/pushed +BitKeeper/post-commit +BitKeeper/post-commit-manual BitKeeper/tmp/* BitKeeper/tmp/bkr3sAHD BitKeeper/tmp/gone @@ -223,6 +226,7 @@ bkpull.log.5 bkpull.log.6 bkpush.log build.log +build_tags.sh client/insert_test client/log_event.cc client/log_event.h @@ -311,6 +315,8 @@ libmysql_r/conf_to_src libmysql_r/my_static.h libmysql_r/mysys_priv.h libmysqld/backup_dir +libmysqld/client.c +libmysqld/client_settings.h libmysqld/convert.cc libmysqld/derror.cc libmysqld/errmsg.c @@ -355,6 +361,7 @@ libmysqld/item_sum.cc libmysqld/item_timefunc.cc libmysqld/item_uniq.cc libmysqld/key.cc +libmysqld/libmysql.c libmysqld/lock.cc libmysqld/log.cc libmysqld/log_event.cc @@ -366,6 +373,7 @@ libmysqld/net_serv.cc libmysqld/opt_ft.cc libmysqld/opt_range.cc libmysqld/opt_sum.cc +libmysqld/pack.c libmysqld/password.c libmysqld/procedure.cc libmysqld/protocol.cc @@ -482,6 +490,7 @@ mysys/#mf_iocache.c# mysys/charset2html mysys/getopt.c mysys/getopt1.c +mysys/main.cc mysys/ste5KbMa mysys/test_charset mysys/test_dir @@ -585,6 +594,7 @@ stamp-h1 stamp-h2 stamp-h3 stamp-h4 +start_mysqld.sh strings/conf_to_src strings/ctype_autoconf.c strings/ctype_extra_sources.c @@ -611,6 +621,7 @@ support-files/mysql.spec tags test_xml tests/client_test +tests/connect_test thread_test tmp/* tools/my_vsnprintf.c @@ -622,7 +633,3 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl -libmysqld/client.c -libmysqld/client_settings.h -libmysqld/libmysql.c -libmysqld/pack.c diff --git a/client/mysqladmin.c b/client/mysqladmin.c index 018bcbc1963..f263d321a7b 100644 --- a/client/mysqladmin.c +++ b/client/mysqladmin.c @@ -769,9 +769,12 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) return 1; } if (argv[1][0]) - make_scrambled_password(crypted_pw,argv[1], - (find_type(argv[0], &command_typelib, 2) == - ADMIN_OLD_PASSWORD), &rand_st); + { + if (find_type(argv[0], &command_typelib, 2) == ADMIN_OLD_PASSWORD) + make_scrambled_password_323(crypted_pw, argv[1]); + else + make_scrambled_password(crypted_pw, argv[1]); + } else crypted_pw[0]=0; /* No password */ sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw); diff --git a/include/mysql.h b/include/mysql.h index d58b729bfc1..2e23a1e2f98 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -229,7 +229,9 @@ typedef struct st_mysql enum mysql_status status; my_bool free_me; /* If free in mysql_close */ my_bool reconnect; /* set to 1 if automatic reconnect */ - char scramble_buff[21]; /* New protocol requires longer scramble*/ + + /* session-wide random string */ + char scramble[SCRAMBLE_LENGTH+1]; /* Set if this is the original connection, not a master or a slave we have diff --git a/include/mysql_com.h b/include/mysql_com.h index b1c94e5c735..8d61641cf29 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -49,8 +49,15 @@ enum enum_server_command }; -#define SCRAMBLE_LENGTH 8 -#define SCRAMBLE41_LENGTH 20 +/* + Length of random string sent by server on handshake; this is also length of + obfuscated password, recieved from client +*/ +#define SCRAMBLE_LENGTH 20 +#define SCRAMBLE_LENGTH_323 8 +/* length of password stored in the db: new passwords are preceeded with '*' */ +#define SCRAMBLED_PASSWORD_CHAR_LENGTH (SCRAMBLE_LENGTH*2+1) +#define SCRAMBLED_PASSWORD_CHAR_LENGTH_323 (SCRAMBLE_LENGTH_323*2) #define NOT_NULL_FLAG 1 /* Field can't be NULL */ @@ -302,31 +309,34 @@ extern "C" { extern unsigned long max_allowed_packet; extern unsigned long net_buffer_length; -void randominit(struct rand_struct *,unsigned long seed1, - unsigned long seed2); +/* + These functions are used for authentication by client and server and + implemented in sql/password.c +*/ + +void randominit(struct rand_struct *, unsigned long seed1, + unsigned long seed2); double my_rnd(struct rand_struct *); -void make_scrambled_password(char *to,const char *password, - my_bool force_old_scramble,struct rand_struct *rand_st); -int get_password_length(my_bool force_old_scramble); -char get_password_version(const char* password); -void create_random_string(int length,struct rand_struct *rand_st,char* target); -my_bool validate_password(const char* password, const char* message, - unsigned long* salt); -void password_hash_stage1(char *to, const char *password); -void password_hash_stage2(char *to,const char *salt); -void password_crypt(const char* from,char* to, const char* password,int length); -void get_hash_and_password(unsigned long* salt, unsigned char pversion,char* hash, - unsigned char* bin_password); -void get_salt_from_password(unsigned long *res,const char *password); -void create_key_from_old_password(const char* password,char* key); -void make_password_from_salt(char *to, unsigned long *hash_res, - unsigned char password_version); -char *scramble(char *to,const char *message,const char *password, - my_bool old_ver); -my_bool check_scramble(const char *, const char *message, - unsigned long *salt,my_bool old_ver); +void create_random_string(char *to, uint length, struct rand_struct *rand_st); + +void hash_password(ulong *to, const char *password, uint password_len); +void make_scrambled_password_323(char *to, const char *password); +void scramble_323(char *to, const char *message, const char *password); +my_bool check_scramble_323(const char *, const char *message, + unsigned long *salt); +void get_salt_from_password_323(unsigned long *res, const char *password); +void make_password_from_salt_323(char *to, const unsigned long *salt); + +void make_scrambled_password(char *to, const char *password); +void scramble(char *to, const char *message, const char *password); +my_bool check_scramble(const char *reply, const char *message, + const unsigned char *hash_stage2); +void get_salt_from_password(unsigned char *res, const char *password); +void make_password_from_salt(char *to, const unsigned char *hash_stage2); + +/* end of password.c */ + char *get_tty_password(char *opt_message); -void hash_password(unsigned long *result, const char *password); const char *mysql_errno_to_sqlstate(unsigned int mysql_errno); /* Some other useful functions */ diff --git a/include/mysqld_error.h b/include/mysqld_error.h index 86bd885d4b2..9509203fdcb 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -290,4 +290,5 @@ #define ER_VARIABLE_IS_NOT_STRUCT 1271 #define ER_UNKNOWN_COLLATION 1272 #define ER_SLAVE_IGNORED_SSL_PARAMS 1273 -#define ER_ERROR_MESSAGES 274 +#define ER_SERVER_IS_IN_SECURE_AUTH_MODE 1274 +#define ER_ERROR_MESSAGES 275 diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 19cfedce51d..67576a961ef 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -604,41 +604,55 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, /* Store user into the buffer */ end=strmov(end,user)+1; - /* - We always start with old type handshake the only difference is message sent - If server handles secure connection type we'll not send the real scramble - */ - if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) + /* write scrambled password according to server capabilities */ + if (passwd[0]) { - if (passwd[0]) + if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) { - /* Prepare false scramble */ - bfill(end, SCRAMBLE_LENGTH, 'x'); - end+=SCRAMBLE_LENGTH; - *end=0; - + *end++= SCRAMBLE_LENGTH; + scramble(end, mysql->scramble, passwd); + end+= SCRAMBLE_LENGTH; + } + else + { + scramble_323(end, mysql->scramble, passwd); + end+= SCRAMBLE_LENGTH_323 + 1; } - else /* For empty password */ - *end=0; /* zero length scramble */ } else - { - /* - Real scramble is only sent to old servers. This can be blocked - by calling mysql_options(MYSQL *, MYSQL_SECURE_CONNECT, (char*) &1); - */ - end=scramble(end, mysql->scramble_buff, passwd, - (my_bool) (mysql->protocol_version == 9)); - } + *end++= '\0'; // empty password /* Add database if needed */ - end=strmov(end+1,db ? db : ""); + end= strmov(end, db ? db : "") + 1; /* Write authentication package */ simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1); - if (mysql_autenticate(mysql, passwd)) + NET *net= &mysql->net; + ulong pkt_length= net_safe_read(mysql); + + if (pkt_length == packet_error) goto error; + if (pkt_length == 1 && net->read_pos[0] == 254 && + mysql->server_capabilities & CLIENT_SECURE_CONNECTION) + { + /* + By sending this very specific reply server asks us to send scrambled + password in old format. The reply contains scramble_323. + */ + scramble_323(buff, mysql->scramble, passwd); + if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net)) + { + net->last_errno= CR_SERVER_LOST; + strmov(net->sqlstate, unknown_sqlstate); + strmov(net->last_error,ER(net->last_errno)); + goto error; + } + /* Read what server thinks about out new auth message report */ + if (net_safe_read(mysql) == packet_error) + goto error; + } + /* Free old connect information */ my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 9c848c3434f..c0608af0de2 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -30,6 +30,7 @@ show tables; Tables_in_test update mysql.user set password=old_password("gambling2") where user="test"; flush privileges; +set password=old_password('gambling3'); show tables; Tables_in_mysql columns_priv diff --git a/mysql-test/r/func_crypt.result b/mysql-test/r/func_crypt.result index 461ae1e7e09..bd4c6d41d39 100644 --- a/mysql-test/r/func_crypt.result +++ b/mysql-test/r/func_crypt.result @@ -1,15 +1,79 @@ select length(encrypt('foo', 'ff')) <> 0; length(encrypt('foo', 'ff')) <> 0 1 -select password("a",""), password("a",NULL), password("","a"), password(NULL,"a"); -password("a","") password("a",NULL) password("","a") password(NULL,"a") -*2517f7235d68d4ba2e5019c93420523101157a792c01 NULL NULL -select password("aaaaaaaaaaaaaaaa","a"), password("a","aaaaaaaaaaaaaaaa"); -password("aaaaaaaaaaaaaaaa","a") password("a","aaaaaaaaaaaaaaaa") -*2cd3b9a44e9a9994789a30f935c92f45a96c5472f381 *37c7c5c794ff144819f2531bf03c57772cd84e40db09 -select old_password('test'), length(password("1")), length(encrypt('test')), encrypt('test','aa'); -old_password('test') length(password("1")) length(encrypt('test')) encrypt('test','aa') -378b243e220ca493 45 13 aaqPiZY5xR5l. -select old_password(""), old_password(NULL), password(""), password(NULL); -old_password("") old_password(NULL) password("") password(NULL) - NULL NULL +select password('abc'); +password('abc') +*0d3ced9bec10a777aec23ccc353a8c08a633045e +select password(''); +password('') + +select old_password('abc'); +old_password('abc') +7cd2b5942be28759 +select old_password(''); +old_password('') + +select password('gabbagabbahey'); +password('gabbagabbahey') +*b0f99d2963660dd7e16b751ec9ee2f17b6a68fa6 +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +select length(password('1')); +length(password('1')) +41 +select length(encrypt('test')); +length(encrypt('test')) +13 +select encrypt('test','aa'); +encrypt('test','aa') +aaqPiZY5xR5l. +select old_password(NULL); +old_password(NULL) +NULL +select password(NULL); +password(NULL) +NULL +set global old_passwords=on; +select password(''); +password('') + +select old_password(''); +old_password('') + +select password('idkfa'); +password('idkfa') +*b669c9dac3aa6f2254b03cdef8dfdd6b2d1054ba +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +set old_passwords=on; +select password('idkfa'); +password('idkfa') +5c078dc54ca0fcca +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +set global old_passwords=off; +select password('idkfa'); +password('idkfa') +5c078dc54ca0fcca +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +set old_passwords=off; +select password('idkfa '); +password('idkfa ') +*2dc31d90647b4c1abc9231563d2236e96c9a2db2 +select password('idkfa'); +password('idkfa') +*b669c9dac3aa6f2254b03cdef8dfdd6b2d1054ba +select password(' idkfa'); +password(' idkfa') +*12b099e56bb7fe8d43c78fd834a9d1d11178d045 +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +select old_password(' i d k f a '); +old_password(' i d k f a ') +5c078dc54ca0fcca diff --git a/mysql-test/t/connect.test b/mysql-test/t/connect.test index e6ccc52f0d4..7585ff0f608 100644 --- a/mysql-test/t/connect.test +++ b/mysql-test/t/connect.test @@ -48,8 +48,9 @@ flush privileges; #connect (con1,localhost,test,gambling2,""); #show tables; connect (con1,localhost,test,gambling2,mysql); +set password=old_password('gambling3'); show tables; -connect (con1,localhost,test,gambling2,test); +connect (con1,localhost,test,gambling3,test); show tables; # Re enable this one day if error handling on connect will take place @@ -63,7 +64,9 @@ show tables; #connect (con1,localhost,test,zorro,); #--error 1045 + # remove user 'test' so that other tests which may use 'test' # do not depend on this test. + delete from mysql.user where user="test"; flush privileges; diff --git a/mysql-test/t/func_crypt.test b/mysql-test/t/func_crypt.test index c72356bda1a..c1c7090cab3 100644 --- a/mysql-test/t/func_crypt.test +++ b/mysql-test/t/func_crypt.test @@ -4,7 +4,33 @@ select length(encrypt('foo', 'ff')) <> 0; --replace_result $1$aa$4OSUA5cjdx0RUQ08opV27/ aaqPiZY5xR5l. # Test new and old password handling functions -select password("a",""), password("a",NULL), password("","a"), password(NULL,"a"); -select password("aaaaaaaaaaaaaaaa","a"), password("a","aaaaaaaaaaaaaaaa"); -select old_password('test'), length(password("1")), length(encrypt('test')), encrypt('test','aa'); -select old_password(""), old_password(NULL), password(""), password(NULL); +select password('abc'); +select password(''); +select old_password('abc'); +select old_password(''); +select password('gabbagabbahey'); +select old_password('idkfa'); +select length(password('1')); +select length(encrypt('test')); +select encrypt('test','aa'); +select old_password(NULL); +select password(NULL); +set global old_passwords=on; +select password(''); +select old_password(''); +select password('idkfa'); +select old_password('idkfa'); +set old_passwords=on; +select password('idkfa'); +select old_password('idkfa'); +set global old_passwords=off; +select password('idkfa'); +select old_password('idkfa'); + +# this test shows that new scrambles honor spaces in passwords: +set old_passwords=off; +select password('idkfa '); +select password('idkfa'); +select password(' idkfa'); +select old_password('idkfa'); +select old_password(' i d k f a '); diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index 2739c45e750..c4cdc7b52d7 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -108,7 +108,7 @@ then c_u="$c_u CREATE TABLE user (" c_u="$c_u Host char(60) binary DEFAULT '' NOT NULL," c_u="$c_u User char(16) binary DEFAULT '' NOT NULL," - c_u="$c_u Password char(45) binary DEFAULT '' NOT NULL," + c_u="$c_u Password char(41) binary DEFAULT '' NOT NULL," c_u="$c_u Select_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_u="$c_u Insert_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_u="$c_u Update_priv enum('N','Y') DEFAULT 'N' NOT NULL," diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index 7af97b9fad5..220bf4036db 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -16,7 +16,7 @@ ALTER TABLE host type=MyISAM; ALTER TABLE func type=MyISAM; ALTER TABLE columns_priv type=MyISAM; ALTER TABLE tables_priv type=MyISAM; -ALTER TABLE user change Password Password char(45) not null; +ALTER TABLE user change Password Password char(41) not null; ALTER TABLE user add File_priv enum('N','Y') NOT NULL; CREATE TABLE IF NOT EXISTS func ( name char(64) DEFAULT '' NOT NULL, diff --git a/sql-common/client.c b/sql-common/client.c index e410330d616..b4d875b8132 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1391,76 +1391,6 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) #endif /* HAVE_OPENSSL */ -/* - Handle password authentication -*/ - -my_bool mysql_autenticate(MYSQL *mysql, const char *passwd) -{ - ulong pkt_length; - NET *net= &mysql->net; - char buff[SCRAMBLE41_LENGTH]; - char password_hash[SCRAMBLE41_LENGTH]; /* Used for storage of stage1 hash */ - - /* We shall only query server if it expect us to do so */ - if ((pkt_length=net_safe_read(mysql)) == packet_error) - goto error; - - if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) - { - /* - This should always happen with new server unless empty password - OK/Error packets have zero as the first char - */ - if (pkt_length == 24 && net->read_pos[0]) - { - /* Old passwords will have '*' at the first byte of hash */ - if (net->read_pos[0] != '*') - { - /* Build full password hash as it is required to decode scramble */ - password_hash_stage1(buff, passwd); - /* Store copy as we'll need it later */ - memcpy(password_hash,buff,SCRAMBLE41_LENGTH); - /* Finally hash complete password using hash we got from server */ - password_hash_stage2(password_hash,(const char*) net->read_pos); - /* Decypt and store scramble 4 = hash for stage2 */ - password_crypt((const char*) net->read_pos+4,mysql->scramble_buff, - password_hash, SCRAMBLE41_LENGTH); - mysql->scramble_buff[SCRAMBLE41_LENGTH]=0; - /* Encode scramble with password. Recycle buffer */ - password_crypt(mysql->scramble_buff,buff,buff,SCRAMBLE41_LENGTH); - } - else - { - /* Create password to decode scramble */ - create_key_from_old_password(passwd,password_hash); - /* Decypt and store scramble 4 = hash for stage2 */ - password_crypt((const char*) net->read_pos+4,mysql->scramble_buff, - password_hash, SCRAMBLE41_LENGTH); - mysql->scramble_buff[SCRAMBLE41_LENGTH]=0; - /* Finally scramble decoded scramble with password */ - scramble(buff, mysql->scramble_buff, passwd,0); - } - /* Write second package of authentication */ - if (my_net_write(net,buff,SCRAMBLE41_LENGTH) || net_flush(net)) - { - net->last_errno= CR_SERVER_LOST; - strmov(net->sqlstate, unknown_sqlstate); - strmov(net->last_error,ER(net->last_errno)); - goto error; - } - /* Read what server thinks about out new auth message report */ - if (net_safe_read(mysql) == packet_error) - goto error; - } - } - return 0; - -error: - return 1; -} - - /* Note that the mysql argument must be initialized with mysql_init() before calling mysql_real_connect ! @@ -1553,7 +1483,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, mysql->server_status=SERVER_STATUS_AUTOCOMMIT; /* - Grab a socket and connect it to the server + Part 0: Grab a socket and connect it to the server */ #if defined(HAVE_SMEM) if ((!mysql->options.protocol || @@ -1754,6 +1684,11 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, strmov(net->last_error,ER(net->last_errno)); goto error; } + + /* + Part 1: Connection established, read and parse first packet + */ + if ((pkt_length=net_safe_read(mysql)) == packet_error) goto error; @@ -1774,8 +1709,13 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, end=strend((char*) net->read_pos+1); mysql->thread_id=uint4korr(end+1); end+=5; - strmake(mysql->scramble_buff,end,8); - end+=9; + /* + Scramble is split into two parts because old clients does not understand + long scrambles; here goes the first part. + */ + strmake(mysql->scramble, end, SCRAMBLE_LENGTH_323); + end+= SCRAMBLE_LENGTH_323+1; + if (pkt_length >= (uint) (end+1 - (char*) net->read_pos)) mysql->server_capabilities=uint2korr(end); if (pkt_length >= (uint) (end+18 - (char*) net->read_pos)) @@ -1784,6 +1724,14 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, mysql->server_language=end[2]; mysql->server_status=uint2korr(end+3); } + end+= 18; + if (pkt_length >= (uint) (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 - + (char *) net->read_pos)) + strmake(mysql->scramble+SCRAMBLE_LENGTH_323, end, + SCRAMBLE_LENGTH-SCRAMBLE_LENGTH_323); + else + mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION; + charset_number= mysql->server_language; /* Set character set */ @@ -1860,9 +1808,12 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, mysql->unix_socket=0; strmov(mysql->server_version,(char*) net->read_pos+1); mysql->port=port; - client_flag|=mysql->options.client_flag; - /* Send client information for access check */ + /* + Part 2: format and send client info to the server for access check + */ + + client_flag|=mysql->options.client_flag; client_flag|=CLIENT_CAPABILITIES; if (client_flag & CLIENT_MULTI_QUERIES) client_flag|= CLIENT_MULTI_RESULTS; @@ -1949,7 +1900,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, mysql->server_status, client_flag)); /* This needs to be changed as it's not useful with big packets */ if (user && user[0]) - strmake(end,user,32); /* Max user name */ + strmake(end,user,USERNAME_LENGTH); /* Max user name */ else read_user_name((char*) end); @@ -1958,41 +1909,30 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, #include "_cust_libmysql.h" #endif DBUG_PRINT("info",("user: %s",end)); - /* - We always start with old type handshake the only difference is message sent - If server handles secure connection type we'll not send the real scramble - */ - if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) + end= strend(end) + 1; + if (passwd[0]) { - if (passwd[0]) + if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) { - /* Prepare false scramble */ - end=strend(end)+1; - bfill(end, SCRAMBLE_LENGTH, 'x'); - end+=SCRAMBLE_LENGTH; - *end=0; + *end++= SCRAMBLE_LENGTH; + scramble(end, mysql->scramble, passwd); + end+= SCRAMBLE_LENGTH; } - else /* For empty password*/ + else { - end=strend(end)+1; - *end=0; /* Store zero length scramble */ + scramble_323(end, mysql->scramble, passwd); + end+= SCRAMBLE_LENGTH_323 + 1; } } else - { - /* - Real scramble is only sent to old servers. This can be blocked - by calling mysql_options(MYSQL *, MYSQL_SECURE_CONNECT, (char*) &1); - */ - end=scramble(strend(end)+1, mysql->scramble_buff, passwd, - (my_bool) (mysql->protocol_version == 9)); - } + *end++= '\0'; /* empty password */ + /* Add database if needed */ if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB)) { - end=strmake(end+1,db,NAME_LEN); - mysql->db=my_strdup(db,MYF(MY_WME)); - db=0; + end= strmake(end, db, NAME_LEN) + 1; + mysql->db= my_strdup(db,MYF(MY_WME)); + db= 0; } /* Write authentication package */ if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net)) @@ -2002,10 +1942,36 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, strmov(net->last_error,ER(net->last_errno)); goto error; } + + /* + Part 3: Authorization data's been sent. Now server can reply with + OK-packet, or re-request scrambled password. + */ - if (mysql_autenticate(mysql, passwd)) + if ((pkt_length=net_safe_read(mysql)) == packet_error) goto error; + if (pkt_length == 1 && net->read_pos[0] == 254 && + mysql->server_capabilities & CLIENT_SECURE_CONNECTION) + { + /* + By sending this very specific reply server asks us to send scrambled + password in old format. + */ + scramble_323(buff, mysql->scramble, passwd); + if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net)) + { + net->last_errno= CR_SERVER_LOST; + strmov(net->sqlstate, unknown_sqlstate); + strmov(net->last_error,ER(net->last_errno)); + goto error; + } + /* Read what server thinks about out new auth message report */ + if (net_safe_read(mysql) == packet_error) + goto error; + } + + if (client_flag & CLIENT_COMPRESS) /* We will use compression */ net->compress=1; diff --git a/sql/item_create.cc b/sql/item_create.cc index e3e3c021a1e..01f064b3b0f 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 16c4fccf709..304e481e86c 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 b0b9cc01bf1..978c8fa430d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1327,95 +1327,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); - 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; - } -} - -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(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); + if ((null_value=args[0]->null_value)) + return 0; + if (res->length() == 0) + return &empty_string; + 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 f64a145b136..d34ef1582d5 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 43a29914e43..5c0d87fa441 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -294,6 +294,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}, @@ -596,7 +597,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 a5d9ce5bce3..603e89f8729 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 MAX_PASSWORD_LENGTH 32 #define HOST_CACHE_SIZE 128 #define MAX_ACCEPT_RETRY 10 // Test accept this many times @@ -771,7 +768,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 6cb47a10cb0..6856427f3db 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; @@ -2819,12 +2820,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 @@ -3535,7 +3530,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 }; @@ -3845,9 +3841,10 @@ master-ssl", (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, @@ -3914,8 +3911,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...", @@ -3925,6 +3920,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, @@ -4709,7 +4707,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; @@ -4813,6 +4812,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 @@ -4934,9 +4934,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 #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 (; target0 -*/ - -int get_password_length(my_bool force_old_scramble) -{ - return (force_old_scramble) ? 16 : SHA1_HASH_SIZE*2+4+1; -} - - -/* - 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 -*/ - -char get_password_version(const char *password) -{ - if (password==NULL) return 0; - if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR; - return 0; -} - - -/* - Get integer value of Hex character - - SYNOPSIS - char_val() - X IN Character to find value for - - RETURN - Appropriate integer value -*/ - - - -static inline unsigned int char_val(char X) +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); + X >= 'A' && X <= 'Z' ? X-'A'+10 : X-'a'+10); } /* - Get Binary salt from password as in mysql.user format - + Convert password from hex string (as stored in mysql.user) to binary form. SYNOPSIS - get_salt_from_password() - res OUT Store binary salt here - password IN Password string as stored in mysql.user - - 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_password(ulong *res,const char *password) +void get_salt_from_password_323(ulong *res, const char *password) { - if (password) /* zero salt corresponds to empty password */ + res[0]= res[1]= 0; + if (password) { - if (password[0]==PVERSION41_CHAR) /* if new password */ - { - uint val=0; - uint i; - password++; /* skip version identifier */ - - /*get hashing salt from password and store in in the start of array */ - for (i=0 ; i < 4 ; i++) - val=(val << 4)+char_val(*password++); - *res++=val; - } - /* 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++); + val=(val << 4)+char_val(*password++); *res++=val; } } - return; } /* - Get string version as stored in mysql.user from salt form + Convert scrambled password from binary form to asciiz hex string. + SYNOPSIS + make_password_from_salt_323() + to OUT store resulting string password here, at least 17 bytes + salt IN password in salt format, 2 ulongs +*/ +void make_password_from_salt_323(char *to, const ulong *salt) +{ + sprintf(to,"%08lx%08lx", salt[0], salt[1]); +} + + +/* + **************** MySQL 4.1.1 authentification routines ************* +*/ + +/* + Generate string of printable random characters of requested length + SYNOPSIS + 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 +*/ + +void create_random_string(char *to, uint length, struct rand_struct *rand_st) +{ + 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'; +} + + +/* Character to use as version identifier for version 4.1 */ + +#define PVERSION41_CHAR '*' + + +/* + Convert given octet sequence to asciiz string of hex characters; + str..str+len and 'to' may not overlap. + SYNOPSIS + 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 void +octet2hex(char *to, const uint8 *str, uint len) +{ + const uint8 *str_end= str + len; + for (; str != str_end; ++str) + { + *to++= _dig_vec[(*str & 0xF0) >> 4]; + *to++= _dig_vec[*str & 0x0F]; + } + *to= '\0'; +} + + +/* + Convert given asciiz string of hex (0..9 a..f) characters to octet + sequence. + SYNOPSIS + 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) +{ + const char *str_end= str + len; + while (str < str_end) + { + register char tmp= char_val(*str++); + *to++= (tmp << 4) | char_val(*str++); + } +} + + +/* + 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 + 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 +*/ + +static void +my_crypt(char *to, const uchar *s1, const uchar *s2, uint len) +{ + const uint8 *s1_end= s1 + len; + while (s1 < s1_end) + *to++= *s1++ ^ *s2++; +} + + +/* + 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 + make_scrambled_password() + buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string + password IN NULL-terminated password string +*/ + +void +make_scrambled_password(char *to, const char *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); +} + + +/* + 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 + 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 +scramble(char *to, const char *message, const char *password) +{ + 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); +} + + +/* + 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 + 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 +*/ + +my_bool +check_scramble(const char *scramble, const char *message, + const uint8 *hash_stage2) +{ + 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); +} + + +/* + 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 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 + to OUT store resulting string here, 2*SHA1_HASH_SIZE+2 bytes + salt IN password in salt format */ -void make_password_from_salt(char *to, ulong *hash_res,uint8 password_version) +void make_password_from_salt(char *to, const uint8 *hash_stage2) { - 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'; -} - - -/* - Convert password in salted form to binary string password and hash-salt - For old password this involes one more hashing - - 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 -*/ - -void get_hash_and_password(ulong *salt, uint8 pversion, char *hash, - unsigned char *bin_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=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=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); - } -} - - -/* - Create key from old password to decode scramble - Used in 4.1 authentication with passwords stored old way - - SYNOPSIS - create_key_from_old_password() - passwd IN Password used for key generation - key OUT Created 20 bytes key - - RETURN - None -*/ - - -void create_key_from_old_password(const char *passwd, char *key) -{ - 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); -} - - -/* - Scramble string with password - Used at pre 4.1 authentication phase. - - 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 -*/ - -char *scramble(char *to,const char *message,const char *password, - my_bool old_ver) -{ - struct rand_struct rand_st; - ulong hash_pass[2],hash_message[2]; - 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; -} - - -/* - Check scrambled message - Used for pre 4.1 password handling - - 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 -*/ - -my_bool check_scramble(const char *scrambled, const char *message, - ulong *hash_pass, my_bool old_ver) -{ - struct rand_struct rand_st; - ulong hash_message[2]; - char buff[16],*to,extra; /* Big enough for check */ - const char *pos; - 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 05aee12d3d9..f32c135ab3c 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 91583759c70..d07bcd52559 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -216,6 +216,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", @@ -242,6 +243,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); @@ -432,6 +434,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, @@ -450,6 +453,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 @@ -608,6 +612,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}, @@ -628,6 +633,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 f06b5ed22d3..9c9ad071967 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -689,6 +689,9 @@ public: } }; +/* updated in sql_acl.cc */ + +extern sys_var_thd_bool sys_old_passwords; /* For sql_yacc */ struct sys_var_with_base diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 879f2293152..9794a88e9e0 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -279,3 +279,4 @@ v/* "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 784046bba68..8256bb21895 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -273,3 +273,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 c7acf37196e..d50e754d656 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -281,3 +281,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 ed19672adfa..4d8538ddd4e 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -275,3 +275,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 42c01d638b4..2d0f13bc4eb 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -275,3 +275,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 07bbed0ba4d..1cafd408556 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -270,3 +270,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 c7c5064b2ce..96cec88dfd0 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -279,3 +279,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 cfccc92d520..62a06e8cb33 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -270,3 +270,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 d90a305b035..816fb61f068 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -272,3 +272,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 9a105842e0a..229b6289776 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -270,3 +270,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 708c421715f..fbfba6cacf2 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -272,3 +272,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 4b36d49cff1..c20d9dc03fa 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -270,3 +270,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 1ca39ce9d7e..d0c65f38a1a 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -272,3 +272,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 a0791c5da0b..d7126b9391a 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -272,3 +272,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 86f8bca6aeb..befb7657fc5 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -274,3 +274,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 e7b24533f3b..668ca757b80 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -270,3 +270,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 a86548f8822..fe5ccdd2239 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -274,3 +274,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 fb415bd16c0..ea377202cc3 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -272,3 +272,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"óÅÒ×ÅÒ ÚÁÐÕÝÅÎ × ÒÅÖÉÍÅ --secure-auth (ÂÅÚÏÐÁÓÎÏÊ Á×ÔÏÒÉÚÁÃÉÉ), ÎÏ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%s@%s' ÐÁÒÏÌØ ÓÏÈÒÁÎ£Î × ÓÔÁÒÏÍ ÆÏÒÍÁÔÅ; ÎÅÏÂÈÏÄÉÍÏ ÏÂÎÏ×ÉÔØ ÆÏÒÍÁÔ ÐÁÒÏÌÑ" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index e6609d391dd..d5568edb570 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -265,3 +265,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 8c1b1b7e36b..b32533c882f 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -278,3 +278,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 67e68ae110e..27e831c9c7c 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -271,3 +271,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 ebb62940a79..fc4a8e45c44 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -270,3 +270,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 bfe041d10e4..9128ada6080 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -275,3 +275,4 @@ "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"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 ee72c97b3a9..c10f0417585 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1707,8 +1707,8 @@ file '%s')", 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/sql_acl.cc b/sql/sql_acl.cc index 9738c0c5d9e..a7fe77f6b06 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -51,7 +51,7 @@ static byte* acl_entry_get_key(acl_entry *entry,uint *length, return (byte*) entry->key; } -#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+17) +#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+USERNAME_LENGTH+1) static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs; static MEM_ROOT mem, memex; @@ -68,11 +68,53 @@ static ulong get_sort(uint count,...); static void init_check_host(void); static ACL_USER *find_acl_user(const char *host, const char *user); static bool update_user_table(THD *thd, const char *host, const char *user, - const char *new_password); + const char *new_password, uint new_password_len); static void update_hostname(acl_host_and_ip *host, const char *hostname); static bool compare_hostname(const acl_host_and_ip *host,const char *hostname, const char *ip); +/* + Convert scrambled password to binary form, according to scramble type, + Binary form is stored in user.salt. +*/ + +static +void +set_user_salt(ACL_USER *acl_user, const char *password, uint password_len) +{ + if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH) + { + get_salt_from_password(acl_user->salt, password); + acl_user->salt_len= SCRAMBLE_LENGTH; + } + else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + { + get_salt_from_password_323((ulong *) acl_user->salt, password); + acl_user->salt_len= SCRAMBLE_LENGTH_323; + } + else + acl_user->salt_len= 0; +} + +/* + This after_update function is used when user.password is less than + SCRAMBLE_LENGTH bytes. +*/ + +static void restrict_update_of_old_passwords_var(THD *thd, + enum_var_type var_type) +{ + if (var_type == OPT_GLOBAL) + { + pthread_mutex_lock(&LOCK_global_system_variables); + global_system_variables.old_passwords= 1; + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + thd->variables.old_passwords= 1; +} + + /* Read grant privileges from the privilege tables in the 'mysql' database. @@ -114,8 +156,6 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) if (!(thd=new THD)) DBUG_RETURN(1); /* purecov: inspected */ thd->store_globals(); - /* Use passwords according to command line option */ - use_old_passwords= opt_old_passwords; acl_cache->clear(1); // Clear locked hostname cache thd->db= my_strdup("mysql",MYF(0)); @@ -172,103 +212,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) { - sql_print_error("mysql.user table is not updated to new password format; Disabling new password usage until mysql_fix_privilege_tables is run"); - use_old_passwords= 1; + if (opt_secure_auth) + { + pthread_mutex_unlock(&LOCK_global_system_variables); + sql_print_error("Fatal error: mysql.user table is in old format, " + "but server started with --secure-auth option."); + goto end; + } + sys_old_passwords.after_update= restrict_update_of_old_passwords_var; + if (global_system_variables.old_passwords) + pthread_mutex_unlock(&LOCK_global_system_variables); + else + { + global_system_variables.old_passwords= 1; + pthread_mutex_unlock(&LOCK_global_system_variables); + sql_print_error("mysql.user table is not updated to new password format; " + "Disabling new password usage until " + "mysql_fix_privilege_tables is run"); + } + thd->variables.old_passwords= 1; + } + else + { + sys_old_passwords.after_update= 0; + pthread_mutex_unlock(&LOCK_global_system_variables); } allow_all_hosts=0; while (!(read_record_info.read_record(&read_record_info))) { ACL_USER user; - uint length=0; - update_hostname(&user.host,get_field(&mem, table->field[0])); - user.user=get_field(&mem, table->field[1]); - user.password=get_field(&mem, table->field[2]); - if (user.password && (length=(uint) strlen(user.password)) == 8 && - protocol_version == PROTOCOL_VERSION) + 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) { - 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 */ + 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; + } } - else /* non empty and not short passwords */ + else // password is correct { - user.pversion=get_password_version(user.password); - /* Only passwords of specific lengths depending on version are allowed */ - if ((!user.pversion && (length % 8 || length > 16)) || - (user.pversion && length != 45)) + 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 */ { - 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 */ + 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; } - } - 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 - { - user.ssl_type=SSL_TYPE_NONE; - bzero((char *)&(user.user_resource),sizeof(user.user_resource)); + else + { + user.ssl_type=SSL_TYPE_NONE; + bzero((char *)&(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; + 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); @@ -469,135 +532,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. + + 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. */ -void prepare_scramble(THD *thd, ACL_USER *acl_user,char* prepared_scramble) +int acl_getroot(THD *thd, USER_RESOURCES *mqh, + const char *passwd, uint passwd_len) { - /* 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; - -*/ - -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) -{ - 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((char*) 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 @@ -608,32 +641,32 @@ 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_verify_result(vio->ssl_) == X509_V_OK && SSL_get_peer_certificate(vio->ssl_)) - user_access=acl_user->access; + 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 || SSL_get_verify_result(vio->ssl_) != X509_V_OK) @@ -643,14 +676,13 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, 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; + 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_)); - user_access=NO_ACCESS; break; } } @@ -661,64 +693,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")); + 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); + 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 */ +#else /* HAVE_OPENSSL */ default: /* - If we don't have SSL but SSL is required for this user the - authentication should fail. - */ + If we don't have SSL but SSL is required for this user the + authentication should fail. + */ break; #endif /* HAVE_OPENSSL */ } + thd->priv_user= acl_user->user ? thd->user : (char *) ""; + *mqh= acl_user->user_resource; + + if (acl_user->host.hostname) + strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME); + else + *thd->priv_host= 0; } - - *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; - -unlock_and_exit: VOID(pthread_mutex_unlock(&acl_cache->lock)); - DBUG_RETURN(user_access); + DBUG_RETURN(res); } @@ -729,8 +755,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, @@ -766,20 +793,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; } } @@ -788,7 +804,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, @@ -799,7 +815,6 @@ static void acl_insert_user(const char *user, const char *host, ACL_USER acl_user; acl_user.user=*user ? strdup_root(&mem,user) : 0; 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); @@ -809,12 +824,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 @@ -1151,7 +1162,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)); @@ -1160,37 +1170,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)); @@ -1230,7 +1230,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); } @@ -1303,7 +1303,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; @@ -1345,7 +1345,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 */ @@ -1393,24 +1393,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); + "Password hash should be a %d-digit hexadecimal number", + MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH); DBUG_RETURN(-1); } + password_len= combo.password.length; password=combo.password.str; } @@ -1435,17 +1434,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 */ @@ -1542,10 +1544,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, @@ -1553,7 +1553,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, @@ -3024,12 +3024,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 0dc8c058b3d..0b2df8e765a 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 763408dc5c2..ba1952e533e 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -138,6 +138,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 8463c0226f5..a49162bb4ef 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -399,6 +399,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; @@ -556,10 +557,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 33e96cc2776..383b6b0f581 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -178,153 +178,179 @@ 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 + 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 - 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 + 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+1]; 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")); - /* - Old client should get nicer error message if password version is - not supported - */ - if (simple_connect && *hint_user && (*hint_user)->pversion) + net_printf(thd, ER_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) { - net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); - mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE)); + VOID(pthread_mutex_lock(&LOCK_thread_count)); + bool count_ok= thread_count < max_connections + delayed_insert_threads + || (thd->master_access & SUPER_ACL); + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (!count_ok) + { // too many connections + send_error(thd, ER_CON_COUNT_ERROR); + DBUG_RETURN(-1); + } + } + + /* 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*) ""); + + /* + 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. + */ + thd->db_access=0; + + /* Don't allow user to connect if he has done too many queries */ + if ((ur.questions || ur.updates || + ur.connections || max_user_connections) && + get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur)) + DBUG_RETURN(1); + if (thd->user_connect && thd->user_connect->user_resources.connections && + check_for_max_user_connections(thd, thd->user_connect)) + DBUG_RETURN(1); + + /* Change database if necessary: OK or FAIL is sent in mysql_change_db */ + if (db && db[0]) + { + if (mysql_change_db(thd, db)) + { + if (thd->user_connect) + decrease_user_connections(thd->user_connect); + DBUG_RETURN(-1); + } } else - { - 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 || max_user_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) || - max_user_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); + net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); + mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE)); + DBUG_RETURN(-1); } - send_ok(thd); // Ready to handle questions - thd->password= test(passwd[0]); // Remember for error messages - DBUG_RETURN(0); // ok + net_printf(thd, ER_ACCESS_DENIED_ERROR, + thd->user, + thd->host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); + mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), + thd->user, + thd->host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); + DBUG_RETURN(-1); } - /* Check for maximum allowable user connections, if the mysqld server is started with corresponding variable that is greater then 0. @@ -526,45 +552,36 @@ 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 */ - char db_buff[NAME_LEN+1]; - ACL_USER* cached_user=NULL; /* Initialise to NULL for first stage */ - DBUG_PRINT("info",("New connection received on %s", - vio_description(net->vio))); - /* Remove warning from valgrind. TODO: Fix it in password.c */ - bzero((char*) &prepared_scramble[0], sizeof(prepared_scramble)); + DBUG_PRINT("info", + ("New connection received on %s", vio_description(net->vio))); + if (!thd->host) // If TCP/IP connection { char ip[30]; if (vio_peer_addr(net->vio, ip, &thd->peer_port)) return (ER_BAD_HOST_ERROR); - if (!(thd->ip = my_strdup(ip,MYF(0)))) + if (!(thd->ip= my_strdup(ip,MYF(0)))) return (ER_OUT_OF_RESOURCES); - thd->host_or_ip=thd->ip; + thd->host_or_ip= thd->ip; #if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread) /* Fast local hostname resolve for Win32 */ if (!strcmp(thd->ip,"127.0.0.1")) @@ -600,15 +617,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); @@ -622,19 +640,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; + 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, + /* 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) @@ -723,7 +756,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)", @@ -740,13 +773,30 @@ 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) + if (thd->client_capabilities & CLIENT_INTERACTIVE) + thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; + if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && + opt_using_transactions) + net->return_status= &thd->server_status; + net->read_timeout=(uint) thd->variables.net_read_timeout; + + char *user= end; + char *passwd= strend(user)+1; + char *db= passwd; + char db_buff[NAME_LEN+1]; // buffer to store db in utf8 + /* + 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; + + /* Since 4.1 all database names are stored in utf8 */ + if (db) { - db= strend(passwd)+1; uint32 length= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info, db, strlen(db), @@ -755,71 +805,11 @@ check_connections(THD *thd) db= db_buff; } - /* 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) && - opt_using_transactions) - net->return_status= &thd->server_status; - net->read_timeout=(uint) thd->variables.net_read_timeout; - - /* Simple connect only for old clients. New clients always use secure auth */ - bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)); - - /* Check user permissions. If password failure we'll get scramble back */ - if ((res=check_user(thd, COM_CONNECT, user, passwd, db, 1, simple_connect, - simple_connect, prepared_scramble, using_password, - &cur_priv_version, - &cached_user)) < 0) - { - /* Store current used and database as they are erased with next packet */ - char tmp_user[USERNAME_LENGTH+1]; - char tmp_db[NAME_LEN+1]; - - /* If the client is old we just have to return error */ - if (simple_connect) - return -1; - - DBUG_PRINT("info",("password challenge")); - - tmp_user[0]= tmp_db[0]= 0; - if (user) - strmake(tmp_user,user,USERNAME_LENGTH); - if (db) - strmake(tmp_db,db,NAME_LEN); - - /* 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); } @@ -876,7 +866,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); @@ -1188,116 +1178,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; + /* + 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(); + + + + /* Small check for incomming packet */ + if ((uint) ((uchar*) db - net->read_pos) > packet_length) + { + send_error(thd, ER_UNKNOWN_COM_ERROR); + break; + } /* Save user and privileges */ - uint save_master_access=thd->master_access; - uint save_db_access= thd->db_access; - uint save_db_length= thd->db_length; - char *save_user= thd->user; - thd->user=NULL; /* Needed for check_user to allocate new user */ - char *save_priv_user= thd->priv_user; - char *save_db= thd->db; - USER_CONN *save_uc= thd->user_connect; - bool simple_connect; - bool using_password; - char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */ - char tmp_user[USERNAME_LENGTH+1]; - char tmp_db[NAME_LEN+1]; - ACL_USER* cached_user ; /* Cached user */ - uint cur_priv_version; /* Cached grant version */ - int res; - ulong pkt_len= 0; /* Length of reply packet */ - - bzero((char*) prepared_scramble, sizeof(prepared_scramble)); - /* Small check for incomming packet */ - - if ((uint) ((uchar*) db - net->read_pos) > packet_length) - goto restore_user_err; - - /* Now we shall basically perform authentication again */ - - /* We can get only old hash at this point */ - if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH) - goto restore_user_err; - - cached_user= NULL; - - /* Simple connect only for old clients. New clients always use sec. auth*/ - simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)); - - /* Store information if we used password. passwd will be dammaged */ - using_password=test(passwd[0]); - - if (simple_connect) /* Restore scramble for old clients */ - memcpy(thd->scramble,thd->old_scramble,9); - - /* - 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) + 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; + int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, false); - /* Bad luck we shall restore old user */ -restore_user_err: - send_error(thd, ER_UNKNOWN_COM_ERROR); - -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 */ @@ -3247,7 +3195,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_yacc.yy b/sql/sql_yacc.yy index 076af38cf62..254ca9677bf 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -506,6 +506,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 @@ -2607,9 +2608,13 @@ simple_expr: | NOW_SYM '(' expr ')' { $$= new Item_func_now_local($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 ')' @@ -4517,6 +4522,7 @@ keyword: | NO_SYM {} | NONE_SYM {} | OFFSET_SYM {} + | OLD_PASSWORD {} | OPEN_SYM {} | PACK_KEYS_SYM {} | PARTIAL {} @@ -4741,15 +4747,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; } ; @@ -5057,14 +5063,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