Develop merge

Develop merge
This commit is contained in:
MassimilianoPinto
2016-09-08 08:53:32 +02:00
25 changed files with 578 additions and 455 deletions

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,43 +133,25 @@ GWAUTHENTICATOR* GetModuleObject()
static int cdc_auth_check(DCB *dcb, CDC_protocol *protocol, char *username, uint8_t *auth_data,
unsigned int *flags)
{
char *user_password;
if (!cdc_load_users_init)
if (dcb->listener->users)
{
/* Load db users or set service user */
if (cdc_load_users(dcb->listener) < 1)
char *user_password = users_fetch(dcb->listener->users, username);
if (user_password)
{
cdc_set_service_user(dcb->listener);
}
/* compute SHA1 of auth_data */
uint8_t sha1_step1[SHA_DIGEST_LENGTH] = "";
char hex_step1[2 * SHA_DIGEST_LENGTH + 1] = "";
cdc_load_users_init = 1;
}
gw_sha1_str(auth_data, SHA_DIGEST_LENGTH, sha1_step1);
gw_bin2hex(hex_step1, sha1_step1, SHA_DIGEST_LENGTH);
user_password = users_fetch(dcb->listener->users, username);
if (!user_password)
{
return CDC_STATE_AUTH_FAILED;
}
else
{
/* compute SHA1 of auth_data */
uint8_t sha1_step1[SHA_DIGEST_LENGTH] = "";
char hex_step1[2 * SHA_DIGEST_LENGTH + 1] = "";
gw_sha1_str(auth_data, SHA_DIGEST_LENGTH, sha1_step1);
gw_bin2hex(hex_step1, sha1_step1, SHA_DIGEST_LENGTH);
if (memcmp(user_password, hex_step1, SHA_DIGEST_LENGTH) == 0)
{
return CDC_STATE_AUTH_OK;
}
else
{
return CDC_STATE_AUTH_FAILED;
return memcmp(user_password, hex_step1, SHA_DIGEST_LENGTH) == 0 ?
CDC_STATE_AUTH_OK : CDC_STATE_AUTH_FAILED;
}
}
return CDC_STATE_AUTH_FAILED;
}
/**
@ -200,10 +179,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 +393,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 +475,52 @@ 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)
* @param service The current service
*/
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_ERROR;
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);
int i = cdc_read_users(newusers, path);
USERS *oldusers = NULL;
spinlock_acquire(&listener->lock);
if (i > 0)
{
/** Successfully loaded at least one user */
oldusers = listener->users;
listener->users = newusers;
rc = AUTH_LOADUSERS_OK;
}
else if (listener->users)
{
/** Failed to load users, use the old users table */
users_free(newusers);
}
else
{
/** No existing users, use the new empty users table */
listener->users = newusers;
}
cdc_set_service_user(listener);
spinlock_release(&listener->lock);
if (oldusers)
{
users_free(oldusers);
}
}
/* load users */
i = cdc_read_users(newusers, path);
if (i <= 0)
{
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)
{
/* 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;
}
else
{
/* replace the service with effective new data */
MXS_DEBUG("%lu [cdc_replace_users] users' tables replaced, checksum differs",
pthread_self());
listener->users = newusers;
}
spinlock_release(&listener->lock);
if (i && 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,63 @@ 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);;
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_WARNING("[%s]: failed to load any user information. Authentication"
" will probably fail as a result.", service->name);
}
else if (loaded > 0)
{
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

@ -35,6 +35,7 @@
* 22/07/16 Massimiliano Pinto Added Semi-Sync replication support
* 24/08/16 Massimiliano Pinto Added slave notification state CS_WAIT_DATA.
* State CS_UPTODATE removed.
* 01/09/2016 Massimiliano Pinto Added support for ANNOTATE_ROWS_EVENT in COM_BINLOG_DUMP
*
* @endverbatim
*/
@ -124,6 +125,11 @@
#define LOG_EVENT_NO_FILTER_F 0x0100
#define LOG_EVENT_MTS_ISOLATE_F 0x0200
/**
* Binlog COM_BINLOG_DUMP flags
*/
#define BLR_REQUEST_ANNOTATE_ROWS_EVENT 2
/**
* How often to call the binlog status function (seconds)
*/

View File

@ -1792,7 +1792,7 @@ static void rses_end_locked_router_action(ROUTER_SLAVE *rses)
static int getCapabilities()
{
return (int)RCAP_TYPE_NO_RSESSION;
return (int)(RCAP_TYPE_NO_RSESSION | RCAP_TYPE_NO_USERS_INIT);
}
/**

View File

@ -49,6 +49,7 @@
* 26/04/2016 Massimiliano Pinto Added MariaDB 10.0 and 10.1 GTID event flags detection
* 22/07/2016 Massimiliano Pinto Added semi_sync replication support
* 24/08/2016 Massimiliano Pinto Added slave notification and blr_distribute_binlog_record removed
* 01/09/2016 Massimiliano Pinto Added support for ANNOTATE_ROWS_EVENT in COM_BINLOG_DUMP
*
* @endverbatim
*/
@ -948,7 +949,18 @@ blr_make_binlog_dump(ROUTER_INSTANCE *router)
data[4] = COM_BINLOG_DUMP; // Command
encode_value(&data[5],
router->current_pos, 32); // binlog position
encode_value(&data[9], 0, 16); // Flags
/* With mariadb10 always ask for annotate rows events */
if (router->mariadb10_compat)
{
// set flag for annotate rows event
encode_value(&data[9], BLR_REQUEST_ANNOTATE_ROWS_EVENT, 16);
}
else
{
encode_value(&data[9], 0, 16); // No flag set
}
encode_value(&data[11],
router->serverid, 32); // Server-id of MaxScale
memcpy((char *)&data[15], router->binlog_name,

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);
}
}
/**