Move loading of user data to authenticator modules

The authenticator modules now load the user data when the new loadusers
entry point is called. This new entry point is optional.

At the moment the code that was in service.c was just moved into the
modules but the ground work for allowing different user loading mechanisms
is done.

Further improvements need to be made so that the authenticators behave
more like routers and filters. This work includes the creation of a
AUTHENTICATOR module object, addition of createInstance entry points for
authenticators and implementing it for all authenticators.
This commit is contained in:
Markus Makela
2016-08-12 10:57:12 +03:00
parent c05f6b394f
commit 9a3da88e63
13 changed files with 258 additions and 387 deletions

View File

@ -249,40 +249,6 @@ load_mysql_users(SERV_LISTENER *listener)
return get_users(listener, listener->users);
}
/**
* Reload the user/passwd form mysql.user table into the service users' hashtable
* environment.
*
* @param service The current service
* @return -1 on any error or the number of users inserted (0 means no users at all)
*/
int
reload_mysql_users(SERV_LISTENER *listener)
{
int i;
USERS *newusers, *oldusers;
HASHTABLE *oldresources;
if ((newusers = mysql_users_alloc()) == NULL)
{
return 0;
}
spinlock_acquire(&listener->lock);
oldresources = listener->resources;
i = get_users(listener, newusers);
oldusers = listener->users;
listener->users = newusers;
spinlock_release(&listener->lock);
users_free(oldusers);
resource_free(oldresources);
return i;
}
/**
* Replace the user/passwd form mysql.user table into the service users' hashtable
* environment.
@ -294,33 +260,44 @@ reload_mysql_users(SERV_LISTENER *listener)
int
replace_mysql_users(SERV_LISTENER *listener)
{
int i;
USERS *newusers, *oldusers;
HASHTABLE *oldresources;
USERS *newusers = mysql_users_alloc();
if ((newusers = mysql_users_alloc()) == NULL)
if (newusers == NULL)
{
return -1;
}
spinlock_acquire(&listener->lock);
oldresources = listener->resources;
/* load db users ad db grants */
i = get_users(listener, newusers);
/** TODO: Make the listener resource a part of the USERS struct */
HASHTABLE *oldresources = listener->resources;
/* load users and grants from the backend database */
int i = get_users(listener, newusers);
if (i <= 0)
{
users_free(newusers);
/* restore resources */
/* Failed to load users, restore old users and resources */
listener->resources = oldresources;
spinlock_release(&listener->lock);
return i;
}
oldusers = listener->users;
USERS *oldusers = listener->users;
/* digest compare */
/**
* TODO: Comparing the checksum after loading users is not necessary. We
* have already queried the server, allocated memory and done the processing
* so comparing if a change was made is pointless since the end result is
* always the same. We end up with either the same users or a new set of
* users. If the new users would always be taken into use, we'd avoid
* the costly task of calculating the diff.
*
* An improvement to the diff calculation would be to push the calculation
* to the backend server. This way the bandwidth usage would be minimized
* and the backend server would tell us if we need to query for more data.
*/
if (oldusers != NULL && memcmp(oldusers->cksum, newusers->cksum,
SHA_DIGEST_LENGTH) == 0)
{

View File

@ -247,97 +247,6 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
listener_init_SSL(port->ssl);
}
/**
* The following code should be located inside the authenticator modules.
*
* The authentication of users is done against the data being gathered here
* but the actual authentication happens inside the authenticator module.
* Since not all authenticators use the same data for authentication, it
* should be up to the authenticator to decide where the user data is retrieved
* and how it is stored.
*
* One way to do it would be to add a @c dcb->authfunc.loadusers(dcb)
* entry point which loads the users and a @c dcb->authfunc.freeusers(dcb)
* which in turn frees the users. It would also make sense to have a
* @c dcb->authfunc.reloadusers(dcb) entry point which would allow for a more
* optimized user reloading process instead of freeing and loading the users
* again.
*/
if (strcmp(port->protocol, "MySQLClient") == 0)
{
int loaded;
if (port->users == NULL)
{
/*
* Allocate specific data for MySQL users
* including hosts and db names
*/
port->users = mysql_users_alloc();
const char* cachedir = get_cachedir();
if ((loaded = load_mysql_users(port)) < 0)
{
MXS_ERROR("Unable to load users for service %s listening at %s:%d.",
service->name, port->address ? port->address : "0.0.0.0",
port->port);
/* Try loading authentication data from file cache */
char path[PATH_MAX];
sprintf(path, "%s/%s/%s/%s/%s", cachedir, service->name, port->name,
DBUSERS_DIR, DBUSERS_FILE);
if ((loaded = dbusers_load(port->users, path)) == -1)
{
MXS_ERROR("Failed to load cached users from '%s'.", path);
users_free(port->users);
dcb_close(port->listener);
goto retblock;
}
else
{
MXS_WARNING("Using cached credential information.");
}
}
else
{
/* Save authentication data to file cache */
char path[PATH_MAX];
sprintf(path, "%s/%s/%s/%s/", cachedir, service->name, port->name, DBUSERS_DIR);
if (mxs_mkdir_all(path, 0777))
{
strcat(path, DBUSERS_FILE);
dbusers_save(port->users, path);
}
}
if (loaded == 0)
{
MXS_ERROR("[%s]: failed to load any user information. Authentication"
" will probably fail as a result.", service->name);
}
/* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
* This way MaxScale could try reloading users' just after startup
*/
service->rate_limit.last = time(NULL) - USERS_REFRESH_TIME;
service->rate_limit.nloads = 1;
MXS_NOTICE("Loaded %d MySQL Users for service [%s].",
loaded, service->name);
}
}
else
{
if (port->users == NULL)
{
/* Generic users table */
port->users = users_alloc();
}
}
if ((funcs = (GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL)) == NULL)
{
dcb_close(port->listener);
@ -348,8 +257,33 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
service->name);
goto retblock;
}
memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL));
const char *authenticator_name = "NullAuth";
if (port->authenticator)
{
authenticator_name = port->authenticator;
}
else if (port->listener->func.auth_default)
{
authenticator_name = port->listener->func.auth_default();
}
GWAUTHENTICATOR *authfuncs = (GWAUTHENTICATOR *)load_module(authenticator_name, MODULE_AUTHENTICATOR);
if (authfuncs == NULL)
{
MXS_ERROR("Failed to load authenticator module '%s' for listener '%s'",
authenticator_name, port->name);
dcb_close(port->listener);
port->listener = NULL;
return 0;
}
memcpy(&port->listener->authfunc, authfuncs, sizeof(GWAUTHENTICATOR));
if (port->address)
{
sprintf(config_bind, "%s:%d", port->address, port->port);
@ -359,6 +293,21 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
sprintf(config_bind, "0.0.0.0:%d", port->port);
}
/** Load the authentication users before before starting the listener */
if (port->listener->authfunc.loadusers &&
port->listener->authfunc.loadusers(port) != AUTH_LOADUSERS_OK)
{
MXS_ERROR("[%s] Failed to load users for listener '%s', authentication might not work.",
service->name, port->name);
}
/**
* At service start last update is set to USERS_REFRESH_TIME seconds earlier. This way MaxScale
* could try reloading users just after startup.
*/
service->rate_limit.last = time(NULL) - USERS_REFRESH_TIME;
service->rate_limit.nloads = 1;
if (port->listener->func.listen(port->listener, config_bind))
{
port->listener->session = session_alloc(service, port->listener);
@ -1129,7 +1078,7 @@ serviceSetFilters(SERVICE *service, char *filters)
}
else
{
MXS_ERROR("Unable to find filter '%s' for service '%s'\n",
MXS_ERROR("Unable to find filter '%s' for service '%s'",
filter_name, service->name);
rval = false;
break;
@ -1474,59 +1423,45 @@ service_update(SERVICE *service, char *router, char *user, char *auth)
int service_refresh_users(SERVICE *service)
{
int ret = 1;
/* check for another running getUsers request */
if (!spinlock_acquire_nowait(&service->users_table_spin))
if (spinlock_acquire_nowait(&service->users_table_spin))
{
MXS_DEBUG("%s: [service_refresh_users] failed to get get lock for "
"loading new users' table: another thread is loading users",
service->name);
time_t now = time(NULL);
return 1;
}
/* check if refresh rate limit has exceeded */
if ((time(NULL) < (service->rate_limit.last + USERS_REFRESH_TIME)) ||
/* Check if refresh rate limit has been exceeded */
if ((now < service->rate_limit.last + USERS_REFRESH_TIME) ||
(service->rate_limit.nloads > USERS_REFRESH_MAX_PER_TIME))
{
spinlock_release(&service->users_table_spin);
MXS_ERROR("%s: Refresh rate limit exceeded for load of users' table.",
service->name);
return 1;
}
service->rate_limit.nloads++;
/* update time and counter */
if (service->rate_limit.nloads > USERS_REFRESH_MAX_PER_TIME)
{
service->rate_limit.nloads = 1;
service->rate_limit.last = time(NULL);
}
SERV_LISTENER *port = service->ports;
while (port)
{
if (replace_mysql_users(port) < 0)
{
ret = -1;
break;
}
ret++;
port = port->next;
}
/* remove lock */
spinlock_release(&service->users_table_spin);
if (ret >= 0)
{
return 0;
MXS_ERROR("[%s] Refresh rate limit exceeded for load of users' table.", service->name);
}
else
{
return 1;
service->rate_limit.nloads++;
/** If we have reached the limit on users refreshes, reset refresh time and count */
if (service->rate_limit.nloads > USERS_REFRESH_MAX_PER_TIME)
{
service->rate_limit.nloads = 1;
service->rate_limit.last = now;
}
ret = 0;
for (SERV_LISTENER *port = service->ports; port; port = port->next)
{
if (port->listener->authfunc.loadusers(port) != AUTH_LOADUSERS_OK)
{
MXS_ERROR("[%s] Failed to load users for listener '%s', authentication might not work.",
service->name, port->name);
ret = 1;
}
}
}
spinlock_release(&service->users_table_spin);
}
return ret;
}
bool service_set_param_value(SERVICE* service,

View File

@ -71,18 +71,12 @@ users_alloc()
void
users_free(USERS *users)
{
if (users == NULL)
{
MXS_ERROR("[%s:%d]: NULL parameter.", __FUNCTION__, __LINE__);
return;
}
if (users->data)
if (users)
{
hashtable_free(users->data);
}
MXS_FREE(users);
}
}
/**
* Add a new user to the user table. The user name must be unique
@ -217,3 +211,18 @@ dcb_usersPrint(DCB *dcb, USERS *users)
}
dcb_printf(dcb, "\n");
}
/**
* @brief Default user loading function
*
* A generic key-value user table is allocated for the service.
*
* @param port Listener configuration
* @return Always AUTH_LOADUSERS_OK
*/
int users_default_loadusers(SERV_LISTENER *port)
{
users_free(port->users);
port->users = users_alloc();
return AUTH_LOADUSERS_OK;
}

View File

@ -36,6 +36,7 @@
struct dcb;
struct server;
struct session;
struct servlistener;
/**
* @verbatim
@ -44,6 +45,8 @@ struct session;
* extract Extract the data from a buffer and place in a structure
* connectssl Determine whether the connection can support SSL
* authenticate Carry out the authentication
* free Free extracted data
* loadusers Load or update authenticator user data
* @endverbatim
*
* This forms the "module object" for authenticator modules within the gateway.
@ -56,14 +59,19 @@ typedef struct gw_authenticator
bool (*connectssl)(struct dcb *);
int (*authenticate)(struct dcb *);
void (*free)(struct dcb *);
int (*loadusers)(struct servlistener *);
} GWAUTHENTICATOR;
/** Return values for the loadusers entry point */
#define AUTH_LOADUSERS_OK 0 /**< Users loaded successfully */
#define AUTH_LOADUSERS_ERROR 1 /**< Failed to load users */
/**
* The GWAUTHENTICATOR version data. The following should be updated whenever
* the GWAUTHENTICATOR structure is changed. See the rules defined in modinfo.h
* that define how these numbers should change.
*/
#define GWAUTHENTICATOR_VERSION {1, 0, 0}
#define GWAUTHENTICATOR_VERSION {1, 1, 0}
#endif /* GW_AUTHENTICATOR_H */

View File

@ -14,6 +14,7 @@
*/
#include <hashtable.h>
#include <dcb.h>
#include <listener.h>
#include <openssl/sha.h>
/**
@ -64,6 +65,8 @@ extern int users_delete(USERS *, char *); /**< Delete a user from the us
extern char *users_fetch(USERS *, char *); /**< Fetch the authentication data for a user */
extern int users_update(USERS *, char *, char *); /**< Change the password data for a user in
the users table */
extern int users_default_loadusers(SERV_LISTENER *port); /**< A generic implementation of the authenticator
* loadusers entry point */
extern void usersPrint(USERS *); /**< Print data about the users loaded */
extern void dcb_usersPrint(DCB *, USERS *); /**< Print data about the users loaded */

View File

@ -46,8 +46,7 @@ MODULE_INFO info =
"The CDC client to MaxScale authenticator implementation"
};
static char *version_str = "V1.0.0";
static int cdc_load_users_init = 0;
static char *version_str = "V1.1.0";
static int cdc_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
static bool cdc_auth_is_client_ssl_capable(DCB *dcb);
@ -55,9 +54,6 @@ static int cdc_auth_authenticate(DCB *dcb);
static void cdc_auth_free_client_data(DCB *dcb);
static int cdc_set_service_user(SERV_LISTENER *listener);
static int cdc_read_users(USERS *users, char *usersfile);
static int cdc_load_users(SERV_LISTENER *listener);
static int cdc_refresh_users(SERV_LISTENER *listener);
static int cdc_replace_users(SERV_LISTENER *listener);
extern char *gw_bin2hex(char *out, const uint8_t *in, unsigned int len);
@ -75,6 +71,7 @@ static GWAUTHENTICATOR MyObject =
cdc_auth_is_client_ssl_capable, /* Check if client supports SSL */
cdc_auth_authenticate, /* Authenticate user credentials */
cdc_auth_free_client_data, /* Free the client data held in DCB */
cdc_replace_users
};
static int cdc_auth_check(
@ -136,26 +133,9 @@ GWAUTHENTICATOR* GetModuleObject()
static int cdc_auth_check(DCB *dcb, CDC_protocol *protocol, char *username, uint8_t *auth_data,
unsigned int *flags)
{
char *user_password;
char *user_password = users_fetch(dcb->listener->users, username);
if (!cdc_load_users_init)
{
/* Load db users or set service user */
if (cdc_load_users(dcb->listener) < 1)
{
cdc_set_service_user(dcb->listener);
}
cdc_load_users_init = 1;
}
user_password = users_fetch(dcb->listener->users, username);
if (!user_password)
{
return CDC_STATE_AUTH_FAILED;
}
else
if (user_password)
{
/* compute SHA1 of auth_data */
uint8_t sha1_step1[SHA_DIGEST_LENGTH] = "";
@ -173,6 +153,8 @@ static int cdc_auth_check(DCB *dcb, CDC_protocol *protocol, char *username, uint
return CDC_STATE_AUTH_FAILED;
}
}
return CDC_STATE_AUTH_FAILED;
}
/**
@ -200,10 +182,9 @@ cdc_auth_authenticate(DCB *dcb)
auth_ret = cdc_auth_check(dcb, protocol, client_data->user, client_data->auth_data, client_data->flags);
/* On failed authentication try to reload user table */
if (CDC_STATE_AUTH_OK != auth_ret && 0 == cdc_refresh_users(dcb->listener))
/* On failed authentication try to reload users and authenticate again */
if (CDC_STATE_AUTH_OK != auth_ret && cdc_replace_users(dcb->listener) == AUTH_LOADUSERS_OK)
{
/* Call protocol authentication */
auth_ret = cdc_auth_check(dcb, protocol, client_data->user, client_data->auth_data, client_data->flags);
}
@ -415,48 +396,6 @@ cdc_set_service_user(SERV_LISTENER *listener)
return 0;
}
/*
* Load AVRO users into (service->users)
*
* @param service The current service
* @return -1 on failure, 0 for no users found, > 0 for found users
*/
static int
cdc_load_users(SERV_LISTENER *listener)
{
SERVICE *service = listener->service;
int loaded = -1;
char path[PATH_MAX + 1] = "";
/* File path for router cached authentication data */
snprintf(path, PATH_MAX, "%s/%s/cdcusers", get_datadir(), service->name);
/* Allocate users table */
if (listener->users == NULL)
{
listener->users = users_alloc();
}
/* Try loading authentication data from file cache */
loaded = cdc_read_users(listener->users, path);
if (loaded == -1)
{
MXS_ERROR("Service %s, Unable to read AVRO users information from %s."
" No AVRO user added to service users table. Service user is still allowed to connect.",
service->name,
path);
}
/* At service start last update is set to CDC_USERS_REFRESH_TIME seconds
* earlier. This way MaxScale could try reloading users just after startup.
*/
service->rate_limit.last = time(NULL) - CDC_USERS_REFRESH_TIME;
service->rate_limit.nloads = 1;
return loaded;
}
/**
* Load the AVRO users
*
@ -539,127 +478,47 @@ cdc_read_users(USERS *users, char *usersfile)
return loaded;
}
/**
* * Refresh the database users for the service
* * This function replaces the MySQL users used by the service with the latest
* * version found on the backend servers. There is a limit on how often the users
* * can be reloaded and if this limit is exceeded, the reload will fail.
* * @param service Service to reload
* * @return 0 on success and 1 on error
* */
static int
cdc_refresh_users(SERV_LISTENER *listener)
{
int ret = 1;
SERVICE *service = listener->service;
/* check for another running getUsers request */
if (!spinlock_acquire_nowait(&service->users_table_spin))
{
MXS_DEBUG("%s: [service_refresh_users] failed to get get lock for "
"loading new users' table: another thread is loading users",
service->name);
return 1;
}
/* check if refresh rate limit has exceeded */
if ((time(NULL) < (service->rate_limit.last + CDC_USERS_REFRESH_TIME)) ||
(service->rate_limit.nloads > CDC_USERS_REFRESH_MAX_PER_TIME))
{
spinlock_release(&service->users_table_spin);
MXS_ERROR("%s: Refresh rate limit exceeded for load of users' table.",
service->name);
return 1;
}
service->rate_limit.nloads++;
/* update time and counter */
if (service->rate_limit.nloads > CDC_USERS_REFRESH_MAX_PER_TIME)
{
service->rate_limit.nloads = 1;
service->rate_limit.last = time(NULL);
}
ret = cdc_replace_users(listener);
/* remove lock */
spinlock_release(&service->users_table_spin);
if (ret >= 0)
{
return 0;
}
else
{
return 1;
}
}
/* Replace the user/passwd in the servicei users tbale from a db file.
* The replacement is succesful only if the users' table checksums differ
* @brief Replace the user/passwd in the servicei users tbale from a db file
*
* @param service The current service
* @return -1 on any error or the number of users inserted (0 means no users at all)
* */
static int
cdc_replace_users(SERV_LISTENER *listener)
*/
int cdc_replace_users(SERV_LISTENER *listener)
{
SERVICE *service = listener->service;
int i;
USERS *newusers, *oldusers;
char path[PATH_MAX + 1] = "";
int rc = AUTH_LOADUSERS_OK;
USERS *newusers = users_alloc();
/* File path for router cached authentication data */
snprintf(path, PATH_MAX, "%s/%s/cdcusers", get_datadir(), service->name);
if ((newusers = users_alloc()) == NULL)
if (newusers)
{
return -1;
}
char path[PATH_MAX + 1];
snprintf(path, PATH_MAX, "%s/%s/cdcusers", get_datadir(), listener->service->name);
/* load users */
i = cdc_read_users(newusers, path);
int i = cdc_read_users(newusers, path);
if (i <= 0)
{
/** Failed to read users or no users loaded */
users_free(newusers);
return i;
}
spinlock_acquire(&listener->lock);
oldusers = listener->users;
/* digest compare */
if (oldusers != NULL && memcmp(oldusers->cksum, newusers->cksum,
SHA_DIGEST_LENGTH) == 0)
if (i < 0)
{
/* same data, nothing to do */
MXS_DEBUG("%lu [cdc_replace_users] users' tables not switched, checksum is the same",
pthread_self());
/* free the new table */
users_free(newusers);
i = 0;
rc = AUTH_LOADUSERS_ERROR;
}
}
else
{
/* replace the service with effective new data */
MXS_DEBUG("%lu [cdc_replace_users] users' tables replaced, checksum differs",
pthread_self());
spinlock_acquire(&listener->lock);
USERS *oldusers = listener->users;
listener->users = newusers;
}
spinlock_release(&listener->lock);
if (i && oldusers)
if (oldusers)
{
/* free the old table */
users_free(oldusers);
}
return i;
}
}
return rc;
}

View File

@ -12,7 +12,7 @@
*/
/**
* @file http_ba__auth.c
* @file http_ba_auth.c
*
* MaxScale HTTP Basic Access authentication for the HTTPD protocol module.
*
@ -32,6 +32,7 @@
#include <openssl/bio.h>
#include <service.h>
#include <secrets.h>
#include <users.h>
/* @see function load_module in load_utils.c for explanation of the following
* lint directives.
@ -46,7 +47,7 @@ MODULE_INFO info =
};
/*lint +e14 */
static char *version_str = "V1.0.0";
static char *version_str = "V1.1.0";
static int http_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
static bool http_auth_is_client_ssl_capable(DCB *dcb);
@ -62,6 +63,7 @@ static GWAUTHENTICATOR MyObject =
http_auth_is_client_ssl_capable, /* Check if client supports SSL */
http_auth_authenticate, /* Authenticate user credentials */
http_auth_free_client_data, /* Free the client data held in DCB */
users_default_loadusers
};
typedef struct http_auth

View File

@ -32,6 +32,7 @@
#include <dcb.h>
#include <buffer.h>
#include <adminusers.h>
#include <users.h>
/* @see function load_module in load_utils.c for explanation of the following
* lint directives.
@ -46,7 +47,7 @@ MODULE_INFO info =
};
/*lint +e14 */
static char *version_str = "V2.0.0";
static char *version_str = "V2.1.0";
static int max_admin_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
static bool max_admin_auth_is_client_ssl_capable(DCB *dcb);
@ -62,6 +63,7 @@ static GWAUTHENTICATOR MyObject =
max_admin_auth_is_client_ssl_capable, /* Check if client supports SSL */
max_admin_auth_authenticate, /* Authenticate user credentials */
max_admin_auth_free_client_data, /* Free the client data held in DCB */
users_default_loadusers
};
/**

View File

@ -30,6 +30,11 @@
#include <gw_authenticator.h>
#include <maxscale/alloc.h>
#include <maxscale/poll.h>
#include <dbusers.h>
#include <gwdirs.h>
#include <gw.h>
#include <secrets.h>
#include <utils.h>
/* @see function load_module in load_utils.c for explanation of the following
* lint directives.
@ -44,12 +49,13 @@ MODULE_INFO info =
};
/*lint +e14 */
static char *version_str = "V1.0.0";
static char *version_str = "V1.1.0";
static int mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
static bool mysql_auth_is_client_ssl_capable(DCB *dcb);
static int mysql_auth_authenticate(DCB *dcb);
static void mysql_auth_free_client_data(DCB *dcb);
static int mysql_auth_load_users(SERV_LISTENER *port);
/*
* The "module object" for mysql client authenticator module.
@ -60,6 +66,7 @@ static GWAUTHENTICATOR MyObject =
mysql_auth_is_client_ssl_capable, /* Check if client supports SSL */
mysql_auth_authenticate, /* Authenticate user credentials */
mysql_auth_free_client_data, /* Free the client data held in DCB */
mysql_auth_load_users /* Load users from backend databases */
};
static int combined_auth_check(
@ -811,3 +818,62 @@ mysql_auth_free_client_data(DCB *dcb)
{
MXS_FREE(dcb->data);
}
/**
* @brief Load MySQL authentication users
*
* This function loads MySQL users from the backend database.
*
* @param port Listener definition
* @return AUTH_LOADUSERS_OK on success, AUTH_LOADUSERS_ERROR on error
*/
static int mysql_auth_load_users(SERV_LISTENER *port)
{
int rc = AUTH_LOADUSERS_OK;
SERVICE *service = port->listener->service;
int loaded = replace_mysql_users(port);
if (loaded < 0)
{
MXS_ERROR("[%s] Unable to load users for listener %s listening at %s:%d.", service->name,
port->name, port->address ? port->address : "0.0.0.0", port->port);
/* Try loading authentication data from file cache */
char path[PATH_MAX];
sprintf(path, "%s/%s/%s/%s/%s", get_cachedir(), service->name, port->name,
DBUSERS_DIR, DBUSERS_FILE);
if ((loaded = dbusers_load(port->users, path)) == -1)
{
MXS_ERROR("[%s] Failed to load cached users from '%s'.", service->name, path);
users_free(port->users);
port->users = NULL;
rc = AUTH_LOADUSERS_ERROR;
}
else
{
MXS_WARNING("Using cached credential information.");
}
}
else
{
/* Users loaded successfully, save authentication data to file cache */
char path[PATH_MAX];
sprintf(path, "%s/%s/%s/%s/", get_cachedir(), service->name, port->name, DBUSERS_DIR);
if (mxs_mkdir_all(path, 0777))
{
strcat(path, DBUSERS_FILE);
dbusers_save(port->users, path);
}
}
if (loaded == 0)
{
MXS_ERROR("[%s]: failed to load any user information. Authentication"
" will probably fail as a result.", service->name);
}
MXS_NOTICE("[%s] Loaded %d MySQL users for listener %s.", service->name, loaded, port->name);
return rc;
}

View File

@ -31,6 +31,7 @@
#include <modinfo.h>
#include <dcb.h>
#include <buffer.h>
#include <users.h>
/* @see function load_module in load_utils.c for explanation of the following
* lint directives.
@ -45,7 +46,7 @@ MODULE_INFO info =
};
/*lint +e14 */
static char *version_str = "V1.0.0";
static char *version_str = "V1.1.0";
static int null_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
static bool null_auth_is_client_ssl_capable(DCB *dcb);
@ -61,6 +62,7 @@ static GWAUTHENTICATOR MyObject =
null_auth_is_client_ssl_capable, /* Check if client supports SSL */
null_auth_authenticate, /* Authenticate user credentials */
null_auth_free_client_data, /* Free the client data held in DCB */
users_default_loadusers
};
/**

View File

@ -31,6 +31,7 @@
#include <modinfo.h>
#include <dcb.h>
#include <buffer.h>
#include <users.h>
/* @see function load_module in load_utils.c for explanation of the following
* lint directives.
@ -45,7 +46,7 @@ MODULE_INFO info =
};
/*lint +e14 */
static char *version_str = "V1.0.0";
static char *version_str = "V1.1.0";
static int null_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
static bool null_auth_is_client_ssl_capable(DCB *dcb);
@ -61,6 +62,7 @@ static GWAUTHENTICATOR MyObject =
null_auth_is_client_ssl_capable, /* Check if client supports SSL */
null_auth_authenticate, /* Authenticate user credentials */
null_auth_free_client_data, /* Free the client data held in DCB */
users_default_loadusers
};
/**

View File

@ -1279,8 +1279,14 @@ clear_server(DCB *dcb, SERVER *server, char *bit)
static void
reload_dbusers(DCB *dcb, SERVICE *service)
{
dcb_printf(dcb, "Loaded %d database users for service %s.\n",
reload_mysql_users(service->ports), service->name);
if (service_refresh_users(service) == 0)
{
dcb_printf(dcb, "Reloaded database users for service %s.\n", service->name);
}
else
{
dcb_printf(dcb, "Error: Failed to reloaded database users for service %s.\n", service->name);
}
}
/**