summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysqltest.cc5
-rw-r--r--mysql-test/r/change_user_notembedded.result5
-rw-r--r--mysql-test/r/mysqltest.result6
-rw-r--r--mysql-test/t/change_user_notembedded.test24
-rw-r--r--sql/sql_class.cc1
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_parse.cc17
-rw-r--r--tests/mysql_client_test.c180
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;
}