diff --git a/Documentation/Authenticators/MySQL-Authenticator.md b/Documentation/Authenticators/MySQL-Authenticator.md index a69198249..05cd3f574 100644 --- a/Documentation/Authenticators/MySQL-Authenticator.md +++ b/Documentation/Authenticators/MySQL-Authenticator.md @@ -84,3 +84,12 @@ case-insensitive by converting all names into their lowercase form. ``` authenticator_options=lower_case_table_names=false ``` + +### `log_password_mismatch` + +This parameter takes a boolean value and is disabled by default. When enabled, +password hashes are logged in the error messages when authentication fails due +to a password mismatch between the one stored in MaxScale and the one given by +the user. This feature should only be used to diagnose authentication issues in +MaxScale and should be done on a secure system as the logging of the password +hashes can be considered a security risk. diff --git a/server/modules/authenticator/MariaDBAuth/dbusers.cc b/server/modules/authenticator/MariaDBAuth/dbusers.cc index a5e7f8bc4..a640a68b4 100644 --- a/server/modules/authenticator/MariaDBAuth/dbusers.cc +++ b/server/modules/authenticator/MariaDBAuth/dbusers.cc @@ -374,11 +374,8 @@ static int auth_cb(void* data, int columns, char** rows, char** row_names) return 0; } -int validate_mysql_user(MYSQL_AUTH* instance, - DCB* dcb, - MYSQL_session* session, - uint8_t* scramble, - size_t scramble_len) +std::pair get_password(MYSQL_AUTH* instance, DCB* dcb, MYSQL_session* session, + uint8_t* scramble, size_t scramble_len) { sqlite3* handle = get_handle(instance); const char* validate_query = instance->lower_case_table_names ? @@ -387,7 +384,6 @@ int validate_mysql_user(MYSQL_AUTH* instance, size_t len = strlen(validate_query) + 1 + strlen(session->user) * 2 + strlen(session->db) * 2 + MYSQL_HOST_MAXLEN + session->auth_token_len * 4 + 1; char sql[len + 1]; - int rval = MXS_AUTH_FAILED; char* err; if (instance->skip_auth) @@ -456,12 +452,25 @@ int validate_mysql_user(MYSQL_AUTH* instance, } } - if (res.ok) + return {res.ok, res.output}; +} + +int validate_mysql_user(MYSQL_AUTH* instance, + DCB* dcb, + MYSQL_session* session, + uint8_t* scramble, + size_t scramble_len) +{ + int rval = MXS_AUTH_FAILED; + sqlite3* handle = get_handle(instance); + auto res = get_password(instance, dcb, session, scramble, scramble_len); + + if (res.first) { /** Found a matching row */ - if (no_password_required(res.output, session->auth_token_len) - || check_password(res.output, + if (no_password_required(res.second.c_str(), session->auth_token_len) + || check_password(res.second.c_str(), session->auth_token, session->auth_token_len, scramble, diff --git a/server/modules/authenticator/MariaDBAuth/mysql_auth.cc b/server/modules/authenticator/MariaDBAuth/mysql_auth.cc index 46c3e8843..e21db8244 100644 --- a/server/modules/authenticator/MariaDBAuth/mysql_auth.cc +++ b/server/modules/authenticator/MariaDBAuth/mysql_auth.cc @@ -195,6 +195,7 @@ static void* mysql_auth_init(char** options) instance->check_permissions = true; instance->lower_case_table_names = false; instance->checksum = 0; + instance->log_password_mismatch = false; for (int i = 0; options[i]; i++) { @@ -224,6 +225,10 @@ static void* mysql_auth_init(char** options) { instance->lower_case_table_names = config_truth_value(value); } + else if (strcmp(options[i], "log_password_mismatch") == 0) + { + instance->log_password_mismatch = config_truth_value(value); + } else { MXS_ERROR("Unknown authenticator option: %s", options[i]); @@ -308,8 +313,9 @@ static GWBUF* gen_auth_switch_request_packet(MySQLProtocol* proto, MYSQL_session return buffer; } -static void log_auth_failure(DCB* dcb, int auth_ret) +static void log_auth_failure(MYSQL_AUTH* instance, DCB* dcb, int auth_ret) { + MySQLProtocol* protocol = DCB_PROTOCOL(dcb, MySQLProtocol); MYSQL_session* client_data = (MYSQL_session*)dcb->data; std::ostringstream extra; @@ -320,6 +326,18 @@ static void log_auth_failure(DCB* dcb, int auth_ret) else if (auth_ret == MXS_AUTH_FAILED_WRONG_PASSWORD) { extra << "Wrong password."; + + if (instance->log_password_mismatch) + { + uint8_t double_sha1[sizeof(client_data->client_sha1)]; + gw_sha1_str(client_data->client_sha1, sizeof(client_data->client_sha1), double_sha1); + char buf[sizeof(double_sha1) * 2 + 1]; + gw_bin2hex(buf, double_sha1, sizeof(double_sha1)); + extra << " Received '" << buf << "', expected '" + << get_password(instance, dcb, client_data, protocol->scramble, + sizeof(protocol->scramble)).second + << "'."; + } } else { @@ -410,9 +428,10 @@ static int mysql_auth_authenticate(DCB* dcb) dcb->user = MXS_STRDUP_A(client_data->user); /** Send an OK packet to the client */ } - else if (dcb->service->log_auth_warnings) + else if (dcb->service->log_auth_warnings + || (instance->log_password_mismatch && auth_ret == MXS_AUTH_FAILED_WRONG_PASSWORD)) { - log_auth_failure(dcb, auth_ret); + log_auth_failure(instance, dcb, auth_ret); } /* let's free the auth_token now */ diff --git a/server/modules/authenticator/MariaDBAuth/mysql_auth.hh b/server/modules/authenticator/MariaDBAuth/mysql_auth.hh index f03116d87..2458f5dcb 100644 --- a/server/modules/authenticator/MariaDBAuth/mysql_auth.hh +++ b/server/modules/authenticator/MariaDBAuth/mysql_auth.hh @@ -112,6 +112,7 @@ typedef struct mysql_auth bool skip_auth; /**< Authentication will always be successful */ bool check_permissions; bool lower_case_table_names; /**< Disable database case-sensitivity */ + bool log_password_mismatch; /**< Log password mismatches*/ uint64_t checksum; } MYSQL_AUTH; @@ -212,4 +213,7 @@ int validate_mysql_user(MYSQL_AUTH* instance, uint8_t* scramble, size_t scramble_len); +std::pair get_password(MYSQL_AUTH* instance, DCB* dcb, MYSQL_session* session, + uint8_t* scramble, size_t scramble_len); + MXS_END_DECLS