diff options
-rw-r--r-- | client/mysqltest.cc | 5 | ||||
-rw-r--r-- | mysql-test/r/change_user_notembedded.result | 5 | ||||
-rw-r--r-- | mysql-test/r/mysqltest.result | 6 | ||||
-rw-r--r-- | mysql-test/t/change_user_notembedded.test | 24 | ||||
-rw-r--r-- | sql/sql_class.cc | 1 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 17 | ||||
-rw-r--r-- | tests/mysql_client_test.c | 180 |
8 files changed, 162 insertions, 77 deletions
diff --git a/client/mysqltest.cc b/client/mysqltest.cc index fa5f2b566c2..fa3ade1a7c1 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -3908,7 +3908,10 @@ void do_change_user(struct st_command *command) 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); diff --git a/mysql-test/r/change_user_notembedded.result b/mysql-test/r/change_user_notembedded.result new file mode 100644 index 00000000000..506a463d6d2 --- /dev/null +++ b/mysql-test/r/change_user_notembedded.result @@ -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 diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 312dd7d0312..32658866951 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -847,9 +847,9 @@ a int(11) YES NULL 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 diff --git a/mysql-test/t/change_user_notembedded.test b/mysql-test/t/change_user_notembedded.test new file mode 100644 index 00000000000..bf5d1956cd5 --- /dev/null +++ b/mysql-test/t/change_user_notembedded.test @@ -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; + diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b7a37ae3f69..d44d28eaae2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -675,6 +675,7 @@ THD::THD() stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE), examined_row_count(0), global_read_lock(0), + failed_com_change_user(0), is_fatal_error(0), transaction_rollback_request(0), is_fatal_sub_stmt_error(0), diff --git a/sql/sql_class.h b/sql/sql_class.h index 6b277add4a5..d55200efea4 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1865,6 +1865,7 @@ public: bool no_errors, password; bool extra_port; /* If extra connection */ + uint8 failed_com_change_user; /** Set to TRUE if execution of the current compound statement can not continue. In particular, disables activation of diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index be0e2db43c6..0c47b7a8bb3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1144,6 +1144,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, uint save_db_length= thd->db_length; char *save_db= thd->db; + int rc; USER_CONN *save_user_connect= thd->user_connect; Security_context save_security_ctx= *thd->security_ctx; CHARSET_INFO *save_character_set_client= @@ -1157,7 +1158,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->security_ctx->user= 0; thd->user_connect= 0; - if (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); + + if (rc) { /* Free user if allocated by acl_authenticate */ x_free(thd->security_ctx->user); @@ -1170,6 +1183,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, 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 { diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 5f9d7bc76c8..fff06ca7daa 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15386,6 +15386,7 @@ static void test_change_user() 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 @@ static void test_change_user() 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); + rc= mysql_change_user(conn, NULL, NULL, NULL); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, "", NULL, NULL); + rc= mysql_change_user(conn, "", NULL, NULL); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, "", "", NULL); + rc= mysql_change_user(conn, "", "", NULL); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, "", "", ""); + 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(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, NULL, "", ""); + rc= mysql_change_user(conn, NULL, "", ""); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, NULL, NULL, ""); + rc= mysql_change_user(conn, NULL, NULL, ""); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + 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(mysql, "", NULL, ""); + rc= mysql_change_user(conn, "", NULL, ""); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, user_pw, NULL, ""); + rc= mysql_change_user(conn, user_pw, NULL, ""); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, user_pw, "", ""); + rc= mysql_change_user(conn, user_pw, "", ""); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, user_pw, "", NULL); + 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(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, user_pw, NULL, NULL); + rc= mysql_change_user(conn, user_pw, NULL, NULL); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, user_pw, "", db); + rc= mysql_change_user(conn, user_pw, "", db); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + 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(mysql, user_pw, NULL, db); + rc= mysql_change_user(conn, user_pw, NULL, db); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, user_pw, pw, db); + rc= mysql_change_user(conn, user_pw, pw, db); myquery(rc); - rc= mysql_change_user(mysql, user_pw, pw, NULL); + rc= mysql_change_user(conn, user_pw, pw, NULL); myquery(rc); - rc= mysql_change_user(mysql, user_pw, pw, ""); + rc= mysql_change_user(conn, user_pw, pw, ""); myquery(rc); - rc= mysql_change_user(mysql, user_no_pw, pw, db); + 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(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, user_no_pw, pw, ""); + rc= mysql_change_user(conn, user_no_pw, pw, ""); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, user_no_pw, pw, NULL); + 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(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, user_no_pw, "", NULL); + rc= mysql_change_user(conn, user_no_pw, "", NULL); myquery(rc); - rc= mysql_change_user(mysql, user_no_pw, "", ""); + rc= mysql_change_user(conn, user_no_pw, "", ""); myquery(rc); - rc= mysql_change_user(mysql, user_no_pw, "", db); + rc= mysql_change_user(conn, user_no_pw, "", db); myquery(rc); - rc= mysql_change_user(mysql, user_no_pw, NULL, db); + rc= mysql_change_user(conn, user_no_pw, NULL, db); myquery(rc); - rc= mysql_change_user(mysql, "", pw, db); + rc= mysql_change_user(conn, "", pw, db); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, "", pw, ""); + rc= mysql_change_user(conn, "", pw, ""); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + 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(mysql, "", pw, NULL); + rc= mysql_change_user(conn, "", pw, NULL); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, NULL, pw, NULL); + rc= mysql_change_user(conn, NULL, pw, NULL); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, NULL, NULL, db); + rc= mysql_change_user(conn, NULL, NULL, db); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, NULL, "", db); + 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(mysql)); + printf("Got error (as expected): %s\n", mysql_error(conn)); - rc= mysql_change_user(mysql, "", "", db); + rc= mysql_change_user(conn, "", "", db); DIE_UNLESS(rc); if (! opt_silent) - printf("Got error (as expected): %s\n", mysql_error(mysql)); + 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 void test_bug31669() 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 @@ static void test_bug31669() 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); + rc= mysql_query(conn, query); myquery(rc); - rc= mysql_query(mysql, "FLUSH PRIVILEGES"); + rc= mysql_query(conn, "FLUSH PRIVILEGES"); myquery(rc); - rc= mysql_change_user(mysql, user, buff, db); + 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; } |