diff --git a/server/modules/authenticator/MySQLAuth/dbusers.c b/server/modules/authenticator/MySQLAuth/dbusers.c index 6572f5595..2c33bad9a 100644 --- a/server/modules/authenticator/MySQLAuth/dbusers.c +++ b/server/modules/authenticator/MySQLAuth/dbusers.c @@ -128,8 +128,7 @@ See earlier error messages for user '%s' for more information." UNION \ SELECT u.user, u.host, t.db, u.select_priv, u.%s \ FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t \ - ON (u.user = t.user AND u.host = t.host) %s\ - ORDER BY user" + ON (u.user = t.user AND u.host = t.host) %s" static int add_databases(SERV_LISTENER *listener, MYSQL *con); static int add_wildcard_users(USERS *users, char* name, char* host, @@ -228,7 +227,7 @@ static char* get_usercount_query(const char* server_version, bool include_root, static char* get_new_users_query(const char *server_version, bool include_root) { const char* password = strstr(server_version, "5.7.") ? MYSQL57_PASSWORD : MYSQL_PASSWORD; - const char *with_root = include_root ? "user.user NOT IN ('root')" : ""; + const char *with_root = include_root ? "WHERE u.user NOT IN ('root')" : ""; size_t n_bytes = snprintf(NULL, 0, NEW_LOAD_DBUSERS_QUERY, password, with_root, password, with_root); char *rval = MXS_MALLOC(n_bytes + 1); @@ -1374,8 +1373,8 @@ static void delete_mysql_users(sqlite3 *handle) * @param db Database * @param anydb Global access to databases */ -static void add_mysql_user(sqlite3 *handle, const char *user, const char *host, - const char *db, bool anydb, const char *pw) +void add_mysql_user(sqlite3 *handle, const char *user, const char *host, + const char *db, bool anydb, const char *pw) { size_t dblen = db && *db ? strlen(db) + 2 : sizeof(null_token); /** +2 for single quotes */ char dbstr[dblen + 1]; @@ -2688,30 +2687,106 @@ dbusers_valueread(int fd) return (void *) value; } -/** - * Save the dbusers data to a hashtable file - * - * @param users The hashtable that stores the user data - * @param filename The filename to save the data in - * @return The number of entries saved - */ -int -dbusers_save(USERS *users, const char *filename) +int dump_user_cb(void *data, int fields, char **row, char **field_names) { - return hashtable_save(users->data, filename, dbusers_keywrite, dbusers_valuewrite); + sqlite3 *handle = (sqlite3*)data; + add_mysql_user(handle, row[0], row[1], row[2], row[3] && strcmp(row[3], "1"), row[4]); + return 0; +} + +int dump_database_cb(void *data, int fields, char **row, char **field_names) +{ + sqlite3 *handle = (sqlite3*)data; + add_database(handle, row[0]); + return 0; +} + +static bool transfer_table_contents(sqlite3 *src, sqlite3 *dest) +{ + bool rval = true; + char *err; + + /** Make sure the tables exist in both databases */ + if (sqlite3_exec(src, users_create_sql, NULL, NULL, &err) != SQLITE_OK || + sqlite3_exec(dest, users_create_sql, NULL, NULL, &err) != SQLITE_OK || + sqlite3_exec(src, databases_create_sql, NULL, NULL, &err) != SQLITE_OK || + sqlite3_exec(dest, databases_create_sql, NULL, NULL, &err) != SQLITE_OK) + { + MXS_ERROR("Failed to create tables: %s", err); + sqlite3_free(err); + rval = false; + } + + if (sqlite3_exec(dest, "BEGIN", NULL, NULL, &err) != SQLITE_OK) + { + MXS_ERROR("Failed to start transaction: %s", err); + sqlite3_free(err); + rval = false; + } + + /** Replace the data */ + if (sqlite3_exec(src, dump_users_query, dump_user_cb, dest, &err) != SQLITE_OK || + sqlite3_exec(src, dump_databases_query, dump_database_cb, dest, &err) != SQLITE_OK) + { + MXS_ERROR("Failed to load database contents: %s", err); + sqlite3_free(err); + rval = false; + } + + if (sqlite3_exec(dest, "COMMIT", NULL, NULL, &err) != SQLITE_OK) + { + MXS_ERROR("Failed to commit transaction: %s", err); + sqlite3_free(err); + rval = false; + } + + return rval; } /** - * Load the dbusers data from a saved hashtable file + * Load users from persisted database * - * @param users The hashtable that stores the user data - * @param filename The filename to laod the data from - * @return The number of entries loaded + * @param dest Open SQLite handle where contents are loaded + * + * @return True on success */ -int -dbusers_load(USERS *users, const char *filename) +bool dbusers_load(sqlite3 *dest, const char *filename) { - return hashtable_load(users->data, filename, dbusers_keyread, dbusers_valueread); + sqlite3 *src; + + if (sqlite3_open_v2(filename, &src, db_flags, NULL) != SQLITE_OK) + { + MXS_ERROR("Failed to open persisted SQLite3 database."); + return false; + } + + bool rval = transfer_table_contents(src, dest); + sqlite3_close_v2(src); + + return rval; +} + +/** + * Save users to persisted database + * + * @param dest Open SQLite handle where contents are stored + * + * @return True on success + */ +bool dbusers_save(sqlite3 *src, const char *filename) +{ + sqlite3 *dest; + + if (sqlite3_open_v2(filename, &dest, db_flags, NULL) != SQLITE_OK) + { + MXS_ERROR("Failed to open persisted SQLite3 database."); + return -1; + } + + bool rval = transfer_table_contents(src, dest); + sqlite3_close_v2(dest); + + return rval; } /** @@ -2861,11 +2936,12 @@ static bool check_server_permissions(SERVICE *service, SERVER* server, server_set_version_string(server, server_string); } - char query[MAX_QUERY_STR_LEN]; + const char *template = "SELECT user, host, %s, Select_priv FROM mysql.user limit 1"; const char* query_pw = strstr(server->server_string, "5.7.") ? - MYSQL57_PASSWORD : MYSQL_PASSWORD; + MYSQL57_PASSWORD : MYSQL_PASSWORD; + char query[strlen(template) + strlen(query_pw) + 1]; bool rval = true; - snprintf(query, sizeof(query), "SELECT user, host, %s, Select_priv FROM mysql.user limit 1", query_pw); + sprintf(query, template, query_pw); if (mysql_query(mysql, query) != 0) { diff --git a/server/modules/authenticator/MySQLAuth/mysql_auth.c b/server/modules/authenticator/MySQLAuth/mysql_auth.c index c8d05e1b2..107d84abe 100644 --- a/server/modules/authenticator/MySQLAuth/mysql_auth.c +++ b/server/modules/authenticator/MySQLAuth/mysql_auth.c @@ -878,8 +878,9 @@ static bool add_service_user(SERV_LISTENER *port) if (newpw) { - add_mysql_users_with_host_ipv4(port->users, user, "%", newpw, "Y", ""); - add_mysql_users_with_host_ipv4(port->users, user, "localhost", newpw, "Y", ""); + MYSQL_AUTH *inst = (MYSQL_AUTH*)port->auth_instance; + add_mysql_user(inst->handle, user, "%", newpw, "Y", ""); + add_mysql_user(inst->handle, user, "localhost", newpw, "Y", ""); MXS_FREE(newpw); rval = true; } @@ -937,7 +938,7 @@ static int mysql_auth_load_users(SERV_LISTENER *port) strcat(path, DBUSERS_FILE); - if ((loaded = dbusers_load(port->users, path)) == -1) + if (!dbusers_load(instance->handle, path)) { MXS_ERROR("[%s] Failed to load cached users from '%s'.", service->name, path); rc = MXS_AUTH_LOADUSERS_ERROR; @@ -963,7 +964,7 @@ static int mysql_auth_load_users(SERV_LISTENER *port) if (mxs_mkdir_all(path, 0777)) { strcat(path, DBUSERS_FILE); - dbusers_save(port->users, path); + dbusers_save(instance->handle, path); MXS_INFO("[%s] Storing cached credential information at '%s'.", service->name, path); } } diff --git a/server/modules/authenticator/MySQLAuth/mysql_auth.h b/server/modules/authenticator/MySQLAuth/mysql_auth.h index 96348e4c0..1bcf70aa9 100644 --- a/server/modules/authenticator/MySQLAuth/mysql_auth.h +++ b/server/modules/authenticator/MySQLAuth/mysql_auth.h @@ -39,7 +39,7 @@ MXS_BEGIN_DECLS /** Cache directory and file names */ static const char DBUSERS_DIR[] = "cache"; -static const char DBUSERS_FILE[] = "dbusers"; +static const char DBUSERS_FILE[] = "dbusers.db"; #define MYSQLAUTH_DATABASE_NAME "file:mysqlauth.db?mode=memory&cache=shared" @@ -76,11 +76,17 @@ static const char delete_databases_query[] = "DELETE FROM " MYSQLAUTH_DATABASES_ /** The insert query template which adds users to the mysqlauth_users table */ static const char insert_user_query[] = - "INSERT INTO " MYSQLAUTH_USERS_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)"; + "INSERT OR REPLACE 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')"; + "INSERT OR REPLACE INTO " MYSQLAUTH_DATABASES_TABLE_NAME " VALUES ('%s')"; + +static const char dump_users_query[] = + "SELECT user, host, db, anydb, password FROM " MYSQLAUTH_USERS_TABLE_NAME; + +static const char dump_databases_query[] = + "SELECT db FROM " MYSQLAUTH_DATABASES_TABLE_NAME; /** Used for NULL value creation in the INSERT query */ static const char null_token[] = "NULL"; @@ -117,11 +123,14 @@ typedef struct mysql_user_host_key char hostname[MYSQL_HOST_MAXLEN + 1]; } MYSQL_USER_HOST; +void add_mysql_user(sqlite3 *handle, const char *user, const char *host, + const char *db, bool anydb, const char *pw); + extern int add_mysql_users_with_host_ipv4(USERS *users, const char *user, const char *host, char *passwd, const char *anydb, const char *db); extern bool check_service_permissions(SERVICE* service); -extern int dbusers_load(USERS *, const char *filename); -extern int dbusers_save(USERS *, const char *filename); +extern bool dbusers_load(sqlite3 *handle, const char *filename); +extern bool dbusers_save(sqlite3 *src, const char *filename); extern int mysql_users_add(USERS *users, MYSQL_USER_HOST *key, char *auth); extern USERS *mysql_users_alloc(); extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);