MXS-862: Add create/destroy and remove plugin_name entry points
The create and destroy entry points allow authenticators to store data in the DCB. This data is not shared by other DCBs related to the same session. The plugin_name entry point wasn't really useful as the plugins would still need to send a AuthSwitchRequest packet if they wanted to change the authentication mechanism.
This commit is contained in:
parent
829d5a7453
commit
dfeb5c46c9
@ -397,10 +397,10 @@ dcb_free_all_memory(DCB *dcb)
|
||||
dcb->authfunc.free(dcb);
|
||||
dcb->data = NULL;
|
||||
}
|
||||
if (dcb->backend_data && dcb->authfunc.free && dcb->dcb_role == DCB_ROLE_BACKEND_HANDLER)
|
||||
if (dcb->authfunc.destroy)
|
||||
{
|
||||
dcb->authfunc.free(dcb);
|
||||
dcb->backend_data = NULL;
|
||||
dcb->authfunc.destroy(dcb->authenticator_data);
|
||||
dcb->authenticator_data = NULL;
|
||||
}
|
||||
if (dcb->protoname)
|
||||
{
|
||||
@ -814,6 +814,12 @@ dcb_connect(SERVER *server, SESSION *session, const char *protocol)
|
||||
|
||||
memcpy(&dcb->authfunc, authfuncs, sizeof(GWAUTHENTICATOR));
|
||||
|
||||
/** Allocate DCB specific authentication data */
|
||||
if (dcb->authfunc.create)
|
||||
{
|
||||
dcb->authenticator_data = dcb->authfunc.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Link dcb to session. Unlink is called in dcb_final_free
|
||||
*/
|
||||
@ -3172,6 +3178,13 @@ dcb_accept(DCB *listener, GWPROTOCOL *protocol_funcs)
|
||||
}
|
||||
}
|
||||
memcpy(&(client_dcb->authfunc), authfuncs, sizeof(GWAUTHENTICATOR));
|
||||
|
||||
/** Allocate DCB specific authentication data */
|
||||
if (client_dcb->authfunc.create)
|
||||
{
|
||||
client_dcb->authenticator_data = client_dcb->authfunc.create();
|
||||
}
|
||||
|
||||
if (client_dcb->service->max_connections &&
|
||||
client_dcb->service->client_count >= client_dcb->service->max_connections)
|
||||
{
|
||||
|
@ -288,6 +288,11 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
|
||||
|
||||
memcpy(&port->listener->authfunc, authfuncs, sizeof(GWAUTHENTICATOR));
|
||||
|
||||
/**
|
||||
* Normally, we'd allocate the DCB specific authentication data. As the
|
||||
* listeners aren't normal DCBs, we can skip that.
|
||||
*/
|
||||
|
||||
if (port->address)
|
||||
{
|
||||
sprintf(config_bind, "%s:%d", port->address, port->port);
|
||||
@ -1457,7 +1462,8 @@ int service_refresh_users(SERVICE *service)
|
||||
|
||||
for (SERV_LISTENER *port = service->ports; port; port = port->next)
|
||||
{
|
||||
if (port->listener->authfunc.loadusers(port) != MXS_AUTH_LOADUSERS_OK)
|
||||
if (port->listener->authfunc.loadusers &&
|
||||
port->listener->authfunc.loadusers(port) != MXS_AUTH_LOADUSERS_OK)
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to load users for listener '%s', authentication might not work.",
|
||||
service->name, port->name);
|
||||
|
@ -252,8 +252,8 @@ typedef struct dcb
|
||||
struct dcb *nextpersistent; /**< Next DCB in the persistent pool for SERVER */
|
||||
time_t persistentstart; /**< Time when DCB placed in persistent pool */
|
||||
struct service *service; /**< The related service */
|
||||
void *data; /**< Specific client data */
|
||||
void *backend_data; /**< Specific backend data */
|
||||
void *data; /**< Specific client data, shared between DCBs of this session */
|
||||
void *authenticator_data; /**< The authenticator data for this DCB */
|
||||
DCBMM memdata; /**< The data related to DCB memory management */
|
||||
SPINLOCK cb_lock; /**< The lock for the callbacks linked list */
|
||||
DCB_CALLBACK *callbacks; /**< The list of callbacks for the DCB */
|
||||
@ -284,7 +284,7 @@ typedef struct dcb
|
||||
.cb_lock = SPINLOCK_INIT, .pollinlock = SPINLOCK_INIT, \
|
||||
.fd = DCBFD_CLOSED, .stats = DCBSTATS_INIT, .ssl_state = SSL_HANDSHAKE_UNKNOWN, \
|
||||
.state = DCB_STATE_ALLOC, .polloutlock = SPINLOCK_INIT, .dcb_chk_tail = CHK_NUM_DCB, \
|
||||
.backend_data = NULL}
|
||||
.authenticator_data = NULL}
|
||||
|
||||
/**
|
||||
* The DCB usage filer used for returning DCB's in use for a certain reason
|
||||
|
@ -42,12 +42,14 @@ struct servlistener;
|
||||
* @verbatim
|
||||
* The operations that can be performed on the descriptor
|
||||
*
|
||||
* create Create a data structure unique to this DCB, stored in dcb->authenticator_data
|
||||
* extract Extract the data from a buffer and place in a structure
|
||||
* shared at the session level, stored in dcb->data
|
||||
* connectssl Determine whether the connection can support SSL
|
||||
* authenticate Carry out the authentication
|
||||
* free Free extracted data
|
||||
* destroy Destroy the unique DCB data
|
||||
* loadusers Load or update authenticator user data
|
||||
* plugin_name The protocol specific name of the authentication plugin.
|
||||
* @endverbatim
|
||||
*
|
||||
* This forms the "module object" for authenticator modules within the gateway.
|
||||
@ -56,12 +58,13 @@ struct servlistener;
|
||||
*/
|
||||
typedef struct gw_authenticator
|
||||
{
|
||||
void *(*create)();
|
||||
int (*extract)(struct dcb *, GWBUF *);
|
||||
bool (*connectssl)(struct dcb *);
|
||||
int (*authenticate)(struct dcb *);
|
||||
void (*free)(struct dcb *);
|
||||
void (*destroy)(void *);
|
||||
int (*loadusers)(struct servlistener *);
|
||||
const char* plugin_name;
|
||||
} GWAUTHENTICATOR;
|
||||
|
||||
/** Return values for extract and authenticate entry points */
|
||||
|
@ -67,12 +67,13 @@ extern char *decryptPassword(char *crypt);
|
||||
*/
|
||||
static GWAUTHENTICATOR MyObject =
|
||||
{
|
||||
cdc_auth_set_protocol_data, /* Extract data into structure */
|
||||
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,
|
||||
NULL
|
||||
NULL, /* No create entry point */
|
||||
cdc_auth_set_protocol_data, /* Extract data into structure */
|
||||
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 */
|
||||
NULL, /* No destroy entry point */
|
||||
cdc_replace_users /* Load CDC users */
|
||||
};
|
||||
|
||||
static int cdc_auth_check(
|
||||
|
@ -59,12 +59,13 @@ static void http_auth_free_client_data(DCB *dcb);
|
||||
*/
|
||||
static GWAUTHENTICATOR MyObject =
|
||||
{
|
||||
http_auth_set_protocol_data, /* Extract data into structure */
|
||||
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,
|
||||
NULL
|
||||
NULL, /* No create entry point */
|
||||
http_auth_set_protocol_data, /* Extract data into structure */
|
||||
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 */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers /* Load generic users */
|
||||
};
|
||||
|
||||
typedef struct http_auth
|
||||
|
@ -59,12 +59,13 @@ static void max_admin_auth_free_client_data(DCB *dcb);
|
||||
*/
|
||||
static GWAUTHENTICATOR MyObject =
|
||||
{
|
||||
max_admin_auth_set_protocol_data, /* Extract data into structure */
|
||||
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,
|
||||
NULL
|
||||
NULL, /* No create entry point */
|
||||
max_admin_auth_set_protocol_data, /* Extract data into structure */
|
||||
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 */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers /* Load generic users */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -62,12 +62,13 @@ static int mysql_auth_load_users(SERV_LISTENER *port);
|
||||
*/
|
||||
static GWAUTHENTICATOR MyObject =
|
||||
{
|
||||
mysql_auth_set_protocol_data, /* Extract data into structure */
|
||||
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 */
|
||||
"mysql_native_password"
|
||||
NULL, /* No create entry point */
|
||||
mysql_auth_set_protocol_data, /* Extract data into structure */
|
||||
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 */
|
||||
NULL, /* No destroy entry point */
|
||||
mysql_auth_load_users /* Load users from backend databases */
|
||||
};
|
||||
|
||||
static int combined_auth_check(
|
||||
@ -243,22 +244,8 @@ mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
CHK_PROTOCOL(protocol);
|
||||
if (dcb->data == NULL)
|
||||
{
|
||||
if (NULL == (client_data = (MYSQL_session *)MXS_CALLOC(1, sizeof(MYSQL_session))))
|
||||
{
|
||||
return MXS_AUTH_FAILED;
|
||||
}
|
||||
#if defined(SS_DEBUG)
|
||||
client_data->myses_chk_top = CHK_NUM_MYSQLSES;
|
||||
client_data->myses_chk_tail = CHK_NUM_MYSQLSES;
|
||||
#endif
|
||||
dcb->data = client_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
client_data = (MYSQL_session *)dcb->data;
|
||||
}
|
||||
|
||||
client_data = (MYSQL_session *)dcb->data;
|
||||
|
||||
client_auth_packet_size = gwbuf_length(buf);
|
||||
|
||||
@ -311,7 +298,7 @@ mysql_auth_set_client_data(
|
||||
gwbuf_copy_data(buffer, 0, client_auth_packet_size, client_auth_packet);
|
||||
|
||||
/* The numbers are the fixed elements in the client handshake packet */
|
||||
int auth_packet_base_size = 4 + 4 + 4 + 1 + 23;
|
||||
int auth_packet_base_size = MYSQL_AUTH_PACKET_BASE_SIZE;
|
||||
int packet_length_used = 0;
|
||||
|
||||
/* Take data from fixed locations first */
|
||||
|
@ -50,7 +50,7 @@ typedef struct mysql_backend_auth
|
||||
* @brief Allocate a new mysql_backend_auth object
|
||||
* @return Allocated object or NULL if memory allocation failed
|
||||
*/
|
||||
mysql_backend_auth_t* mba_alloc()
|
||||
void* auth_backend_create()
|
||||
{
|
||||
mysql_backend_auth_t* mba = MXS_MALLOC(sizeof(*mba));
|
||||
|
||||
@ -62,6 +62,18 @@ mysql_backend_auth_t* mba_alloc()
|
||||
return mba;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free allocated mysql_backend_auth object
|
||||
* @param data Allocated mysql_backend_auth object
|
||||
*/
|
||||
void auth_backend_destroy(void *data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
MXS_FREE(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the MySQL authentication packet from backend, packet # is 2
|
||||
*
|
||||
@ -95,9 +107,9 @@ auth_backend_extract(DCB *dcb, GWBUF *buf)
|
||||
{
|
||||
int rval = MXS_AUTH_FAILED;
|
||||
|
||||
if (dcb->backend_data || (dcb->backend_data = mba_alloc()))
|
||||
if (dcb->authenticator_data)
|
||||
{
|
||||
mysql_backend_auth_t *mba = (mysql_backend_auth_t*)dcb->backend_data;
|
||||
mysql_backend_auth_t *mba = (mysql_backend_auth_t*)dcb->authenticator_data;
|
||||
|
||||
switch (mba->state)
|
||||
{
|
||||
@ -146,7 +158,7 @@ static int
|
||||
auth_backend_authenticate(DCB *dcb)
|
||||
{
|
||||
int rval = MXS_AUTH_FAILED;
|
||||
mysql_backend_auth_t *mba = (mysql_backend_auth_t*)dcb->backend_data;
|
||||
mysql_backend_auth_t *mba = (mysql_backend_auth_t*)dcb->authenticator_data;
|
||||
|
||||
if (mba->state == MBA_SEND_RESPONSE)
|
||||
{
|
||||
@ -192,16 +204,6 @@ auth_backend_ssl(DCB *dcb)
|
||||
return dcb->server->server_ssl != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dummy function for the free entry point
|
||||
*/
|
||||
static void
|
||||
auth_backend_free(DCB *dcb)
|
||||
{
|
||||
MXS_FREE(dcb->backend_data);
|
||||
dcb->backend_data = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dummy function for the loadusers entry point
|
||||
*/
|
||||
@ -230,12 +232,13 @@ static char *version_str = "V1.0.0";
|
||||
*/
|
||||
static GWAUTHENTICATOR MyObject =
|
||||
{
|
||||
auth_backend_extract, /* Extract data into structure */
|
||||
auth_backend_ssl, /* Check if client supports SSL */
|
||||
auth_backend_authenticate, /* Authenticate user credentials */
|
||||
auth_backend_free, /* Free the client data held in DCB */
|
||||
auth_backend_load_users, /* Load users from backend databases */
|
||||
DEFAULT_MYSQL_AUTH_PLUGIN
|
||||
auth_backend_create, /* Create authenticator */
|
||||
auth_backend_extract, /* Extract data into structure */
|
||||
auth_backend_ssl, /* Check if client supports SSL */
|
||||
auth_backend_authenticate, /* Authenticate user credentials */
|
||||
NULL, /* The shared data is freed by the client DCB */
|
||||
auth_backend_destroy, /* Destroy authenticator */
|
||||
NULL /* We don't need to load users */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -58,12 +58,13 @@ static void null_auth_free_client_data(DCB *dcb);
|
||||
*/
|
||||
static GWAUTHENTICATOR MyObject =
|
||||
{
|
||||
null_auth_set_protocol_data, /* Extract data into structure */
|
||||
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,
|
||||
NULL
|
||||
NULL, /* No create entry point */
|
||||
null_auth_set_protocol_data, /* Extract data into structure */
|
||||
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 */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers /* Load generic users */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -58,12 +58,13 @@ static void null_auth_free_client_data(DCB *dcb);
|
||||
*/
|
||||
static GWAUTHENTICATOR MyObject =
|
||||
{
|
||||
null_auth_set_protocol_data, /* Extract data into structure */
|
||||
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,
|
||||
NULL
|
||||
NULL, /* No create entry point */
|
||||
null_auth_set_protocol_data, /* Extract data into structure */
|
||||
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 */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers /* Load generic users */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -81,6 +81,9 @@
|
||||
|
||||
#define DEFAULT_MYSQL_AUTH_PLUGIN "mysql_native_password"
|
||||
|
||||
/** All authentication responses are at least this many bytes long */
|
||||
#define MYSQL_AUTH_PACKET_BASE_SIZE 36
|
||||
|
||||
/** Maximum length of a MySQL packet */
|
||||
#define MYSQL_PACKET_LENGTH_MAX 0x00ffffff
|
||||
|
||||
@ -297,6 +300,8 @@ typedef struct
|
||||
/* The following can be compared using memcmp to detect a null password */
|
||||
extern uint8_t null_client_sha1[MYSQL_SCRAMBLE_LEN];
|
||||
|
||||
MYSQL_session* mysql_session_alloc();
|
||||
|
||||
MySQLProtocol* mysql_protocol_init(DCB* dcb, int fd);
|
||||
void mysql_protocol_done (DCB* dcb);
|
||||
const char *gw_mysql_protocol_state2string(int state);
|
||||
|
@ -848,8 +848,12 @@ mxs_auth_state_t gw_send_backend_auth(DCB *dcb)
|
||||
uint32_t capabilities = create_capabilities(conn, (local_session.db && strlen(local_session.db)), false);
|
||||
gw_mysql_set_byte4(client_capabilities, capabilities);
|
||||
|
||||
const char* auth_plugin_name = dcb->authfunc.plugin_name ?
|
||||
dcb->authfunc.plugin_name : DEFAULT_MYSQL_AUTH_PLUGIN;
|
||||
/**
|
||||
* Use the default authentication plugin name. If the server is using a
|
||||
* different authentication mechanism, it will send an AuthSwitchRequest
|
||||
* packet.
|
||||
*/
|
||||
const char* auth_plugin_name = DEFAULT_MYSQL_AUTH_PLUGIN;
|
||||
|
||||
long bytes = response_length(conn, local_session.user, local_session.client_sha1,
|
||||
local_session.db, auth_plugin_name);
|
||||
@ -909,7 +913,7 @@ mxs_auth_state_t gw_send_backend_auth(DCB *dcb)
|
||||
}
|
||||
|
||||
// if the db is not NULL append it
|
||||
if (local_session.db && strlen(local_session.db))
|
||||
if (local_session.db[0])
|
||||
{
|
||||
memcpy(payload, local_session.db, strlen(local_session.db));
|
||||
payload += strlen(local_session.db);
|
||||
|
@ -311,8 +311,12 @@ 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_MYSQL_AUTH_PLUGIN;
|
||||
/**
|
||||
* Use the default authentication plugin name in the initial handshake. If the
|
||||
* authenticator needs to change the authentication method, it should send
|
||||
* an AuthSwitchRequest packet to the client.
|
||||
*/
|
||||
const char* plugin_name = DEFAULT_MYSQL_AUTH_PLUGIN;
|
||||
int plugin_name_len = strlen(plugin_name);
|
||||
|
||||
mysql_payload_size =
|
||||
@ -562,6 +566,13 @@ int gw_read_client_event(DCB* dcb)
|
||||
static int
|
||||
gw_read_do_authentication(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
|
||||
{
|
||||
/** Allocate the shared session structure */
|
||||
if (dcb->data == NULL && (dcb->data = mysql_session_alloc()) == NULL)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The first step in the authentication process is to extract the
|
||||
* relevant information from the buffer supplied and place it
|
||||
@ -590,7 +601,17 @@ gw_read_do_authentication(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
|
||||
*/
|
||||
if (MXS_AUTH_SUCCEEDED == auth_val)
|
||||
{
|
||||
SESSION *session;
|
||||
if (dcb->user == NULL)
|
||||
{
|
||||
/** User authentication complete, copy the username to the DCB */
|
||||
MYSQL_session *ses = dcb->data;
|
||||
if ((dcb->user = MXS_STRDUP(ses->user)) == NULL)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
gwbuf_free(read_buffer);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protocol->protocol_auth_state = MXS_AUTH_STATE_RESPONSE_SENT;
|
||||
/**
|
||||
@ -600,7 +621,7 @@ gw_read_do_authentication(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
|
||||
* is changed so that future data will go through the
|
||||
* normal data handling function instead of this one.
|
||||
*/
|
||||
session = session_alloc(dcb->service, dcb);
|
||||
SESSION *session = session_alloc(dcb->service, dcb);
|
||||
|
||||
if (session != NULL)
|
||||
{
|
||||
|
@ -56,6 +56,25 @@ uint8_t null_client_sha1[MYSQL_SCRAMBLE_LEN] = "";
|
||||
|
||||
static server_command_t* server_command_init(server_command_t* srvcmd, mysql_server_cmd_t cmd);
|
||||
|
||||
/**
|
||||
* @brief Allocate a new MySQL_session
|
||||
* @return New MySQL_session or NULL if memory allocation failed
|
||||
*/
|
||||
MYSQL_session* mysql_session_alloc()
|
||||
{
|
||||
MYSQL_session *ses = MXS_CALLOC(1, sizeof(MYSQL_session));
|
||||
|
||||
if (ses)
|
||||
{
|
||||
#ifdef SS_DEBUG
|
||||
ses->myses_chk_top = CHK_NUM_MYSQLSES;
|
||||
ses->myses_chk_tail = CHK_NUM_MYSQLSES;
|
||||
#endif
|
||||
}
|
||||
|
||||
return ses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates MySQL protocol structure
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user