Merge branch 'fix-127' into feature-MAX-2
Conflicts: server/core/dbusers.c server/core/service.c server/core/users.c server/include/dbusers.h server/include/users.h server/modules/protocol/mysql_client.c server/modules/protocol/mysql_common.c
This commit is contained in:
parent
17b328cb9a
commit
45543eceed
2
doxygate
2
doxygate
@ -25,7 +25,7 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
|
||||
# by quotes) that should identify the project.
|
||||
|
||||
PROJECT_NAME = SkySQL Gateway
|
||||
PROJECT_NAME = MaxScale
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
|
||||
# This could be handy for archiving the generated documentation or
|
||||
|
@ -26,7 +26,8 @@
|
||||
* 24/06/2013 Massimiliano Pinto Initial implementation
|
||||
* 08/08/2013 Massimiliano Pinto Fixed bug for invalid memory access in row[1]+1 when row[1] is ""
|
||||
* 06/02/2014 Massimiliano Pinto Mysql user root selected based on configuration flag
|
||||
* 07/02/2014 Massimiliano Pinto Added Mysql user@host authentication
|
||||
* 26/02/2014 Massimiliano Pinto Addd: replace_mysql_users() routine may replace users' table based on a checksum
|
||||
* 28/02/2014 Massimiliano Pinto Added Mysql user@host authentication
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -37,13 +38,15 @@
|
||||
#include <dcb.h>
|
||||
#include <service.h>
|
||||
#include <users.h>
|
||||
#include <dbusers.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dbusers.h>
|
||||
|
||||
#define USERS_QUERY_NO_ROOT " AND user NOT IN ('root')"
|
||||
#define LOAD_MYSQL_USERS_QUERY "SELECT user, host, password FROM mysql.user WHERE user IS NOT NULL AND user <> ''"
|
||||
#define LOAD_MYSQL_USERS_QUERY "SELECT user, host, password, concat(user,host,password) AS userdata FROM mysql.user WHERE user IS NOT NULL AND user <> ''"
|
||||
#define MYSQL_USERS_COUNT "SELECT COUNT(1) AS nusers FROM mysql.user"
|
||||
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
@ -93,6 +96,57 @@ struct users *newusers, *oldusers;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the user/passwd form mysql.user table into the service users' hashtable
|
||||
* environment.
|
||||
* The replacement is succesful only if the users' table checksums differ
|
||||
*
|
||||
* @param service The current service
|
||||
* @return -1 on any error or the number of users inserted (0 means no users at all)
|
||||
*/
|
||||
int
|
||||
replace_mysql_users(SERVICE *service)
|
||||
{
|
||||
int i;
|
||||
struct users *newusers, *oldusers;
|
||||
|
||||
if ((newusers = users_alloc()) == NULL)
|
||||
return -1;
|
||||
|
||||
i = getUsers(service, newusers);
|
||||
|
||||
if (i <= 0)
|
||||
return i;
|
||||
|
||||
spinlock_acquire(&service->spin);
|
||||
oldusers = service->users;
|
||||
|
||||
if (memcmp(oldusers->cksum, newusers->cksum, SHA_DIGEST_LENGTH) == 0) {
|
||||
/* same data, nothing to do */
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [replace_mysql_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 */
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [replace_mysql_users] users' tables replaced, checksum differs",
|
||||
pthread_self())));
|
||||
service->users = newusers;
|
||||
}
|
||||
|
||||
spinlock_release(&service->spin);
|
||||
|
||||
if (i)
|
||||
users_free(oldusers);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the user/passwd form mysql.user table into the service users' hashtable
|
||||
* environment.
|
||||
@ -104,18 +158,22 @@ struct users *newusers, *oldusers;
|
||||
static int
|
||||
getUsers(SERVICE *service, struct users *users)
|
||||
{
|
||||
MYSQL *con = NULL;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result = NULL;
|
||||
int num_fields = 0;
|
||||
char *service_user = NULL;
|
||||
char *service_passwd = NULL;
|
||||
char *dpwd;
|
||||
int total_users = 0;
|
||||
SERVER *server;
|
||||
struct sockaddr_in serv_addr;
|
||||
MYSQL_USER_HOST key;
|
||||
char *users_query;
|
||||
MYSQL *con = NULL;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result = NULL;
|
||||
int num_fields = 0;
|
||||
char *service_user = NULL;
|
||||
char *service_passwd = NULL;
|
||||
char *dpwd;
|
||||
int total_users = 0;
|
||||
SERVER *server;
|
||||
char *users_query;
|
||||
unsigned char hash[SHA_DIGEST_LENGTH]="";
|
||||
char *users_data = NULL;
|
||||
int nusers = 0;
|
||||
int users_data_row_len = MYSQL_USER_MAXLEN + MYSQL_HOST_MAXLEN + MYSQL_PASSWORD_LEN + 1;
|
||||
struct sockaddr_in serv_addr;
|
||||
MYSQL_USER_HOST key;
|
||||
|
||||
/* enable_root for MySQL protocol module means load the root user credentials from backend databases */
|
||||
if(service->enable_root) {
|
||||
@ -183,6 +241,44 @@ getUsers(SERVICE *service, struct users *users)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mysql_query(con, MYSQL_USERS_COUNT)) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Loading users for service %s encountered "
|
||||
"error: %s.",
|
||||
service->name,
|
||||
mysql_error(con))));
|
||||
mysql_close(con);
|
||||
return -1;
|
||||
}
|
||||
result = mysql_store_result(con);
|
||||
|
||||
if (result == NULL) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Loading users for service %s encountered "
|
||||
"error: %s.",
|
||||
service->name,
|
||||
mysql_error(con))));
|
||||
mysql_close(con);
|
||||
return -1;
|
||||
}
|
||||
num_fields = mysql_num_fields(result);
|
||||
row = mysql_fetch_row(result);
|
||||
|
||||
nusers = atoi(row[0]);
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
if (!nusers) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Counting users for service %s returned 0",
|
||||
service->name)));
|
||||
mysql_close(con);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mysql_query(con, users_query)) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
@ -193,6 +289,7 @@ getUsers(SERVICE *service, struct users *users)
|
||||
mysql_close(con);
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = mysql_store_result(con);
|
||||
|
||||
if (result == NULL) {
|
||||
@ -206,15 +303,20 @@ getUsers(SERVICE *service, struct users *users)
|
||||
return -1;
|
||||
}
|
||||
num_fields = mysql_num_fields(result);
|
||||
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
char ret_ip[INET_ADDRSTRLEN + 1]="";
|
||||
const char *rc;
|
||||
|
||||
users_data = (char *)calloc(nusers, users_data_row_len * sizeof(char));
|
||||
|
||||
if(users_data == NULL)
|
||||
return -1;
|
||||
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
/**
|
||||
* Two fields should be returned.
|
||||
* Four fields should be returned.
|
||||
* user and passwd+1 (escaping the first byte that is '*') are
|
||||
* added to hashtable.
|
||||
*/
|
||||
char ret_ip[INET_ADDRSTRLEN + 1]="";
|
||||
const char *rc;
|
||||
|
||||
/* prepare the user@host data struct */
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
@ -249,6 +351,8 @@ getUsers(SERVICE *service, struct users *users)
|
||||
row[1],
|
||||
rc == NULL ? "NULL" : ret_ip)));
|
||||
|
||||
strncat(users_data, row[2], users_data_row_len);
|
||||
|
||||
total_users++;
|
||||
} else {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -271,7 +375,15 @@ getUsers(SERVICE *service, struct users *users)
|
||||
row[0],
|
||||
row[1])));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SHA1((const unsigned char *) users_data, strlen(users_data), hash);
|
||||
|
||||
memcpy(users->cksum, hash, SHA_DIGEST_LENGTH);
|
||||
|
||||
free(users_data);
|
||||
|
||||
mysql_free_result(result);
|
||||
mysql_close(con);
|
||||
mysql_thread_end();
|
||||
|
@ -26,7 +26,8 @@
|
||||
* 18/06/13 Mark Riddoch Initial implementation
|
||||
* 24/06/13 Massimiliano Pinto Added: Loading users from mysql backend in serviceStart
|
||||
* 06/02/14 Massimiliano Pinto Added: serviceEnableRootUser routine
|
||||
* 14/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services)
|
||||
* 25/02/14 Massimiliano Pinto Added: service refresh limit feature
|
||||
* 28/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services)
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -83,6 +84,8 @@ SERVICE *service;
|
||||
service->enable_root = 0;
|
||||
service->routerOptions = NULL;
|
||||
service->databases = NULL;
|
||||
memset(&service->rate_limit, 0, sizeof(SERVICE_REFRESH_RATE));
|
||||
spinlock_init(&service->users_table_spin);
|
||||
spinlock_init(&service->spin);
|
||||
|
||||
spinlock_acquire(&service_spin);
|
||||
@ -698,3 +701,47 @@ void *router_obj;
|
||||
serviceSetUser(service, user, auth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int service_refresh_users(SERVICE *service) {
|
||||
int ret = 1;
|
||||
/* check for another running getUsers request */
|
||||
if (! spinlock_acquire_nowait(&service->users_table_spin)) {
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [service_refresh_users] failed to get get lock for loading new users' table: another thread is loading users",
|
||||
pthread_self())));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* check if refresh rate limit has exceeded */
|
||||
if ( (time(NULL) < (service->rate_limit.last + USERS_REFRESH_TIME)) || (service->rate_limit.nloads > USERS_REFRESH_MAX_PER_TIME)) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [service_refresh_users] refresh rate limit exceeded loading new users' table",
|
||||
pthread_self())));
|
||||
|
||||
spinlock_release(&service->users_table_spin);
|
||||
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);
|
||||
}
|
||||
|
||||
ret = replace_mysql_users(service);
|
||||
|
||||
/* remove lock */
|
||||
spinlock_release(&service->users_table_spin);
|
||||
|
||||
if (ret >= 0)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ USERS *rval;
|
||||
if ((rval = calloc(1, sizeof(USERS))) == NULL)
|
||||
return NULL;
|
||||
|
||||
if ((rval->data = hashtable_alloc(USERS_HASHTABLE_SIZE, user_hash, strcmp)) == NULL)
|
||||
if ((rval->data = hashtable_alloc(USERS_HASHTABLE_DEFAULT_SIZE, user_hash, strcmp)) == NULL)
|
||||
{
|
||||
free(rval);
|
||||
return NULL;
|
||||
|
@ -28,11 +28,22 @@
|
||||
*
|
||||
* Date Who Description
|
||||
* 25/06/13 Mark Riddoch Initial implementation
|
||||
* 07/02/14 Massimiliano Pinto Added MySQL user and host data structure
|
||||
* 25/02/13 Massimiliano Pinto Added users table refresh rate default values
|
||||
* 28/02/14 Massimiliano Pinto Added MySQL user and host data structure
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
/* Refresh rate limits for load users from database */
|
||||
#define USERS_REFRESH_TIME 30 /* Allowed time interval (in seconds) after last update*/
|
||||
#define USERS_REFRESH_MAX_PER_TIME 4 /* Max number of load calls within the time interval */
|
||||
|
||||
/* Max length of fields in the mysql.user table */
|
||||
#define MYSQL_USER_MAXLEN 128
|
||||
#define MYSQL_PASSWORD_LEN 41
|
||||
#define MYSQL_HOST_MAXLEN 60
|
||||
#define MYSQL_DATABASE_MAXLEN 128
|
||||
|
||||
/**
|
||||
* MySQL user and host data structure
|
||||
*/
|
||||
@ -46,4 +57,5 @@ extern int reload_mysql_users(SERVICE *service);
|
||||
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);
|
||||
extern int replace_mysql_users(SERVICE *service);
|
||||
#endif
|
||||
|
@ -37,6 +37,7 @@
|
||||
* prototypes
|
||||
* 23/06/13 Mark Riddoch Added service user and users
|
||||
* 06/02/14 Massimiliano Pinto Added service flag for root user access
|
||||
* 25/02/14 Massimiliano Pinto Added service refresh limit feature
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -79,6 +80,15 @@ typedef struct {
|
||||
char *authdata; /**< The authentication data requied */
|
||||
} SERVICE_USER;
|
||||
|
||||
/**
|
||||
* The service refresh rate hols the counter and last load timet for this service to
|
||||
* load users data from the backend database
|
||||
*/
|
||||
typedef struct {
|
||||
int nloads;
|
||||
time_t last;
|
||||
} SERVICE_REFRESH_RATE;
|
||||
|
||||
/**
|
||||
* Defines a service within the gateway.
|
||||
*
|
||||
@ -87,24 +97,28 @@ typedef struct {
|
||||
* to the service.
|
||||
*/
|
||||
typedef struct service {
|
||||
char *name; /**< The service name */
|
||||
int state; /**< The service state */
|
||||
SERV_PROTOCOL *ports; /**< Linked list of ports and protocols
|
||||
* that this service will listen on.
|
||||
*/
|
||||
char *routerModule; /**< Name of router module to use */
|
||||
char **routerOptions;/**< Router specific option strings */
|
||||
char *name; /**< The service name */
|
||||
int state; /**< The service state */
|
||||
SERV_PROTOCOL *ports; /**< Linked list of ports and protocols
|
||||
* that this service will listen on.
|
||||
*/
|
||||
char *routerModule; /**< Name of router module to use */
|
||||
char **routerOptions; /**< Router specific option strings */
|
||||
struct router_object
|
||||
*router; /**< The router we are using */
|
||||
*router; /**< The router we are using */
|
||||
void *router_instance;
|
||||
/**< The router instance for this service */
|
||||
struct server *databases; /**< The set of servers in the backend */
|
||||
SERVICE_USER credentials; /**< The cedentials of the service user */
|
||||
SPINLOCK spin; /**< The service spinlock */
|
||||
SERVICE_STATS stats; /**< The service statistics */
|
||||
struct users *users; /**< The user data for this service */
|
||||
int enable_root; /**< Allow root user access */
|
||||
struct service *next; /**< The next service in the linked list */
|
||||
/**< The router instance for this service */
|
||||
struct server *databases; /**< The set of servers in the backend */
|
||||
SERVICE_USER credentials; /**< The cedentials of the service user */
|
||||
SPINLOCK spin; /**< The service spinlock */
|
||||
SERVICE_STATS stats; /**< The service statistics */
|
||||
struct users *users; /**< The user data for this service */
|
||||
int enable_root; /**< Allow root user access */
|
||||
SPINLOCK
|
||||
users_table_spin; /**< The spinlock for users data refresh */
|
||||
SERVICE_REFRESH_RATE
|
||||
rate_limit; /**< The refresh rate limit for users table */
|
||||
struct service *next; /**< The next service in the linked list */
|
||||
} SERVICE;
|
||||
|
||||
#define SERVICE_STATE_ALLOC 1 /**< The service has been allocated */
|
||||
@ -128,6 +142,7 @@ extern int serviceSetUser(SERVICE *, char *, char *);
|
||||
extern int serviceGetUser(SERVICE *, char **, char **);
|
||||
extern int serviceEnableRootUser(SERVICE *, int );
|
||||
extern void service_update(SERVICE *, char *, char *, char *);
|
||||
extern int service_refresh_users(SERVICE *);
|
||||
extern void printService(SERVICE *);
|
||||
extern void printAllServices();
|
||||
extern void dprintAllServices(DCB *);
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
#include <hashtable.h>
|
||||
#include <dcb.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
/**
|
||||
* @file users.h The functions to manipulate the table of users maintained
|
||||
@ -31,11 +32,13 @@
|
||||
* 23/06/13 Mark Riddoch Initial implementation
|
||||
* 14/02/14 Massimiliano Pinto Added usersCustomUserFormat, optional username format routine
|
||||
* 21/02/14 Massimiliano Pinto Added USERS_HASHTABLE_SIZE
|
||||
* 26/02/14 Massimiliano Pinto Added checksum to users' table with SHA1
|
||||
* 27/02/14 Massimiliano Pinto Added USERS_HASHTABLE_DEFAULT_SIZE
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#define USERS_HASHTABLE_SIZE 52
|
||||
#define USERS_HASHTABLE_DEFAULT_SIZE 52
|
||||
|
||||
/**
|
||||
* The users table statistics structure
|
||||
@ -55,6 +58,8 @@ typedef struct users {
|
||||
HASHTABLE *data; /**< The hashtable containing the actual data */
|
||||
char *(*usersCustomUserFormat)(void *); /**< Optional username format routine */
|
||||
USERS_STATS stats; /**< The statistics for the users table */
|
||||
unsigned char
|
||||
cksum[SHA_DIGEST_LENGTH]; /**< The users' table ckecksum */
|
||||
} USERS;
|
||||
|
||||
extern USERS *users_alloc(); /**< Allocate a users table */
|
||||
|
@ -29,6 +29,13 @@
|
||||
* Added authentication reply status
|
||||
* 12-07-2013 Massimiliano Pinto Added routines for change_user
|
||||
* 14-02-2014 Massimiliano Pinto setipaddress returns int
|
||||
* 25-02-2014 Massimiliano Pinto Added dcb parameter to gw_find_mysql_user_password_sha1()
|
||||
* and repository to gw_check_mysql_scramble_data()
|
||||
* It's now possible to specify a different users' table than
|
||||
* dcb->service->users default
|
||||
* 26-02-2014 Massimiliano Pinto Removed previouvsly added parameters to gw_check_mysql_scramble_data() and
|
||||
* gw_find_mysql_user_password_sha1()
|
||||
* 28-02-2014 Massimiliano Pinto MYSQL_DATABASE_MAXLEN,MYSQL_USER_MAXLEN moved to dbusers.h
|
||||
*
|
||||
*/
|
||||
|
||||
@ -72,9 +79,6 @@
|
||||
#define MYSQL_SCRAMBLE_LEN GW_MYSQL_SCRAMBLE_SIZE
|
||||
#endif
|
||||
|
||||
#define MYSQL_USER_MAXLEN 128
|
||||
#define MYSQL_DATABASE_MAXLEN 128
|
||||
|
||||
#define GW_NOINTR_CALL(A) do { errno = 0; A; } while (errno == EINTR)
|
||||
// network buffer is 32K
|
||||
#define MAX_BUFFER_SIZE 32768
|
||||
@ -265,7 +269,7 @@ int gw_send_change_user_to_backend(
|
||||
int gw_find_mysql_user_password_sha1(
|
||||
char *username,
|
||||
uint8_t *gateway_password,
|
||||
void *repository);
|
||||
DCB *dcb);
|
||||
int gw_check_mysql_scramble_data(
|
||||
DCB *dcb,
|
||||
uint8_t *token,
|
||||
|
@ -243,7 +243,7 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
switch (receive_rc) {
|
||||
case -1:
|
||||
backend_protocol->state = MYSQL_AUTH_FAILED;
|
||||
|
||||
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : backend server didn't "
|
||||
@ -300,6 +300,9 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
gwbuf_length(dcb->delayq));
|
||||
}
|
||||
|
||||
/* try reload users' table for next connection */
|
||||
service_refresh_users(dcb->session->client->service);
|
||||
|
||||
while (session->state != SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
ss_dassert(
|
||||
@ -882,6 +885,14 @@ static int gw_change_user(DCB *backend, SERVER *server, SESSION *in_session, GWB
|
||||
// Note: if auth_token_len == 0 && auth_token == NULL, user is without password
|
||||
auth_ret = gw_check_mysql_scramble_data(backend->session->client, auth_token, auth_token_len, client_protocol->scramble, sizeof(client_protocol->scramble), username, client_sha1);
|
||||
|
||||
if (auth_ret != 0) {
|
||||
if (!service_refresh_users(backend->session->client->service)) {
|
||||
/* Try authentication again with new repository data */
|
||||
/* Note: if no auth client authentication will fail */
|
||||
auth_ret = gw_check_mysql_scramble_data(backend->session->client, auth_token, auth_token_len, client_protocol->scramble, sizeof(client_protocol->scramble), username, client_sha1);
|
||||
}
|
||||
}
|
||||
|
||||
// let's free the auth_token now
|
||||
if (auth_token)
|
||||
free(auth_token);
|
||||
@ -891,6 +902,7 @@ static int gw_change_user(DCB *backend, SERVER *server, SESSION *in_session, GWB
|
||||
|
||||
// send the error packet
|
||||
mysql_send_auth_error(backend->session->client, 1, 0, "Authorization failed on change_user");
|
||||
rv = 1;
|
||||
|
||||
} else {
|
||||
// get db name
|
||||
|
@ -29,7 +29,10 @@
|
||||
* 24/06/2013 Massimiliano Pinto Added: fetch passwords from service users' hashtable
|
||||
* 02/09/2013 Massimiliano Pinto Added: session refcount
|
||||
* 16/12/2013 Massimiliano Pinto Added: client closed socket detection with recv(..., MSG_PEEK)
|
||||
* 07/02/2014 Massimiliano Pinto Added: client IPv4 in dcb->ipv4 and inet_ntop for string representation
|
||||
* 24/02/2014 Massimiliano Pinto Added: on failed authentication a new users' table is loaded with time and frequency limitations
|
||||
* If current user is authenticated the new users' table will replace the old one
|
||||
* 28/02/2014 Massimiliano Pinto Added: client IPv4 in dcb->ipv4 and inet_ntop for string representation
|
||||
*
|
||||
*/
|
||||
|
||||
#include <skygw_utils.h>
|
||||
@ -445,6 +448,15 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
username,
|
||||
stage1_hash);
|
||||
|
||||
/* On failed auth try to load users' table from backend database */
|
||||
if (auth_ret != 0) {
|
||||
if (!service_refresh_users(dcb->service)) {
|
||||
/* Try authentication again with new repository data */
|
||||
/* Note: if no auth client authentication will fail */
|
||||
auth_ret = gw_check_mysql_scramble_data(dcb, auth_token, auth_token_len, protocol->scramble, sizeof(protocol->scramble), username, stage1_hash);
|
||||
}
|
||||
}
|
||||
|
||||
/* let's free the auth_token now */
|
||||
if (auth_token)
|
||||
free(auth_token);
|
||||
|
@ -257,13 +257,17 @@ int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload) {
|
||||
payload+=2;
|
||||
|
||||
// get scramble len
|
||||
scramble_len = payload[0] -1;
|
||||
ss_dassert(scramble_len > GW_SCRAMBLE_LENGTH_323);
|
||||
ss_dassert(scramble_len <= GW_MYSQL_SCRAMBLE_SIZE);
|
||||
if (payload[0] > 0) {
|
||||
scramble_len = payload[0] -1;
|
||||
ss_dassert(scramble_len > GW_SCRAMBLE_LENGTH_323);
|
||||
ss_dassert(scramble_len <= GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
if ( (scramble_len < GW_SCRAMBLE_LENGTH_323) || scramble_len > GW_MYSQL_SCRAMBLE_SIZE) {
|
||||
/* log this */
|
||||
return -2;
|
||||
if ( (scramble_len < GW_SCRAMBLE_LENGTH_323) || scramble_len > GW_MYSQL_SCRAMBLE_SIZE) {
|
||||
/* log this */
|
||||
return -2;
|
||||
}
|
||||
} else {
|
||||
scramble_len = GW_MYSQL_SCRAMBLE_SIZE;
|
||||
}
|
||||
|
||||
// skip 10 zero bytes
|
||||
@ -403,7 +407,7 @@ int gw_send_authentication_to_backend(
|
||||
uint8_t client_capabilities[4];
|
||||
uint32_t server_capabilities;
|
||||
uint32_t final_capabilities;
|
||||
char dbpass[129]="";
|
||||
char dbpass[MYSQL_USER_MAXLEN + 1]="";
|
||||
GWBUF *buffer;
|
||||
DCB *dcb;
|
||||
|
||||
@ -814,7 +818,7 @@ int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, My
|
||||
uint8_t client_capabilities[4];
|
||||
uint32_t server_capabilities;
|
||||
uint32_t final_capabilities;
|
||||
char dbpass[129]="";
|
||||
char dbpass[MYSQL_USER_MAXLEN + 1]="";
|
||||
GWBUF *buffer;
|
||||
DCB *dcb;
|
||||
|
||||
@ -981,12 +985,12 @@ int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, My
|
||||
* Check authentication token received against stage1_hash and scramble
|
||||
*
|
||||
* @param dcb The current dcb
|
||||
* @param token The token sent by the client in the authentication request
|
||||
* @param token_len The token size in bytes
|
||||
* @param scramble The scramble data sent by the server during handshake
|
||||
* @param scramble_len The scrable size in bytes
|
||||
* @param username The current username in the authentication request
|
||||
* @param stage1_hash The SHA1(candidate_password) decoded by this routine
|
||||
* @param token The token sent by the client in the authentication request
|
||||
* @param token_len The token size in bytes
|
||||
* @param scramble The scramble data sent by the server during handshake
|
||||
* @param scramble_len The scrable size in bytes
|
||||
* @param username The current username in the authentication request
|
||||
* @param stage1_hash The SHA1(candidate_password) decoded by this routine
|
||||
* @return 0 on succesful check or != 0 on failure
|
||||
*
|
||||
*/
|
||||
@ -1007,7 +1011,7 @@ int gw_check_mysql_scramble_data(DCB *dcb, uint8_t *token, unsigned int token_le
|
||||
* please note 'real_password' is unknown!
|
||||
*/
|
||||
|
||||
ret_val = gw_find_mysql_user_password_sha1(username, password, (DCB *) dcb);
|
||||
ret_val = gw_find_mysql_user_password_sha1(username, password, dcb);
|
||||
|
||||
if (ret_val) {
|
||||
return 1;
|
||||
@ -1087,25 +1091,27 @@ int gw_check_mysql_scramble_data(DCB *dcb, uint8_t *token, unsigned int token_le
|
||||
/**
|
||||
* gw_find_mysql_user_password_sha1
|
||||
*
|
||||
* The routine fetches look for an user int he Gateway users' tableg
|
||||
* If found the HEX passwotd, representing sha1(sha1(password)), is converted in binary data and
|
||||
* The routine fetches look for an user int he Gateway users' table
|
||||
* The users' table is dcb->service->users or a different one specified with void *repository
|
||||
*
|
||||
* If found the HEX password, representing sha1(sha1(password)), is converted in binary data and
|
||||
* copied into gateway_password
|
||||
*
|
||||
* @param username The user to look for
|
||||
* @param gateway_password The related SHA1(SHA1(password)), the pointer must be preallocated
|
||||
* @param repository The pointer to users' table data, passed as void *
|
||||
* @param username The user to look for
|
||||
* @param gateway_password The related SHA1(SHA1(password)), the pointer must be preallocated
|
||||
* @param dcb Current DCB
|
||||
* @return 1 if user is not found or 0 if the user exists
|
||||
*
|
||||
*/
|
||||
|
||||
int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, void *repository) {
|
||||
int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, DCB *dcb) {
|
||||
SERVICE *service = NULL;
|
||||
struct sockaddr_in *client;
|
||||
char *user_password = NULL;
|
||||
DCB *dcb = (DCB *)repository;
|
||||
MYSQL_USER_HOST key;
|
||||
|
||||
service = (SERVICE *) ((DCB *)repository)->service;
|
||||
service = (SERVICE *) dcb->service;
|
||||
client = (struct sockaddr_in *) &dcb->ipv4;
|
||||
|
||||
key.user = username;
|
||||
|
@ -61,8 +61,9 @@
|
||||
* 31/07/2013 Massimiliano Pinto Added a check for candidate server, if NULL return
|
||||
* 12/08/2013 Mark Riddoch Log unsupported router options
|
||||
* 04/09/2013 Massimiliano Pinto Added client NULL check in clientReply
|
||||
* 22/10/2013 Massimiliano Pinto errorReply called from backend, for client error replyi
|
||||
* 22/10/2013 Massimiliano Pinto errorReply called from backend, for client error reply
|
||||
* or take different actions such as open a new backend connection
|
||||
* 20/02/2014 Massimiliano Pinto If router_options=slave, route traffic to master if no slaves available
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -283,6 +284,7 @@ ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *)instance;
|
||||
ROUTER_CLIENT_SES *client_rses;
|
||||
BACKEND *candidate = NULL;
|
||||
int i;
|
||||
int master_host = -1;
|
||||
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
@ -336,6 +338,16 @@ int i;
|
||||
inst->bitmask)));
|
||||
}
|
||||
|
||||
/*
|
||||
* If router_options=slave, get the running master
|
||||
* It will be used if there are no running slaves at all
|
||||
*/
|
||||
if (inst->bitvalue == SERVER_SLAVE) {
|
||||
if (master_host < 0 && (SERVER_IS_MASTER(inst->servers[i]->server))) {
|
||||
master_host = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->servers[i] &&
|
||||
SERVER_IS_RUNNING(inst->servers[i]->server) &&
|
||||
(inst->servers[i]->server->status & inst->bitmask) ==
|
||||
@ -368,15 +380,22 @@ int i;
|
||||
}
|
||||
}
|
||||
|
||||
/* no candidate server here, clean and return NULL */
|
||||
/* There is no candidate server here!
|
||||
* With router_option=slave a master_host could be set, so route traffic there.
|
||||
* Otherwise, just clean up and return NULL
|
||||
*/
|
||||
if (!candidate) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Failed to create new routing session. "
|
||||
"Couldn't find eligible candidate server. Freeing "
|
||||
"allocated resources.")));
|
||||
free(client_rses);
|
||||
return NULL;
|
||||
if (master_host >= 0) {
|
||||
candidate = inst->servers[master_host];
|
||||
} else {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Failed to create new routing session. "
|
||||
"Couldn't find eligible candidate server. Freeing "
|
||||
"allocated resources.")));
|
||||
free(client_rses);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -673,9 +692,8 @@ errorReply(
|
||||
int action)
|
||||
{
|
||||
DCB *client = NULL;
|
||||
ROUTER_OBJECT *router = NULL;
|
||||
SESSION *session = backend_dcb->session;
|
||||
client = backend_dcb->session->client;
|
||||
client = session->client;
|
||||
|
||||
ss_dassert(client != NULL);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user