MXS-862: Make packet sequence handling automatic
Made the packet sequence number handling automatic so that it always uses the correct one. All functions now have documentation in them. Cleaned up code and added comments to GSSAPI code.
This commit is contained in:
parent
68e53567a0
commit
571919264f
@ -75,9 +75,15 @@ MXS_BEGIN_DECLS
|
||||
#define MYSQL_HEADER_LEN 4L
|
||||
#define MYSQL_CHECKSUM_LEN 4L
|
||||
|
||||
/** Offsets to various parts of the client packet */
|
||||
#define MYSQL_SEQ_OFFSET 3
|
||||
#define MYSQL_COM_OFFSET 4
|
||||
/**
|
||||
* Offsets and sizes of various parts of the client packet. If the offset is
|
||||
* defined but not the size, the size of the value is one byte.
|
||||
*/
|
||||
#define MYSQL_SEQ_OFFSET 3
|
||||
#define MYSQL_COM_OFFSET 4
|
||||
#define MYSQL_CHARSET_OFFSET 12
|
||||
#define MYSQL_CLIENT_CAP_OFFSET 4
|
||||
#define MYSQL_CLIENT_CAP_SIZE 4
|
||||
|
||||
#define GW_MYSQL_PROTOCOL_VERSION 10 // version is 10
|
||||
#define GW_MYSQL_HANDSHAKE_FILLER 0x00
|
||||
@ -279,8 +285,9 @@ typedef struct
|
||||
} MySQLProtocol;
|
||||
|
||||
/** Defines for response codes */
|
||||
#define MYSQL_REPLY_ERR 0xff
|
||||
#define MYSQL_REPLY_OK 0x00
|
||||
#define MYSQL_REPLY_ERR 0xff
|
||||
#define MYSQL_REPLY_OK 0x00
|
||||
#define MYSQL_REPLY_AUTHSWITCHREQUEST 0xfe
|
||||
|
||||
/*
|
||||
* Let's try this with proper enums instead of numbers
|
||||
|
@ -82,7 +82,7 @@ void* gssapi_auth_init(char **options)
|
||||
* @see https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest
|
||||
* @see https://web.mit.edu/kerberos/krb5-1.5/krb5-1.5.4/doc/krb5-user/What-is-a-Kerberos-Principal_003f.html
|
||||
*/
|
||||
static GWBUF* create_auth_change_packet(GSSAPI_INSTANCE *instance)
|
||||
static GWBUF* create_auth_change_packet(GSSAPI_INSTANCE *instance, gssapi_auth_t *auth)
|
||||
{
|
||||
size_t principal_name_len = strlen(instance->principal_name);
|
||||
size_t plen = sizeof(auth_plugin_name) + 1 + principal_name_len;
|
||||
@ -93,7 +93,7 @@ static GWBUF* create_auth_change_packet(GSSAPI_INSTANCE *instance)
|
||||
uint8_t *data = (uint8_t*)GWBUF_DATA(buffer);
|
||||
gw_mysql_set_byte3(data, plen);
|
||||
data += 3;
|
||||
*data++ = 0x02; // Second packet
|
||||
*data++ = ++auth->sequence; // Second packet
|
||||
*data++ = 0xfe; // AuthSwitchRequest command
|
||||
memcpy(data, auth_plugin_name, sizeof(auth_plugin_name)); // Plugin name
|
||||
data += sizeof(auth_plugin_name);
|
||||
@ -143,10 +143,14 @@ static void copy_client_information(DCB *dcb, GWBUF *buffer)
|
||||
{
|
||||
size_t buflen = gwbuf_length(buffer);
|
||||
MySQLProtocol *protocol = (MySQLProtocol*)dcb->protocol;
|
||||
/* Take data from fixed locations first */
|
||||
gwbuf_copy_data(buffer, 4, 4, (uint8_t*)&protocol->client_capabilities);
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
/* Store the connection characteristics and sequence number of the current packet */
|
||||
protocol->charset = 0;
|
||||
gwbuf_copy_data(buffer, 4 + 4 + 4, 1, (uint8_t*)&protocol->charset);
|
||||
gwbuf_copy_data(buffer, MYSQL_CHARSET_OFFSET, 1, (uint8_t*)&protocol->charset);
|
||||
gwbuf_copy_data(buffer, MYSQL_CLIENT_CAP_OFFSET, MYSQL_CLIENT_CAP_SIZE,
|
||||
(uint8_t*)&protocol->client_capabilities);
|
||||
gwbuf_copy_data(buffer, MYSQL_SEQ_OFFSET, 1, &auth->sequence);
|
||||
|
||||
if (buflen > MYSQL_AUTH_PACKET_BASE_SIZE)
|
||||
{
|
||||
@ -292,7 +296,7 @@ int gssapi_auth_authenticate(DCB *dcb)
|
||||
/** We need to send the authentication switch packet to change the
|
||||
* authentication to something other than the 'mysql_native_password'
|
||||
* method */
|
||||
GWBUF *buffer = create_auth_change_packet(instance);
|
||||
GWBUF *buffer = create_auth_change_packet(instance, auth);
|
||||
|
||||
if (buffer && dcb->func.write(dcb, buffer))
|
||||
{
|
||||
|
@ -39,9 +39,10 @@ enum gssapi_auth_state
|
||||
/** Common state tracking structure */
|
||||
typedef struct gssapi_auth
|
||||
{
|
||||
enum gssapi_auth_state state;
|
||||
uint8_t *principal_name;
|
||||
size_t principal_name_len;
|
||||
enum gssapi_auth_state state; /**< Authentication state*/
|
||||
uint8_t *principal_name; /**< Principal name */
|
||||
size_t principal_name_len; /**< Length of the principal name */
|
||||
uint8_t sequence; /**< The next packet seqence number */
|
||||
} gssapi_auth_t;
|
||||
|
||||
/** These functions can used for the `create` and `destroy` entry points */
|
||||
|
@ -24,6 +24,7 @@ void* gssapi_auth_alloc(void *instance)
|
||||
rval->state = GSSAPI_AUTH_INIT;
|
||||
rval->principal_name = NULL;
|
||||
rval->principal_name_len = 0;
|
||||
rval->sequence = 0;
|
||||
}
|
||||
|
||||
return rval;
|
||||
|
@ -19,11 +19,14 @@
|
||||
#include "gssapi_auth.h"
|
||||
|
||||
/**
|
||||
* @file gssapi_backend_auth.c GSSAPI backend authenticator
|
||||
* @file gssapi_backend_auth.c - GSSAPI backend authenticator
|
||||
*/
|
||||
|
||||
/** TODO: Document functions and GSSAPI specific code */
|
||||
|
||||
/**
|
||||
* @brief Create a new GSSAPI token
|
||||
* @param dcb Backend DCB
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
static bool send_new_auth_token(DCB *dcb)
|
||||
{
|
||||
bool rval = false;
|
||||
@ -35,9 +38,11 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
gss_name_t princ = GSS_C_NO_NAME;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
/** The service principal name is sent by the backend server */
|
||||
target.value = auth->principal_name;
|
||||
target.length = auth->principal_name_len + 1;
|
||||
|
||||
/** Convert the name into GSSAPI format */
|
||||
major = gss_import_name(&minor, &target, GSS_C_NT_USER_NAME, &princ);
|
||||
|
||||
if (GSS_ERROR(major))
|
||||
@ -45,6 +50,7 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
report_error(major, minor);
|
||||
}
|
||||
|
||||
/** Request the token for the service */
|
||||
major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL,
|
||||
&handle, princ, GSS_C_NO_OID, 0, 0,
|
||||
GSS_C_NO_CHANNEL_BINDINGS, &in, NULL, &out, 0, 0);
|
||||
@ -54,6 +60,7 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
}
|
||||
else
|
||||
{
|
||||
/** We successfully requested the token, send it to the backend server */
|
||||
GWBUF *buffer = gwbuf_alloc(MYSQL_HEADER_LEN + out.length);
|
||||
|
||||
if (buffer)
|
||||
@ -61,8 +68,9 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
uint8_t *data = (uint8_t*)GWBUF_DATA(buffer);
|
||||
gw_mysql_set_byte3(data, out.length);
|
||||
data += 3;
|
||||
*data++ = 0x03;
|
||||
*data++ = ++auth->sequence;
|
||||
memcpy(data, out.value, out.length);
|
||||
|
||||
if (dcb_write(dcb, buffer))
|
||||
{
|
||||
rval = true;
|
||||
@ -77,6 +85,7 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
}
|
||||
|
||||
major = gss_release_name(&minor, &princ);
|
||||
|
||||
if (GSS_ERROR(major))
|
||||
{
|
||||
report_error(major, minor);
|
||||
@ -87,15 +96,48 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract the principal name from the AuthSwitchRequest packet
|
||||
*
|
||||
* @param dcb Backend DCB
|
||||
* @param buffer Buffer containing an AuthSwitchRequest packet
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
bool extract_principal_name(DCB *dcb, GWBUF *buffer)
|
||||
{
|
||||
bool rval = false;
|
||||
size_t buflen = gwbuf_length(buffer) - MYSQL_HEADER_LEN;
|
||||
uint8_t databuf[buflen];
|
||||
gwbuf_copy_data(buffer, MYSQL_HEADER_LEN, buflen, databuf);
|
||||
uint8_t *data = databuf;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
while (*data)
|
||||
/** Copy the payload and the current packet sequence number */
|
||||
gwbuf_copy_data(buffer, MYSQL_HEADER_LEN, buflen, databuf);
|
||||
gwbuf_copy_data(buffer, MYSQL_SEQ_OFFSET, 1, &auth->sequence);
|
||||
|
||||
if (databuf[0] != MYSQL_REPLY_AUTHSWITCHREQUEST)
|
||||
{
|
||||
/** Server responded with something we did not expect. If it's an OK packet,
|
||||
* it's possible that the server authenticated us as the anonymous user. This
|
||||
* means that the server is not secure. */
|
||||
MXS_ERROR("Server '%s' returned an unexpected authentication response.%s",
|
||||
dcb->server->unique_name, databuf[0] == MYSQL_REPLY_OK ?
|
||||
" Authentication was complete before it even started, "
|
||||
"anonymous users might not be disabled." : "");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The AuthSwitchRequest packet
|
||||
*
|
||||
* 0xfe - Command byte
|
||||
* string[NUL] - Auth plugin name
|
||||
* string[EOF] - Auth plugin data
|
||||
*
|
||||
* Skip over the auth plugin name and copy the service principal name stored
|
||||
* in the auth plugin data section.
|
||||
*/
|
||||
while (*data && data < databuf + buflen)
|
||||
{
|
||||
data++;
|
||||
}
|
||||
@ -103,35 +145,51 @@ bool extract_principal_name(DCB *dcb, GWBUF *buffer)
|
||||
data++;
|
||||
buflen -= data - databuf;
|
||||
|
||||
uint8_t *principal = MXS_MALLOC(buflen);
|
||||
|
||||
if (principal)
|
||||
if (buflen > 0)
|
||||
{
|
||||
memcpy(principal, data, buflen);
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
auth->principal_name = principal;
|
||||
auth->principal_name_len = buflen;
|
||||
rval = true;
|
||||
uint8_t *principal = MXS_MALLOC(buflen + 1);
|
||||
|
||||
if (principal)
|
||||
{
|
||||
/** Store the principal name for later when we request the token
|
||||
* from the GSSAPI server */
|
||||
memcpy(principal, data, buflen);
|
||||
principal[buflen] = '\0';
|
||||
auth->principal_name = principal;
|
||||
auth->principal_name_len = buflen;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Backend server did not send any auth plugin data.");
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract data from a MySQL packet
|
||||
* @param dcb Backend DCB
|
||||
* @param buffer Buffer containing a complete packet
|
||||
* @return MXS_AUTH_INCOMPLETE if authentication is ongoing, MXS_AUTH_SUCCEEDED
|
||||
* if authentication is complete and MXS_AUTH_FAILED if authentication failed.
|
||||
*/
|
||||
static int gssapi_backend_auth_extract(DCB *dcb, GWBUF *buffer)
|
||||
{
|
||||
int rval = MXS_AUTH_FAILED;
|
||||
gssapi_auth_t *data = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
if (data->state == GSSAPI_AUTH_INIT && extract_principal_name(dcb, buffer))
|
||||
if (auth->state == GSSAPI_AUTH_INIT && extract_principal_name(dcb, buffer))
|
||||
{
|
||||
rval = MXS_AUTH_INCOMPLETE;
|
||||
}
|
||||
else if (data->state == GSSAPI_AUTH_DATA_SENT)
|
||||
else if (auth->state == GSSAPI_AUTH_DATA_SENT)
|
||||
{
|
||||
/** Read authentication response */
|
||||
if (mxs_mysql_is_ok_packet(buffer))
|
||||
{
|
||||
data->state = GSSAPI_AUTH_OK;
|
||||
auth->state = GSSAPI_AUTH_OK;
|
||||
rval = MXS_AUTH_SUCCEEDED;
|
||||
}
|
||||
}
|
||||
@ -139,26 +197,37 @@ static int gssapi_backend_auth_extract(DCB *dcb, GWBUF *buffer)
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether the DCB supports SSL
|
||||
* @param dcb Backend DCB
|
||||
* @return True if DCB supports SSL
|
||||
*/
|
||||
static bool gssapi_backend_auth_connectssl(DCB *dcb)
|
||||
{
|
||||
return dcb->server->server_ssl != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Authenticate the backend connection
|
||||
* @param dcb Backend DCB
|
||||
* @return MXS_AUTH_INCOMPLETE if authentication is ongoing, MXS_AUTH_SUCCEEDED
|
||||
* if authentication is complete and MXS_AUTH_FAILED if authentication failed.
|
||||
*/
|
||||
static int gssapi_backend_auth_authenticate(DCB *dcb)
|
||||
{
|
||||
int rval = MXS_AUTH_FAILED;
|
||||
gssapi_auth_t *auth_data = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
if (auth_data->state == GSSAPI_AUTH_INIT)
|
||||
if (auth->state == GSSAPI_AUTH_INIT)
|
||||
{
|
||||
if (send_new_auth_token(dcb))
|
||||
{
|
||||
rval = MXS_AUTH_INCOMPLETE;
|
||||
auth_data->state = GSSAPI_AUTH_DATA_SENT;
|
||||
auth->state = GSSAPI_AUTH_DATA_SENT;
|
||||
}
|
||||
|
||||
}
|
||||
else if (auth_data->state == GSSAPI_AUTH_OK)
|
||||
else if (auth->state == GSSAPI_AUTH_OK)
|
||||
{
|
||||
rval = MXS_AUTH_SUCCEEDED;
|
||||
}
|
||||
@ -171,7 +240,7 @@ static int gssapi_backend_auth_authenticate(DCB *dcb)
|
||||
*/
|
||||
static GWAUTHENTICATOR MyObject =
|
||||
{
|
||||
NULL, /* TODO: implement initialize entry point */
|
||||
NULL, /* No initialize entry point */
|
||||
gssapi_auth_alloc, /* Allocate authenticator data */
|
||||
gssapi_backend_auth_extract, /* Extract data into structure */
|
||||
gssapi_backend_auth_connectssl, /* Check if client supports SSL */
|
||||
|
@ -479,6 +479,9 @@ int gw_read_client_event(DCB* dcb)
|
||||
static int
|
||||
gw_read_do_authentication(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
|
||||
{
|
||||
ss_debug(uint8_t hdr[MYSQL_HEADER_LEN]);
|
||||
ss_dassert(gwbuf_copy_data(read_buffer, 0, MYSQL_HEADER_LEN, hdr) == MYSQL_HEADER_LEN &&
|
||||
MYSQL_GET_PACKET_LEN(hdr) + MYSQL_HEADER_LEN == gwbuf_length(read_buffer));
|
||||
/** Allocate the shared session structure */
|
||||
if (dcb->data == NULL && (dcb->data = mysql_session_alloc()) == NULL)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user