Hi, Vladislav! On Jan 04, Vladislav Vaintroub wrote: > revision-id: 82d7e2094c8 (mariadb-10.4.1-41-g82d7e2094c8) > parent(s): c4ec6bb69ec > author: Vladislav Vaintroub <w...@mariadb.com> > committer: Vladislav Vaintroub <w...@mariadb.com> > timestamp: 2019-01-04 10:42:05 +0100 > message: > > MDEV-7598 Lock user after too many password errors
COM_CHANGE_USER has its own blocking, that works differently. See change_user_notembedded test. 1. Does it also increment acl_user->password_errors counter now? 2. Should COM_CHANGE_USER be changed to behave more like connect? even the answer to the second question is "no", please, add tests for change_user to document the behavior from the first question. > diff --git a/mysql-test/main/max_password_errors.test > b/mysql-test/main/max_password_errors.test > new file mode 100644 > index 00000000000..fbb9e699b49 > --- /dev/null > +++ b/mysql-test/main/max_password_errors.test > @@ -0,0 +1,46 @@ > +--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); add another bad_pass connect here, please, to show what error one will get (ER_ACCESS_DENIED_ERROR or ER_USER_IS_BLOCKED). > +# Test that FLUSH PRIVILEGES clears the error > +FLUSH PRIVILEGES; > +connect (con1, localhost, u, good_pass); > +disconnect con1; > diff --git a/mysql-test/main/max_password_errors_auth_named_pipe.opt > b/mysql-test/main/max_password_errors_auth_named_pipe.opt I'd better put the test into the plugins suite. That's where all similar tests are. > diff --git a/mysql-test/main/max_password_errors_auth_socket.opt > b/mysql-test/main/max_password_errors_auth_socket.opt same > diff --git a/mysql-test/main/max_password_errors_auth_socket.test > b/mysql-test/main/max_password_errors_auth_socket.test > new file mode 100644 > index 00000000000..4f31927b02d > --- /dev/null > +++ b/mysql-test/main/max_password_errors_auth_socket.test > @@ -0,0 +1,20 @@ > +# 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_unix_socket.inc > + > +set @old_max_password_errors=@@max_password_errors; > +create user nosuchuser identified with 'unix_socket'; Technically, you need to test that $USER != nosuchuser. Otherwise someone someday will surely run it from a nosuchuser account :) May be just include have_unix_socket.inc after you created nosuchuser, it should do the trick. > diff --git a/mysql-test/main/mysqld--help.result > b/mysql-test/main/mysqld--help.result > index 8faf332a7dd..3255927d4a6 100644 > --- a/mysql-test/main/mysqld--help.result > +++ b/mysql-test/main/mysqld--help.result > @@ -546,6 +546,11 @@ 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.Value 0 > + disables user blocking 1. Space before "Value" 2. Why 0 disables blocking? max_connect_errors doesn't have a special 0 value. > --max-prepared-stmt-count=# > Maximum number of prepared statements in the server > --max-recursive-iterations[=#] > @@ -1518,6 +1523,7 @@ max-heap-table-size 16777216 > max-join-size 18446744073709551615 > max-length-for-sort-data 1024 > max-long-data-size 16777216 > +max-password-errors 0 > max-prepared-stmt-count 16382 > max-recursive-iterations 18446744073709551615 see? max-recursive-iterations doesn't have a special 0 value, it simply uses maxint. So does max-join-size. > max-relay-log-size 1073741824 > diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc > index 85b54009219..95f92688677 100644 > --- a/sql/sql_acl.cc > +++ b/sql/sql_acl.cc > @@ -12323,6 +12324,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")); > +} I don't like special cases :( May be after implementing `unix_socket OR mysql_native_password` feature, this special case could be removed? > /** > Finds acl entry in user database for authentication purposes. > > @@ -12341,6 +12359,21 @@ 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 && max_password_errors && user->password_errors >= > max_password_errors) > + { > + if (ignore_max_password_errors(user)) > + user->password_errors= 0; add a comment here, like // skip a second mutex lock in handle_password_errors() > + else > + { > + 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); > > @@ -13305,6 +13370,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 && max_password_errors) add && !mpvio->make_it_fail here (see how make_it_fail is set). > + handle_password_errors(acl_user->user.str, acl_user->host.hostname, > PASSWD_ERROR_INCREMENT); > break; > case CR_ERROR: > default: Regards, Sergei Chief Architect MariaDB and secur...@mariadb.org _______________________________________________ Mailing list: https://launchpad.net/~maria-developers Post to : maria-developers@lists.launchpad.net Unsubscribe : https://launchpad.net/~maria-developers More help : https://help.launchpad.net/ListHelp