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:
MassimilianoPinto
2014-02-28 12:28:14 +01:00
parent 17b328cb9a
commit 45543eceed
12 changed files with 322 additions and 79 deletions

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);
}