=== modified file 'client/mysqltest.cc' --- a/client/mysqltest.cc 2012-11-08 13:24:35 +0000 +++ b/client/mysqltest.cc 2013-01-24 23:17:39 +0000 @@ -3908,7 +3908,10 @@ cur_con->name, ds_user.str, ds_passwd.str, ds_db.str)); if (mysql_change_user(mysql, ds_user.str, ds_passwd.str, ds_db.str)) - die("change user failed: %s", mysql_error(mysql)); + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), &ds_res); + else + handle_no_error(command); dynstr_free(&ds_user); dynstr_free(&ds_passwd); === added file 'mysql-test/r/change_user_notembedded.result' --- a/mysql-test/r/change_user_notembedded.result 1970-01-01 00:00:00 +0000 +++ b/mysql-test/r/change_user_notembedded.result 2013-01-24 23:17:39 +0000 @@ -0,0 +1,5 @@ +ERROR 28000: Access denied for user 'foo'@'localhost' (using password: NO) +ERROR 28000: Access denied for user 'foo'@'localhost' (using password: NO) +ERROR 28000: Access denied for user 'foo'@'localhost' (using password: NO) +ERROR 08S01: Unknown command +ERROR 08S01: Unknown command === modified file 'mysql-test/r/mysqltest.result' --- a/mysql-test/r/mysqltest.result 2011-11-24 16:48:58 +0000 +++ b/mysql-test/r/mysqltest.result 2013-01-24 23:17:39 +0000 @@ -847,9 +847,9 @@ b varchar(255) YES NULL c datetime YES NULL drop table t1; -mysqltest: At line 1: change user failed: Unknown database 'inexistent' -mysqltest: At line 1: change user failed: Access denied for user 'inexistent'@'localhost' (using password: NO) -mysqltest: At line 1: change user failed: Access denied for user 'root'@'localhost' (using password: YES) +mysqltest: At line 1: query 'change_user root,,inexistent' failed: 1049: Unknown database 'inexistent' +mysqltest: At line 1: query 'change_user inexistent,,test' failed: 1045: Access denied for user 'inexistent'@'localhost' (using password: NO) +mysqltest: At line 1: query 'change_user root,inexistent,test' failed: 1045: Access denied for user 'root'@'localhost' (using password: YES) REPLACED_FILE1.txt file1.txt file2.txt === added file 'mysql-test/t/change_user_notembedded.test' --- a/mysql-test/t/change_user_notembedded.test 1970-01-01 00:00:00 +0000 +++ b/mysql-test/t/change_user_notembedded.test 2013-01-24 23:17:39 +0000 @@ -0,0 +1,24 @@ +source include/not_embedded.inc; + +# +# MDEV-3915 COM_CHANGE_USER allows fast password brute-forcing +# +# only three failed change_user per connection. +# successful change_user do NOT reset the counter +# +connect (test,localhost,root,,); +connection test; +--error 1045 +change_user foo,bar; +--error 1045 +change_user foo; +change_user; +--error 1045 +change_user foo,bar; +--error 1047 +change_user foo,bar; +--error 1047 +change_user; +disconnect test; +connection default; + === modified file 'sql/sql_class.cc' --- a/sql/sql_class.cc 2012-08-22 14:13:54 +0000 +++ b/sql/sql_class.cc 2013-01-24 23:17:39 +0000 @@ -675,6 +675,7 @@ warning_info(&main_warning_info), stmt_da(&main_da), global_disable_checkpoint(0), + failed_com_change_user(0), is_fatal_error(0), transaction_rollback_request(0), is_fatal_sub_stmt_error(0), === modified file 'sql/sql_class.h' --- a/sql/sql_class.h 2013-01-21 23:23:40 +0000 +++ b/sql/sql_class.h 2013-01-24 23:17:39 +0000 @@ -1865,6 +1865,7 @@ bool no_errors; uint8 password; + uint8 failed_com_change_user; /** Set to TRUE if execution of the current compound statement can not continue. In particular, disables activation of === modified file 'sql/sql_parse.cc' --- a/sql/sql_parse.cc 2012-08-22 14:13:54 +0000 +++ b/sql/sql_parse.cc 2013-01-24 23:17:39 +0000 @@ -1157,7 +1158,17 @@ thd->security_ctx->user= 0; thd->user_connect= 0; - rc= acl_authenticate(thd, 0, packet_length); + /* + to limit COM_CHANGE_USER ability to brute-force passwords, + we only allow three unsuccessful COM_CHANGE_USER per connection. + */ + if (thd->failed_com_change_user >= 3) + { + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); + rc= 1; + } + else + rc= acl_authenticate(thd, 0, packet_length); MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd); if (rc) { @@ -1170,6 +1183,8 @@ thd->variables.collation_connection= save_collation_connection; thd->variables.character_set_results= save_character_set_results; thd->update_charset(); + thd->failed_com_change_user++; + my_sleep(1000000); } else { === modified file 'tests/mysql_client_test.c' --- a/tests/mysql_client_test.c 2013-01-10 12:54:04 +0000 +++ b/tests/mysql_client_test.c 2013-01-24 23:17:39 +0000 @@ -15386,6 +15386,7 @@ const char *pw= "password"; const char *db= "mysqltest_user_test_database"; int rc; + MYSQL* conn; DBUG_ENTER("test_change_user"); myheader("test_change_user"); @@ -15429,149 +15430,173 @@ rc= mysql_query(mysql, buff); myquery(rc); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); /* Try some combinations */ - rc= mysql_change_user(mysql, NULL, NULL, NULL); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - - rc= mysql_change_user(mysql, "", NULL, NULL); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, "", "", NULL); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, "", "", ""); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, NULL, "", ""); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - - rc= mysql_change_user(mysql, NULL, NULL, ""); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, "", NULL, ""); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_pw, NULL, ""); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_pw, "", ""); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_pw, "", NULL); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_pw, NULL, NULL); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_pw, "", db); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_pw, NULL, db); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_pw, pw, db); - myquery(rc); - - rc= mysql_change_user(mysql, user_pw, pw, NULL); - myquery(rc); - - rc= mysql_change_user(mysql, user_pw, pw, ""); - myquery(rc); - - rc= mysql_change_user(mysql, user_no_pw, pw, db); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_no_pw, pw, ""); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_no_pw, pw, NULL); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, user_no_pw, "", NULL); - myquery(rc); - - rc= mysql_change_user(mysql, user_no_pw, "", ""); - myquery(rc); - - rc= mysql_change_user(mysql, user_no_pw, "", db); - myquery(rc); - - rc= mysql_change_user(mysql, user_no_pw, NULL, db); - myquery(rc); - - rc= mysql_change_user(mysql, "", pw, db); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, "", pw, ""); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, "", pw, NULL); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, NULL, pw, NULL); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, NULL, NULL, db); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, NULL, "", db); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); - - rc= mysql_change_user(mysql, "", "", db); - DIE_UNLESS(rc); - if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + rc= mysql_change_user(conn, NULL, NULL, NULL); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + + rc= mysql_change_user(conn, "", NULL, NULL); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, "", "", NULL); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + mysql_close(conn); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + + rc= mysql_change_user(conn, "", "", ""); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, NULL, "", ""); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + + rc= mysql_change_user(conn, NULL, NULL, ""); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + mysql_close(conn); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + + rc= mysql_change_user(conn, "", NULL, ""); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, user_pw, NULL, ""); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, user_pw, "", ""); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + mysql_close(conn); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + + rc= mysql_change_user(conn, user_pw, "", NULL); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, user_pw, NULL, NULL); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, user_pw, "", db); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + mysql_close(conn); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + + rc= mysql_change_user(conn, user_pw, NULL, db); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, user_pw, pw, db); + myquery(rc); + + rc= mysql_change_user(conn, user_pw, pw, NULL); + myquery(rc); + + rc= mysql_change_user(conn, user_pw, pw, ""); + myquery(rc); + + rc= mysql_change_user(conn, user_no_pw, pw, db); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, user_no_pw, pw, ""); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + mysql_close(conn); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + + rc= mysql_change_user(conn, user_no_pw, pw, NULL); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, user_no_pw, "", NULL); + myquery(rc); + + rc= mysql_change_user(conn, user_no_pw, "", ""); + myquery(rc); + + rc= mysql_change_user(conn, user_no_pw, "", db); + myquery(rc); + + rc= mysql_change_user(conn, user_no_pw, NULL, db); + myquery(rc); + + rc= mysql_change_user(conn, "", pw, db); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, "", pw, ""); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + mysql_close(conn); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + + rc= mysql_change_user(conn, "", pw, NULL); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, NULL, pw, NULL); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, NULL, NULL, db); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + mysql_close(conn); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + + rc= mysql_change_user(conn, NULL, "", db); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); + + rc= mysql_change_user(conn, "", "", db); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(conn)); /* Cleanup the environment */ - mysql_change_user(mysql, opt_user, opt_password, current_db); + mysql_change_user(conn, opt_user, opt_password, current_db); + + mysql_close(conn); sprintf(buff, "drop database %s", db); rc= mysql_query(mysql, buff); @@ -16234,29 +16259,35 @@ static char db[NAME_CHAR_LEN+1]; static char query[LARGE_BUFFER_SIZE*2]; #endif + MYSQL* conn; DBUG_ENTER("test_bug31669"); myheader("test_bug31669"); - rc= mysql_change_user(mysql, NULL, NULL, NULL); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + + rc= mysql_change_user(conn, NULL, NULL, NULL); DIE_UNLESS(rc); - rc= mysql_change_user(mysql, "", "", ""); + rc= mysql_change_user(conn, "", "", ""); DIE_UNLESS(rc); memset(buff, 'a', sizeof(buff)); - rc= mysql_change_user(mysql, buff, buff, buff); + mysql_close(conn); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + + rc= mysql_change_user(conn, buff, buff, buff); DIE_UNLESS(rc); - rc = mysql_change_user(mysql, opt_user, opt_password, current_db); + rc = mysql_change_user(conn, opt_user, opt_password, current_db); DIE_UNLESS(!rc); #ifndef EMBEDDED_LIBRARY memset(db, 'a', sizeof(db)); db[NAME_CHAR_LEN]= 0; strxmov(query, "CREATE DATABASE IF NOT EXISTS ", db, NullS); - rc= mysql_query(mysql, query); + rc= mysql_query(conn, query); myquery(rc); memset(user, 'b', sizeof(user)); @@ -16265,54 +16296,59 @@ buff[LARGE_BUFFER_SIZE]= 0; strxmov(query, "GRANT ALL PRIVILEGES ON *.* TO '", user, "'@'%' IDENTIFIED BY " "'", buff, "' WITH GRANT OPTION", NullS); - rc= mysql_query(mysql, query); + rc= mysql_query(conn, query); myquery(rc); strxmov(query, "GRANT ALL PRIVILEGES ON *.* TO '", user, "'@'localhost' IDENTIFIED BY " "'", buff, "' WITH GRANT OPTION", NullS); - rc= mysql_query(mysql, query); - myquery(rc); - - rc= mysql_query(mysql, "FLUSH PRIVILEGES"); - myquery(rc); - - rc= mysql_change_user(mysql, user, buff, db); + rc= mysql_query(conn, query); + myquery(rc); + + rc= mysql_query(conn, "FLUSH PRIVILEGES"); + myquery(rc); + + rc= mysql_change_user(conn, user, buff, db); DIE_UNLESS(!rc); user[USERNAME_CHAR_LENGTH-1]= 'a'; - rc= mysql_change_user(mysql, user, buff, db); + rc= mysql_change_user(conn, user, buff, db); DIE_UNLESS(rc); user[USERNAME_CHAR_LENGTH-1]= 'b'; buff[LARGE_BUFFER_SIZE-1]= 'd'; - rc= mysql_change_user(mysql, user, buff, db); + rc= mysql_change_user(conn, user, buff, db); DIE_UNLESS(rc); buff[LARGE_BUFFER_SIZE-1]= 'c'; db[NAME_CHAR_LEN-1]= 'e'; - rc= mysql_change_user(mysql, user, buff, db); + rc= mysql_change_user(conn, user, buff, db); DIE_UNLESS(rc); + mysql_close(conn); + conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0); + db[NAME_CHAR_LEN-1]= 'a'; - rc= mysql_change_user(mysql, user, buff, db); + rc= mysql_change_user(conn, user, buff, db); DIE_UNLESS(!rc); - rc= mysql_change_user(mysql, user + 1, buff + 1, db + 1); + rc= mysql_change_user(conn, user + 1, buff + 1, db + 1); DIE_UNLESS(rc); - rc = mysql_change_user(mysql, opt_user, opt_password, current_db); + rc = mysql_change_user(conn, opt_user, opt_password, current_db); DIE_UNLESS(!rc); strxmov(query, "DROP DATABASE ", db, NullS); - rc= mysql_query(mysql, query); + rc= mysql_query(conn, query); myquery(rc); strxmov(query, "DELETE FROM mysql.user WHERE User='", user, "'", NullS); - rc= mysql_query(mysql, query); + rc= mysql_query(conn, query); myquery(rc); - DIE_UNLESS(mysql_affected_rows(mysql) == 2); + DIE_UNLESS(mysql_affected_rows(conn) == 2); #endif + mysql_close(conn); + DBUG_VOID_RETURN; }