Store databases in the SQLite database
The databases are now also stored in the sqlite database. This allows the `resource` member of the USERS struct to be removed in the future.
This commit is contained in:
parent
041c0f1f2d
commit
2f4df0c21e
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
Loading…
x
Reference in New Issue
Block a user