MXS-862: Refactor backend authentication handling
The backend responses are now read in one place and the functions just read the data. The protocol level will now handle the packet gathering process and the authentication part just inspects the data. Backend connections now load authenticators when they are being connected. In the future, this enables the use of authentication modules for backend connection.
This commit is contained in:
@ -79,7 +79,7 @@
|
||||
#define GW_MYSQL_SCRAMBLE_SIZE 20
|
||||
#define GW_SCRAMBLE_LENGTH_323 8
|
||||
|
||||
#define DEFAULT_AUTH_PLUGIN_NAME "mysql_native_password"
|
||||
#define DEFAULT_MYSQL_AUTH_PLUGIN "mysql_native_password"
|
||||
|
||||
/** Maximum length of a MySQL packet */
|
||||
#define MYSQL_PACKET_LENGTH_MAX 0x00ffffff
|
||||
@ -97,28 +97,6 @@
|
||||
#define COM_QUIT_PACKET_SIZE (4+1)
|
||||
struct dcb;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MYSQL_ALLOC, /* Initial state of protocol auth state */
|
||||
/* The following are used only for backend connections */
|
||||
MYSQL_PENDING_CONNECT,
|
||||
MYSQL_CONNECTED,
|
||||
/* The following can be used for either client or backend */
|
||||
/* The comments have only been checked for client use at present */
|
||||
MYSQL_AUTH_SENT,
|
||||
MYSQL_AUTH_RECV, /* This is only ever a transient value */
|
||||
MYSQL_AUTH_FAILED, /* Once this is set, the connection */
|
||||
/* will be ended, so this is transient */
|
||||
/* The following is used only for backend connections */
|
||||
MYSQL_HANDSHAKE_FAILED,
|
||||
/* The following are obsolete and will be removed */
|
||||
MYSQL_AUTH_SSL_REQ, /*< client requested SSL but SSL_accept hasn't beed called */
|
||||
MYSQL_AUTH_SSL_HANDSHAKE_DONE, /*< SSL handshake has been fully completed */
|
||||
MYSQL_AUTH_SSL_HANDSHAKE_FAILED, /*< SSL handshake failed for any reason */
|
||||
MYSQL_AUTH_SSL_HANDSHAKE_ONGOING, /*< SSL_accept has been called but the
|
||||
* SSL handshake hasn't been completed */
|
||||
MYSQL_IDLE
|
||||
} mysql_auth_state_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@ -272,7 +250,7 @@ typedef struct
|
||||
mysql_server_cmd_t current_command; /**< Current command being executed */
|
||||
server_command_t protocol_command; /*< session command list */
|
||||
server_command_t* protocol_cmd_history; /*< session command history */
|
||||
mysql_auth_state_t protocol_auth_state; /*< Authentication status */
|
||||
mxs_auth_state_t protocol_auth_state; /*< Authentication status */
|
||||
mysql_protocol_state_t protocol_state; /*< Protocol struct status */
|
||||
uint8_t scramble[MYSQL_SCRAMBLE_LEN]; /*< server scramble,
|
||||
* created or received */
|
||||
@ -288,6 +266,9 @@ typedef struct
|
||||
#endif
|
||||
} MySQLProtocol;
|
||||
|
||||
/** Defines for response codes */
|
||||
#define MYSQL_REPLY_ERR 0xff
|
||||
#define MYSQL_REPLY_OK 0x00
|
||||
|
||||
/*
|
||||
* Let's try this with proper enums instead of numbers
|
||||
@ -307,7 +288,7 @@ typedef struct
|
||||
#define MYSQL_GET_ERRCODE(payload) (gw_mysql_get_byte2(&payload[5]))
|
||||
#define MYSQL_GET_STMTOK_NPARAM(payload) (gw_mysql_get_byte2(&payload[9]))
|
||||
#define MYSQL_GET_STMTOK_NATTR(payload) (gw_mysql_get_byte2(&payload[11]))
|
||||
#define MYSQL_IS_ERROR_PACKET(payload) ((int)MYSQL_GET_COMMAND(payload)==0xff)
|
||||
#define MYSQL_IS_ERROR_PACKET(payload) ((int)MYSQL_GET_COMMAND(payload)==MYSQL_REPLY_ERR)
|
||||
#define MYSQL_IS_COM_QUIT(payload) (MYSQL_GET_COMMAND(payload)==MYSQL_COM_QUIT)
|
||||
#define MYSQL_IS_COM_INIT_DB(payload) (MYSQL_GET_COMMAND(payload)==MYSQL_COM_INIT_DB)
|
||||
#define MYSQL_IS_CHANGE_USER(payload) (MYSQL_GET_COMMAND(payload)==MYSQL_COM_CHANGE_USER)
|
||||
@ -383,5 +364,6 @@ void init_response_status (
|
||||
mysql_server_cmd_t cmd,
|
||||
int* npackets,
|
||||
ssize_t* nbytes);
|
||||
bool read_complete_packet(DCB *dcb, GWBUF **readbuf);
|
||||
|
||||
#endif /** _MYSQL_PROTOCOL_H */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -312,7 +312,7 @@ int MySQLSendHandshake(DCB* dcb)
|
||||
memcpy(mysql_plugin_data, server_scramble + 8, 12);
|
||||
|
||||
const char* plugin_name = dcb->authfunc.plugin_name ?
|
||||
dcb->authfunc.plugin_name : DEFAULT_AUTH_PLUGIN_NAME;
|
||||
dcb->authfunc.plugin_name : DEFAULT_MYSQL_AUTH_PLUGIN;
|
||||
int plugin_name_len = strlen(plugin_name);
|
||||
|
||||
mysql_payload_size =
|
||||
@ -512,7 +512,7 @@ int gw_read_client_event(DCB* dcb)
|
||||
* will be changed to MYSQL_IDLE (see below).
|
||||
*
|
||||
*/
|
||||
case MYSQL_AUTH_SENT:
|
||||
case MXS_AUTH_STATE_MESSAGE_READ:
|
||||
/* After this call read_buffer will point to freed data */
|
||||
if (nbytes_read < 3 || (0 == max_bytes && nbytes_read <
|
||||
(MYSQL_GET_PACKET_LEN((uint8_t *) GWBUF_DATA(read_buffer)) + 4)) ||
|
||||
@ -533,12 +533,12 @@ int gw_read_client_event(DCB* dcb)
|
||||
* result in a call that comes to this section of code.
|
||||
*
|
||||
*/
|
||||
case MYSQL_IDLE:
|
||||
case MXS_AUTH_STATE_COMPLETE:
|
||||
/* After this call read_buffer will point to freed data */
|
||||
return_code = gw_read_normal_data(dcb, read_buffer, nbytes_read);
|
||||
break;
|
||||
|
||||
case MYSQL_AUTH_FAILED:
|
||||
case MXS_AUTH_STATE_FAILED:
|
||||
gwbuf_free(read_buffer);
|
||||
return_code = 1;
|
||||
break;
|
||||
@ -601,7 +601,7 @@ gw_read_do_authentication(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
|
||||
{
|
||||
SESSION *session;
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_RECV;
|
||||
protocol->protocol_auth_state = MXS_AUTH_STATE_RESPONSE_SENT;
|
||||
/**
|
||||
* Create session, and a router session for it.
|
||||
* If successful, there will be backend connection(s)
|
||||
@ -619,7 +619,7 @@ gw_read_do_authentication(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
|
||||
ss_dassert(session->state != SESSION_STATE_ALLOC &&
|
||||
session->state != SESSION_STATE_DUMMY);
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_IDLE;
|
||||
protocol->protocol_auth_state = MXS_AUTH_STATE_COMPLETE;
|
||||
/**
|
||||
* Send an AUTH_OK packet to the client,
|
||||
* packet sequence is # packet_number
|
||||
@ -640,7 +640,7 @@ gw_read_do_authentication(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
|
||||
MXS_AUTH_INCOMPLETE != auth_val &&
|
||||
MXS_AUTH_SSL_INCOMPLETE != auth_val)
|
||||
{
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
|
||||
protocol->protocol_auth_state = MXS_AUTH_STATE_FAILED;
|
||||
mysql_client_auth_error_handling(dcb, auth_val);
|
||||
/**
|
||||
* Close DCB and which will release MYSQL_session
|
||||
@ -1106,7 +1106,7 @@ int gw_write_client_event(DCB *dcb)
|
||||
protocol = (MySQLProtocol *)dcb->protocol;
|
||||
CHK_PROTOCOL(protocol);
|
||||
|
||||
if (protocol->protocol_auth_state == MYSQL_IDLE)
|
||||
if (protocol->protocol_auth_state == MXS_AUTH_STATE_COMPLETE)
|
||||
{
|
||||
dcb_drain_writeq(dcb);
|
||||
goto return_1;
|
||||
@ -1207,7 +1207,7 @@ static void gw_process_one_new_client(DCB *client_dcb)
|
||||
MySQLSendHandshake(client_dcb);
|
||||
|
||||
// client protocol state change
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_SENT;
|
||||
protocol->protocol_auth_state = MXS_AUTH_STATE_MESSAGE_READ;
|
||||
|
||||
/**
|
||||
* Set new descriptor to event set. At the same time,
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <modutil.h>
|
||||
|
||||
static server_command_t* server_command_init(server_command_t* srvcmd, mysql_server_cmd_t cmd);
|
||||
|
||||
@ -78,7 +79,7 @@ MySQLProtocol* mysql_protocol_init(DCB* dcb, int fd)
|
||||
goto return_p;
|
||||
}
|
||||
p->protocol_state = MYSQL_PROTOCOL_ALLOC;
|
||||
p->protocol_auth_state = MYSQL_ALLOC;
|
||||
p->protocol_auth_state = MXS_AUTH_STATE_INIT;
|
||||
p->current_command = MYSQL_COM_UNDEFINED;
|
||||
p->protocol_command.scom_cmd = MYSQL_COM_UNDEFINED;
|
||||
p->protocol_command.scom_nresponse_packets = 0;
|
||||
@ -143,28 +144,20 @@ const char* gw_mysql_protocol_state2string (int state)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case MYSQL_ALLOC:
|
||||
return "MySQL Protocl struct allocated";
|
||||
case MYSQL_PENDING_CONNECT:
|
||||
return "MySQL Backend socket PENDING connect";
|
||||
case MYSQL_CONNECTED:
|
||||
return "MySQL Backend socket CONNECTED";
|
||||
case MYSQL_AUTH_SENT:
|
||||
return "MySQL Authentication handshake has been sent";
|
||||
case MYSQL_AUTH_RECV:
|
||||
return "MySQL Received user, password, db and capabilities";
|
||||
case MYSQL_AUTH_FAILED:
|
||||
return "MySQL Authentication failed";
|
||||
case MYSQL_IDLE:
|
||||
return "MySQL authentication is succesfully done.";
|
||||
case MYSQL_AUTH_SSL_REQ:
|
||||
return "MYSQL_AUTH_SSL_REQ";
|
||||
case MYSQL_AUTH_SSL_HANDSHAKE_DONE:
|
||||
return "MYSQL_AUTH_SSL_HANDSHAKE_DONE";
|
||||
case MYSQL_AUTH_SSL_HANDSHAKE_FAILED:
|
||||
return "MYSQL_AUTH_SSL_HANDSHAKE_FAILED";
|
||||
case MYSQL_AUTH_SSL_HANDSHAKE_ONGOING:
|
||||
return "MYSQL_AUTH_SSL_HANDSHAKE_ONGOING";
|
||||
case MXS_AUTH_STATE_INIT:
|
||||
return "Authentication initialized";
|
||||
case MXS_AUTH_STATE_PENDING_CONNECT:
|
||||
return "Network connection pending";
|
||||
case MXS_AUTH_STATE_CONNECTED:
|
||||
return "Network connection created";
|
||||
case MXS_AUTH_STATE_MESSAGE_READ:
|
||||
return "Read server handshake";
|
||||
case MXS_AUTH_STATE_RESPONSE_SENT:
|
||||
return "Response to handshake sent";
|
||||
case MXS_AUTH_STATE_FAILED:
|
||||
return "Authentication failed";
|
||||
case MXS_AUTH_STATE_COMPLETE:
|
||||
return "Authentication is complete.";
|
||||
default:
|
||||
return "MySQL (unknown protocol state)";
|
||||
}
|
||||
@ -992,3 +985,43 @@ char *create_auth_fail_str(char *username,
|
||||
retblock:
|
||||
return errstr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a complete packet from a DCB
|
||||
*
|
||||
* Read a complete packet from a connected DCB. If data was read, @c readbuf
|
||||
* will point to the head of the read data. If no data was read, @c readbuf will
|
||||
* be set to NULL.
|
||||
*
|
||||
* @param dcb DCB to read from
|
||||
* @param readbuf Pointer to a buffer where the data is stored
|
||||
* @return True on success, false if an error occurred while data was being read
|
||||
*/
|
||||
bool read_complete_packet(DCB *dcb, GWBUF **readbuf)
|
||||
{
|
||||
bool rval = false;
|
||||
GWBUF *localbuf = NULL;
|
||||
|
||||
if (dcb_read(dcb, &localbuf, 0) >= 0)
|
||||
{
|
||||
rval = true;
|
||||
dcb->last_read = hkheartbeat;
|
||||
GWBUF *packets = modutil_get_complete_packets(&localbuf);
|
||||
|
||||
if (packets)
|
||||
{
|
||||
/** A complete packet was read */
|
||||
*readbuf = packets;
|
||||
}
|
||||
|
||||
if (localbuf)
|
||||
{
|
||||
/** Store any extra data in the DCB's readqueue */
|
||||
spinlock_acquire(&dcb->authlock);
|
||||
dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, localbuf);
|
||||
spinlock_release(&dcb->authlock);
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
Reference in New Issue
Block a user