diff --git a/server/modules/authenticator/MySQLAuth/dbusers.c b/server/modules/authenticator/MySQLAuth/dbusers.c index 92b92dade..6572f5595 100644 --- a/server/modules/authenticator/MySQLAuth/dbusers.c +++ b/server/modules/authenticator/MySQLAuth/dbusers.c @@ -1243,6 +1243,32 @@ static bool check_password(const char *output, return memcmp(final_step, stored_token, stored_token_len) == 0; } +static int database_cb(void *data, int columns, char** rows, char** row_names) +{ + bool *rval = (bool*)data; + *rval = true; + return 0; +} + +static bool check_database(sqlite3 *handle, const char *database) +{ + size_t len = sizeof(mysqlauth_validate_database_query) + strlen(database) + 1; + char sql[len]; + + sprintf(sql, mysqlauth_validate_database_query, database); + bool rval = false; + char *err; + + if (sqlite3_exec(handle, sql, database_cb, &rval, &err) != SQLITE_OK) + { + MXS_ERROR("Failed to execute auth query: %s", err); + sqlite3_free(err); + rval = false; + } + + return rval; +} + /** Used to detect empty result sets */ struct user_query_result { @@ -1271,57 +1297,51 @@ static int auth_cb(void *data, int columns, char** rows, char** row_names) */ bool validate_mysql_user(sqlite3 *handle, DCB *dcb, MYSQL_session *session) { - size_t len = sizeof(mysqlauth_validation_query) + strlen(session->user) * 2 + + size_t len = sizeof(mysqlauth_validate_user_query) + strlen(session->user) * 2 + strlen(session->db) * 2 + MYSQL_HOST_MAXLEN + session->auth_token_len * 4 + 1; char sql[len + 1]; bool rval = false; char *err; - /** - * Try authentication twice; first time with the current users, second - * time with fresh users - */ - for (int i = 0; i < 2 && !rval; i++) - { - sprintf(sql, mysqlauth_validation_query, session->user, dcb->remote, - session->db, session->db); + sprintf(sql, mysqlauth_validate_user_query, session->user, dcb->remote, + session->db, session->db); - struct user_query_result res = {}; + struct user_query_result res = {}; + + if (sqlite3_exec(handle, sql, auth_cb, &res, &err) != SQLITE_OK) + { + MXS_ERROR("Failed to execute auth query: %s", err); + sqlite3_free(err); + } + + if (!res.ok) + { + /** + * Try authentication with the hostname instead of the IP. We do this only + * as a last resort so we avoid the high cost of the DNS lookup. + */ + char client_hostname[MYSQL_HOST_MAXLEN]; + wildcard_domain_match(dcb->remote, client_hostname); + sprintf(sql, mysqlauth_validate_user_query, session->user, client_hostname, + session->db, session->db); if (sqlite3_exec(handle, sql, auth_cb, &res, &err) != SQLITE_OK) { MXS_ERROR("Failed to execute auth query: %s", err); sqlite3_free(err); - rval = false; } + } - if (!res.ok) + if (res.ok) + { + /** Found a matching row */ + MySQLProtocol *proto = (MySQLProtocol*)dcb->protocol; + + if (check_password(res.output, session->auth_token, session->auth_token_len, + proto->scramble, sizeof(proto->scramble))) { - /** Try authentication with the hostname */ - char client_hostname[MYSQL_HOST_MAXLEN]; - wildcard_domain_match(dcb->remote, client_hostname); - sprintf(sql, mysqlauth_validation_query, session->user, client_hostname, - session->db, session->db); - - if (sqlite3_exec(handle, sql, auth_cb, &res, &err) != SQLITE_OK) - { - MXS_ERROR("Failed to execute auth query: %s", err); - sqlite3_free(err); - rval = false; - } - } - - if (res.ok) - { - /** Found a matching row */ - MySQLProtocol *proto = (MySQLProtocol*)dcb->protocol; - rval = check_password(res.output, session->auth_token, session->auth_token_len, - proto->scramble, sizeof(proto->scramble)); - } - - if (!rval && i == 0) - { - service_refresh_users(dcb->service); + /** Password is OK, check that the database exists */ + rval = check_database(handle, session->db); } } @@ -1337,7 +1357,8 @@ static void delete_mysql_users(sqlite3 *handle) { char *err; - if (sqlite3_exec(handle, delete_query, NULL, NULL, &err) != SQLITE_OK) + if (sqlite3_exec(handle, delete_users_query, NULL, NULL, &err) != SQLITE_OK || + sqlite3_exec(handle, delete_databases_query, NULL, NULL, &err) != SQLITE_OK) { MXS_ERROR("Failed to delete old users: %s", err); sqlite3_free(err); @@ -1384,10 +1405,10 @@ static void add_mysql_user(sqlite3 *handle, const char *user, const char *host, strcpy(pwstr, null_token); } - size_t len = sizeof(insert_sql_pattern) + strlen(user) + strlen(host) + dblen + pwlen + 1; + size_t len = sizeof(insert_user_query) + strlen(user) + strlen(host) + dblen + pwlen + 1; char insert_sql[len + 1]; - sprintf(insert_sql, insert_sql_pattern, user, host, dbstr, anydb ? "1" : "0", pwstr); + sprintf(insert_sql, insert_user_query, user, host, dbstr, anydb ? "1" : "0", pwstr); char *err; if (sqlite3_exec(handle, insert_sql, NULL, NULL, &err) != SQLITE_OK) @@ -1399,6 +1420,21 @@ static void add_mysql_user(sqlite3 *handle, const char *user, const char *host, MXS_INFO("Added user: %s", insert_sql); } +static void add_database(sqlite3 *handle, const char *db) +{ + size_t len = sizeof(insert_database_query) + strlen(db) + 1; + char insert_sql[len + 1]; + + sprintf(insert_sql, insert_database_query, db); + + char *err; + if (sqlite3_exec(handle, insert_sql, NULL, NULL, &err) != SQLITE_OK) + { + MXS_ERROR("Failed to insert database: %s", err); + sqlite3_free(err); + } +} + /** * Load the user/passwd form mysql.user table into the service users' hashtable * environment. @@ -1848,12 +1884,12 @@ get_users(SERV_LISTENER *listener, USERS *users) /** Testing new users query */ char *query = get_new_users_query(server->server->server_string, service->enable_root); + MYSQL_AUTH *instance = (MYSQL_AUTH*)listener->auth_instance; if (query) { if (mysql_query(con, query) == 0) { - MYSQL_AUTH *instance = (MYSQL_AUTH*)listener->auth_instance; delete_mysql_users(instance->handle); if ((result = mysql_store_result(con))) @@ -1876,6 +1912,24 @@ get_users(SERV_LISTENER *listener, USERS *users) MXS_FREE(query); } + /** Load the list of databases */ + if (mysql_query(con, "SHOW DATABASES") == 0) + { + if ((result = mysql_store_result(con))) + { + while ((row = mysql_fetch_row(result))) + { + add_database(instance->handle, row[0]); + } + + mysql_free_result(result); + } + } + else + { + MXS_ERROR("Failed to load list of databases: %s", mysql_error(con)); + } + mysql_close(con); return total_users; diff --git a/server/modules/authenticator/MySQLAuth/mysql_auth.c b/server/modules/authenticator/MySQLAuth/mysql_auth.c index 4f08008c6..c8d05e1b2 100644 --- a/server/modules/authenticator/MySQLAuth/mysql_auth.c +++ b/server/modules/authenticator/MySQLAuth/mysql_auth.c @@ -128,7 +128,8 @@ static void* mysql_auth_init(char **options) char *err; - if (sqlite3_exec(instance->handle, create_sql, NULL, NULL, &err) != SQLITE_OK) + if (sqlite3_exec(instance->handle, users_create_sql, NULL, NULL, &err) != SQLITE_OK || + sqlite3_exec(instance->handle, databases_create_sql, NULL, NULL, &err) != SQLITE_OK) { MXS_ERROR("Failed to create database: %s", err); sqlite3_free(err); @@ -264,9 +265,6 @@ mysql_auth_authenticate(DCB *dcb) MXS_DEBUG("Receiving connection from '%s' to database '%s'.", client_data->user, client_data->db); - auth_ret = combined_auth_check(dcb, client_data->auth_token, client_data->auth_token_len, - protocol, client_data->user, client_data->client_sha1, client_data->db); - MYSQL_AUTH *instance = (MYSQL_AUTH*)dcb->listener->auth_instance; bool is_ok = validate_mysql_user(instance->handle, dcb, client_data); diff --git a/server/modules/authenticator/MySQLAuth/mysql_auth.h b/server/modules/authenticator/MySQLAuth/mysql_auth.h index d9a037a5d..96348e4c0 100644 --- a/server/modules/authenticator/MySQLAuth/mysql_auth.h +++ b/server/modules/authenticator/MySQLAuth/mysql_auth.h @@ -44,25 +44,43 @@ static const char DBUSERS_FILE[] = "dbusers"; #define MYSQLAUTH_DATABASE_NAME "file:mysqlauth.db?mode=memory&cache=shared" /** The table name where we store the users */ -#define MYSQLAUTH_TABLE_NAME "mysqlauth_users" +#define MYSQLAUTH_USERS_TABLE_NAME "mysqlauth_users" -/** CREATE TABLE statement for the in-memory table */ -static const char create_sql[] = - "CREATE TABLE IF NOT EXISTS " MYSQLAUTH_TABLE_NAME +/** The table name where we store the users */ +#define MYSQLAUTH_DATABASES_TABLE_NAME "mysqlauth_databases" + +/** CREATE TABLE statement for the in-memory users table */ +static const char users_create_sql[] = + "CREATE TABLE IF NOT EXISTS " MYSQLAUTH_USERS_TABLE_NAME "(user varchar(255), host varchar(255), db varchar(255), anydb boolean, password text)"; -/** The query that is executed when a user is authenticated */ -static const char mysqlauth_validation_query[] = - "SELECT password FROM " MYSQLAUTH_TABLE_NAME +/** CREATE TABLE statement for the in-memory databases table */ +static const char databases_create_sql[] = + "CREATE TABLE IF NOT EXISTS " MYSQLAUTH_DATABASES_TABLE_NAME "(db varchar(255))"; + +/** Query that checks if there's a grant for the user being authenticated */ +static const char mysqlauth_validate_user_query[] = + "SELECT password FROM " MYSQLAUTH_USERS_TABLE_NAME " WHERE user = '%s' AND '%s' LIKE host AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)" " LIMIT 1"; +/** Query that checks that the database exists */ +static const char mysqlauth_validate_database_query[] = + "SELECT * FROM " MYSQLAUTH_DATABASES_TABLE_NAME " WHERE db = '%s' LIMIT 1"; + /** Delete query used to clean up the database before loading new users */ -static const char delete_query[] = "DELETE FROM " MYSQLAUTH_TABLE_NAME; +static const char delete_users_query[] = "DELETE FROM " MYSQLAUTH_USERS_TABLE_NAME; + +/** Delete query used to clean up the database before loading new users */ +static const char delete_databases_query[] = "DELETE FROM " MYSQLAUTH_DATABASES_TABLE_NAME; /** The insert query template which adds users to the mysqlauth_users table */ -static const char insert_sql_pattern[] = - "INSERT INTO " MYSQLAUTH_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)"; +static const char insert_user_query[] = + "INSERT INTO " MYSQLAUTH_USERS_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)"; + +/** The insert query template which adds the databases to the table */ +static const char insert_database_query[] = + "INSERT INTO " MYSQLAUTH_DATABASES_TABLE_NAME " VALUES ('%s')"; /** Used for NULL value creation in the INSERT query */ static const char null_token[] = "NULL";