diff options
author | Vladislav Vaintroub <wlad@mariadb.com> | 2018-12-05 13:13:07 +0100 |
---|---|---|
committer | Vladislav Vaintroub <wlad@mariadb.com> | 2019-01-08 12:58:08 +0100 |
commit | 0460058eb487b87bef058087b9a5336f47c8f1a0 (patch) | |
tree | 02e23729f7731213d80932f6d9e22c1452a3c110 | |
parent | 30da40bb8c303159747b1cf1d74411b049b6d252 (diff) | |
download | mariadb-git-bb-10.4-wlad-MDEV-7598.tar.gz |
MDEV-7598 Lock user after too many password errorsbb-10.4-wlad-MDEV-7598
-rw-r--r-- | mysql-test/include/have_auth_named_pipe.inc | 13 | ||||
-rw-r--r-- | mysql-test/main/max_password_errors.result | 45 | ||||
-rw-r--r-- | mysql-test/main/max_password_errors.test | 64 | ||||
-rw-r--r-- | mysql-test/main/mysqld--help.result | 5 | ||||
-rw-r--r-- | mysql-test/suite/plugins/r/max_password_errors_auth_named_pipe.result | 12 | ||||
-rw-r--r-- | mysql-test/suite/plugins/r/max_password_errors_auth_socket.result | 12 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/max_password_errors_auth_named_pipe.opt | 1 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/max_password_errors_auth_named_pipe.test | 22 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/max_password_errors_auth_socket.opt | 1 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/max_password_errors_auth_socket.test | 23 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/r/sysvars_server_embedded.result | 14 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result | 14 | ||||
-rw-r--r-- | sql/mysqld.cc | 1 | ||||
-rw-r--r-- | sql/mysqld.h | 1 | ||||
-rw-r--r-- | sql/share/errmsg-utf8.txt | 2 | ||||
-rw-r--r-- | sql/sql_acl.cc | 67 | ||||
-rw-r--r-- | sql/sys_vars.cc | 8 |
17 files changed, 305 insertions, 0 deletions
diff --git a/mysql-test/include/have_auth_named_pipe.inc b/mysql-test/include/have_auth_named_pipe.inc new file mode 100644 index 00000000000..4f4bf37f11e --- /dev/null +++ b/mysql-test/include/have_auth_named_pipe.inc @@ -0,0 +1,13 @@ +--source include/not_embedded.inc + +if (!$AUTH_NAMED_PIPE_SO) { + skip No auth_named_pipe plugin; +} + +if (!$USERNAME) { + skip USER variable is undefined; +} + +if (`SELECT count(*) <> 0 FROM mysql.user WHERE user = '$USERNAME'`) { + skip %USERNAME%=$USER which exists in mysql.user; +} diff --git a/mysql-test/main/max_password_errors.result b/mysql-test/main/max_password_errors.result new file mode 100644 index 00000000000..020761b4f2e --- /dev/null +++ b/mysql-test/main/max_password_errors.result @@ -0,0 +1,45 @@ +set @old_max_password_errors=@@max_password_errors; +set global max_password_errors=2; +create user u identified by 'good_pass'; +connect(localhost,u,bas_pass,test,MASTER_PORT,MASTER_SOCKET); +connect con1, localhost, u, bas_pass; +ERROR 28000: Access denied for user 'u'@'localhost' (using password: YES) +connect(localhost,u,bad_pass,test,MASTER_PORT,MASTER_SOCKET); +connect con1, localhost, u, bad_pass; +ERROR 28000: Access denied for user 'u'@'localhost' (using password: YES) +connect(localhost,u,good_pass,test,MASTER_PORT,MASTER_SOCKET); +connect con1, localhost, u, good_pass; +ERROR HY000: User is blocked because of too many credential errors; unblock with 'FLUSH PRIVILEGES' +connect(localhost,u,bad_pass,test,MASTER_PORT,MASTER_SOCKET); +connect con1, localhost, u, bad_pass; +ERROR HY000: User is blocked because of too many credential errors; unblock with 'FLUSH PRIVILEGES' +FLUSH PRIVILEGES; +connect con1, localhost, u, good_pass; +disconnect con1; +connect(localhost,u,bad_pass,test,MASTER_PORT,MASTER_SOCKET); +connect con1, localhost, u, bad_pass; +ERROR 28000: Access denied for user 'u'@'localhost' (using password: YES) +connect con1, localhost, u, good_pass; +disconnect con1; +connect(localhost,u,bad_pass,test,MASTER_PORT,MASTER_SOCKET); +connect con1, localhost, u, bad_pass; +ERROR 28000: Access denied for user 'u'@'localhost' (using password: YES) +connect con1, localhost, u, good_pass; +ERROR 28000: Access denied for user 'u'@'localhost' (using password: YES) +ERROR 28000: Access denied for user 'u'@'localhost' (using password: YES) +ERROR HY000: User is blocked because of too many credential errors; unblock with 'FLUSH PRIVILEGES' +disconnect con1; +connection default; +FLUSH PRIVILEGES; +connect(localhost,root,bas_pass,test,MASTER_PORT,MASTER_SOCKET); +connect con1, localhost, root, bas_pass; +ERROR 28000: Access denied for user 'root'@'localhost' (using password: YES) +connect(localhost,root,bad_pass,test,MASTER_PORT,MASTER_SOCKET); +connect con1, localhost, root, bad_pass; +ERROR 28000: Access denied for user 'root'@'localhost' (using password: YES) +connect con1, localhost, u, good_pass; +disconnect con1; +connection default; +DROP USER u; +FLUSH PRIVILEGES; +set global max_password_errors=@old_max_password_errors; diff --git a/mysql-test/main/max_password_errors.test b/mysql-test/main/max_password_errors.test new file mode 100644 index 00000000000..1debca0258d --- /dev/null +++ b/mysql-test/main/max_password_errors.test @@ -0,0 +1,64 @@ +--source include/not_embedded.inc +set @old_max_password_errors=@@max_password_errors; +set global max_password_errors=2; +create user u identified by 'good_pass'; + +# Test that user is blocked after 'max_password_errors' bad passwords +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_ERROR; +connect(con1, localhost, u, bas_pass); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_ERROR; +connect (con1, localhost, u, bad_pass); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_USER_IS_BLOCKED; +connect(con1, localhost, u, good_pass); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_USER_IS_BLOCKED; +connect(con1, localhost, u, bad_pass); + + +# Test that FLUSH PRIVILEGES clears the error +FLUSH PRIVILEGES; +connect (con1, localhost, u, good_pass); +disconnect con1; + +# Test that good login clears the error +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_ERROR; +connect (con1, localhost, u, bad_pass); +connect (con1, localhost, u, good_pass); +disconnect con1; +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_ERROR; +connect (con1, localhost, u, bad_pass); +connect (con1, localhost, u, good_pass); + +# Test the behavior of change_user +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_ERROR; +change_user u,bad_pass; +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_ERROR; +change_user u,bad_pass; +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_USER_IS_BLOCKED; +change_user u,good_pass; +disconnect con1; + +connection default; +FLUSH PRIVILEGES; + +#Test that root@localhost is not blocked, with password errors +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_ERROR; +connect(con1, localhost, root, bas_pass); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_ERROR; +connect (con1, localhost, root, bad_pass); +connect (con1, localhost, u, good_pass); +disconnect con1; +connection default; +DROP USER u; +FLUSH PRIVILEGES; +set global max_password_errors=@old_max_password_errors;
\ No newline at end of file diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index 8faf332a7dd..b8a99393b28 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -546,6 +546,10 @@ The following specify which files/extra groups are read (specified before remain The maximum BLOB length to send to server from mysql_send_long_data API. Deprecated option; use max_allowed_packet instead. + --max-password-errors=# + If there is more than this number of failed connect + attempts due to invalid password, user will be blocked + from further connections until FLUSH_PRIVILEGES. --max-prepared-stmt-count=# Maximum number of prepared statements in the server --max-recursive-iterations[=#] @@ -1518,6 +1522,7 @@ max-heap-table-size 16777216 max-join-size 18446744073709551615 max-length-for-sort-data 1024 max-long-data-size 16777216 +max-password-errors 18446744073709551615 max-prepared-stmt-count 16382 max-recursive-iterations 18446744073709551615 max-relay-log-size 1073741824 diff --git a/mysql-test/suite/plugins/r/max_password_errors_auth_named_pipe.result b/mysql-test/suite/plugins/r/max_password_errors_auth_named_pipe.result new file mode 100644 index 00000000000..82d464e3cb2 --- /dev/null +++ b/mysql-test/suite/plugins/r/max_password_errors_auth_named_pipe.result @@ -0,0 +1,12 @@ +set @old_max_password_errors=@@max_password_errors; +create user nosuchuser identified with 'named_pipe'; +set global max_password_errors=1; +connect(localhost,nosuchuser,,test,MASTER_PORT,MASTER_SOCKET); +connect pipe_con,localhost,nosuchuser,,,,,PIPE; +ERROR 28000: Access denied for user 'nosuchuser'@'localhost' +connect(localhost,nosuchuser,,test,MASTER_PORT,MASTER_SOCKET); +connect pipe_con,localhost,nosuchuser,,,,,PIPE; +ERROR 28000: Access denied for user 'nosuchuser'@'localhost' +DROP USER nosuchuser; +FLUSH PRIVILEGES; +set global max_password_errors=@old_max_password_errors; diff --git a/mysql-test/suite/plugins/r/max_password_errors_auth_socket.result b/mysql-test/suite/plugins/r/max_password_errors_auth_socket.result new file mode 100644 index 00000000000..eb7cb64167b --- /dev/null +++ b/mysql-test/suite/plugins/r/max_password_errors_auth_socket.result @@ -0,0 +1,12 @@ +set @old_max_password_errors=@@max_password_errors; +create user nosuchuser identified with 'unix_socket'; +set global max_password_errors=1; +connect(localhost,nosuchuser,,test,MASTER_PORT,MASTER_SOCKET); +connect pipe_con,localhost,nosuchuser; +ERROR 28000: Access denied for user 'nosuchuser'@'localhost' +connect(localhost,nosuchuser,,test,MASTER_PORT,MASTER_SOCKET); +connect pipe_con,localhost,nosuchuser; +ERROR 28000: Access denied for user 'nosuchuser'@'localhost' +DROP USER nosuchuser; +FLUSH PRIVILEGES; +set global max_password_errors=@old_max_password_errors; diff --git a/mysql-test/suite/plugins/t/max_password_errors_auth_named_pipe.opt b/mysql-test/suite/plugins/t/max_password_errors_auth_named_pipe.opt new file mode 100644 index 00000000000..52bf94f3511 --- /dev/null +++ b/mysql-test/suite/plugins/t/max_password_errors_auth_named_pipe.opt @@ -0,0 +1 @@ +--loose-enable-named-pipe --plugin-load=$AUTH_NAMED_PIPE_SO diff --git a/mysql-test/suite/plugins/t/max_password_errors_auth_named_pipe.test b/mysql-test/suite/plugins/t/max_password_errors_auth_named_pipe.test new file mode 100644 index 00000000000..79aeb7d3cbe --- /dev/null +++ b/mysql-test/suite/plugins/t/max_password_errors_auth_named_pipe.test @@ -0,0 +1,22 @@ +# Tests that max_password_errors has no effect on login errors with +# passwordless plugins (Windows version / auth_named_pipe) + +--source include/not_embedded.inc +--source include/have_auth_named_pipe.inc +if (`SELECT '$USERNAME' = 'nosuchuser'`) { + skip skipped for nosuchuser; +} +set @old_max_password_errors=@@max_password_errors; +create user nosuchuser identified with 'named_pipe'; + +set global max_password_errors=1; +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_NO_PASSWORD_ERROR; +connect(pipe_con,localhost,nosuchuser,,,,,PIPE); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_NO_PASSWORD_ERROR; +connect(pipe_con,localhost,nosuchuser,,,,,PIPE); + +DROP USER nosuchuser; +FLUSH PRIVILEGES; +set global max_password_errors=@old_max_password_errors; diff --git a/mysql-test/suite/plugins/t/max_password_errors_auth_socket.opt b/mysql-test/suite/plugins/t/max_password_errors_auth_socket.opt new file mode 100644 index 00000000000..91bb73e34f7 --- /dev/null +++ b/mysql-test/suite/plugins/t/max_password_errors_auth_socket.opt @@ -0,0 +1 @@ +--loose-enable-named-pipe --plugin-load=$AUTH_SOCKET_SO diff --git a/mysql-test/suite/plugins/t/max_password_errors_auth_socket.test b/mysql-test/suite/plugins/t/max_password_errors_auth_socket.test new file mode 100644 index 00000000000..495a68a0b59 --- /dev/null +++ b/mysql-test/suite/plugins/t/max_password_errors_auth_socket.test @@ -0,0 +1,23 @@ +# Tests that max_password_errors has no effect on login errors with +# passwordless plugins (Unix version / auth_unix_socket) + +--source include/not_embedded.inc +--source include/have_unix_socket.inc + +if (`SELECT '$USER' = 'nosuchuser'`) { + skip USER is nosuchuser; +} +set @old_max_password_errors=@@max_password_errors; +create user nosuchuser identified with 'unix_socket'; + +set global max_password_errors=1; +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_NO_PASSWORD_ERROR; +connect(pipe_con,localhost,nosuchuser); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +error ER_ACCESS_DENIED_NO_PASSWORD_ERROR; +connect(pipe_con,localhost,nosuchuser); + +DROP USER nosuchuser; +FLUSH PRIVILEGES; +set global max_password_errors=@old_max_password_errors; diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 1dac71788bd..64dbbf6a75a 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -2210,6 +2210,20 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY YES COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME MAX_PASSWORD_ERRORS +SESSION_VALUE NULL +GLOBAL_VALUE 4294967295 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 4294967295 +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT If there is more than this number of failed connect attempts due to invalid password, user will be blocked from further connections until FLUSH_PRIVILEGES. +NUMERIC_MIN_VALUE 1 +NUMERIC_MAX_VALUE 4294967295 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME MAX_PREPARED_STMT_COUNT SESSION_VALUE NULL GLOBAL_VALUE 16382 diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 4c9b38c48b7..d99c2fae240 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -2420,6 +2420,20 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY YES COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME MAX_PASSWORD_ERRORS +SESSION_VALUE NULL +GLOBAL_VALUE 4294967295 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 4294967295 +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT If there is more than this number of failed connect attempts due to invalid password, user will be blocked from further connections until FLUSH_PRIVILEGES. +NUMERIC_MIN_VALUE 1 +NUMERIC_MAX_VALUE 4294967295 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME MAX_PREPARED_STMT_COUNT SESSION_VALUE NULL GLOBAL_VALUE 16382 diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 561b55a9023..da0be1c7945 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -513,6 +513,7 @@ ulong specialflag=0; ulong binlog_cache_use= 0, binlog_cache_disk_use= 0; ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0; ulong max_connections, max_connect_errors; +uint max_password_errors; ulong extra_max_connections; uint max_digest_length= 0; ulong slave_retried_transactions; diff --git a/sql/mysqld.h b/sql/mysqld.h index 22c572b19fa..49663f27a66 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -241,6 +241,7 @@ extern ulong slow_launch_threads, slow_launch_time; extern MYSQL_PLUGIN_IMPORT ulong max_connections; extern uint max_digest_length; extern ulong max_connect_errors, connect_timeout; +extern uint max_password_errors; extern my_bool slave_allow_batching; extern my_bool allow_slave_start; extern LEX_CSTRING reason_slave_blocked; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index daf010d8e8a..e09afebe074 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7931,3 +7931,5 @@ ER_BACKUP_STAGE_FAILED eng "Backup stage '%s' failed" ER_BACKUP_UNKNOWN_STAGE eng "Unknown backup stage: '%s'. Stage should be one of START, FLUSH, BLOCK_DDL, BLOCK_COMMIT or END" +ER_USER_IS_BLOCKED + eng "User is blocked because of too many credential errors; unblock with 'FLUSH PRIVILEGES'" diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 3c31be5bd36..c5229d02d51 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -147,6 +147,7 @@ public: size_t hostname_length; USER_RESOURCES user_resource; enum SSL_type ssl_type; + uint password_errors; const char *ssl_cipher, *x509_issuer, *x509_subject; LEX_CSTRING plugin; LEX_CSTRING auth_string; @@ -12325,6 +12326,23 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, } #ifndef NO_EMBEDDED_ACCESS_CHECKS + +/** + Safeguard to avoid blocking the root, when max_password_errors + limit is reached. + + Currently, we allow password errors for superuser on localhost. + + @return true, if password errors should be ignored, and user should not be locked. +*/ +static bool ignore_max_password_errors(const ACL_USER *acl_user) +{ + const char *host= acl_user->host.hostname; + return (acl_user->access & SUPER_ACL) + && (!strcasecmp(host, "localhost") || + !strcmp(host, "127.0.0.1") || + !strcmp(host, "::1")); +} /** Finds acl entry in user database for authentication purposes. @@ -12343,6 +12361,16 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) mysql_mutex_lock(&acl_cache->lock); ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip); + + if (user && user->password_errors >= max_password_errors && !ignore_max_password_errors(user)) + { + mysql_mutex_unlock(&acl_cache->lock); + my_error(ER_USER_IS_BLOCKED, MYF(0)); + general_log_print(mpvio->auth_info.thd, COM_CONNECT, + ER_THD(mpvio->auth_info.thd, ER_USER_IS_BLOCKED)); + DBUG_RETURN(1); + } + if (user) mpvio->acl_user= user->copy(mpvio->auth_info.thd->mem_root); @@ -13188,6 +13216,38 @@ static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name, return res; } +enum PASSWD_ERROR_ACTION +{ + PASSWD_ERROR_CLEAR, + PASSWD_ERROR_INCREMENT +}; + +/* Increment, or clear password errors for a user. */ +static void handle_password_errors(const char *user, const char *hostname, PASSWD_ERROR_ACTION action) +{ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + mysql_mutex_assert_not_owner(&acl_cache->lock); + mysql_mutex_lock(&acl_cache->lock); + ACL_USER *u = find_user_exact(hostname, user); + if (u) + { + switch(action) + { + case PASSWD_ERROR_INCREMENT: + u->password_errors++; + break; + case PASSWD_ERROR_CLEAR: + u->password_errors= 0; + break; + default: + DBUG_ASSERT(0); + break; + } + } + mysql_mutex_unlock(&acl_cache->lock); +#endif +} + /** Perform the handshake, authorize the client and update thd sctx variables. @@ -13307,6 +13367,8 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) break; case CR_AUTH_USER_CREDENTIALS: errors.m_authentication= 1; + if (thd->password && !mpvio.make_it_fail) + handle_password_errors(acl_user->user.str, acl_user->host.hostname, PASSWD_ERROR_INCREMENT); break; case CR_ERROR: default: @@ -13321,6 +13383,11 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) } sctx->proxy_user[0]= 0; + if (thd->password && acl_user->password_errors) + { + /* Login succeeded, clear password errors.*/ + handle_password_errors(acl_user->user.str, acl_user->host.hostname, PASSWD_ERROR_CLEAR); + } if (initialized) // if not --skip-grant-tables { diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index e8044ef9035..0bf297b8eef 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1515,6 +1515,14 @@ static Sys_var_ulong Sys_max_connect_errors( VALID_RANGE(1, UINT_MAX), DEFAULT(MAX_CONNECT_ERRORS), BLOCK_SIZE(1)); +static Sys_var_uint Sys_max_password_errors( + "max_password_errors", + "If there is more than this number of failed connect attempts " + "due to invalid password, user will be blocked from further connections until FLUSH_PRIVILEGES.", + GLOBAL_VAR(max_password_errors), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(1, UINT_MAX), DEFAULT(UINT_MAX), + BLOCK_SIZE(1)); + static Sys_var_uint Sys_max_digest_length( "max_digest_length", "Maximum length considered for digest text.", READ_ONLY GLOBAL_VAR(max_digest_length), |