implement server-side auth switch, now mysql8 client can login

This commit is contained in:
jingxiaobing 2018-05-16 17:57:23 +08:00
parent 0b8bc3cf97
commit dea8ae911d
6 changed files with 110 additions and 69 deletions

View File

@ -111,7 +111,7 @@ NETWORK_MYSQLD_PLUGIN_PROTO(server_con_init)
challenge->server_version_str = g_strdup_printf("%s admin", PACKAGE_STRING);
challenge->server_version = 50099;
challenge->charset = charset_get_number("latin1");
challenge->capabilities = CETUS_DEFAULT_FLAGS;
challenge->capabilities = CETUS_DEFAULT_FLAGS & ~CLIENT_TRANSACTIONS;
#ifdef HAVE_OPENSSL
if (chas->ssl) {
challenge->capabilities |= CLIENT_SSL;
@ -155,30 +155,50 @@ NETWORK_MYSQLD_PLUGIN_PROTO(server_read_auth)
/* decode the packet */
network_mysqld_proto_skip_network_header(&packet);
auth = network_mysqld_auth_response_new(con->client->challenge->capabilities);
if (network_mysqld_proto_get_auth_response(&packet, auth)) {
network_mysqld_auth_response_free(auth);
return NETWORK_SOCKET_ERROR;
}
if (!(auth->client_capabilities & CLIENT_PROTOCOL_41)) {
/* should use packet-id 0 */
network_mysqld_queue_append(con->client, con->client->send_queue,
C("\xff\xd7\x07" "4.0 protocol is not supported"));
network_mysqld_auth_response_free(auth);
return NETWORK_SOCKET_ERROR;
}
if (con->client->response == NULL) {
auth = network_mysqld_auth_response_new(con->client->challenge->capabilities);
if (network_mysqld_proto_get_auth_response(&packet, auth)) {
network_mysqld_auth_response_free(auth);
return NETWORK_SOCKET_ERROR;
}
if (!(auth->client_capabilities & CLIENT_PROTOCOL_41)) {
/* should use packet-id 0 */
network_mysqld_queue_append(con->client, con->client->send_queue,
C("\xff\xd7\x07" "4.0 protocol is not supported"));
network_mysqld_auth_response_free(auth);
return NETWORK_SOCKET_ERROR;
}
#ifdef HAVE_OPENSSL
if (auth->ssl_request) {
network_ssl_create_connection(con->client, NETWORK_SSL_SERVER);
g_string_free(g_queue_pop_tail(con->client->recv_queue->chunks), TRUE);
con->state = ST_FRONT_SSL_HANDSHAKE;
return NETWORK_SOCKET_SUCCESS;
}
if (auth->ssl_request) {
network_ssl_create_connection(con->client, NETWORK_SSL_SERVER);
g_string_free(g_queue_pop_tail(con->client->recv_queue->chunks), TRUE);
con->state = ST_FRONT_SSL_HANDSHAKE;
return NETWORK_SOCKET_SUCCESS;
}
#endif
con->client->response = auth;
con->client->response = auth;
if (g_strcmp0(auth->auth_plugin_name->str, "mysql_native_password") != 0) {
GString *packet = g_string_new(0);
network_mysqld_proto_append_auth_switch(packet, "mysql_native_password",
con->client->challenge->auth_plugin_data);
network_mysqld_queue_append(con->client, con->client->send_queue, S(packet));
con->state = ST_SEND_AUTH_RESULT;
con->auth_result_state = AUTH_SWITCH;
g_string_free(packet, TRUE);
g_string_free(g_queue_pop_tail(recv_sock->recv_queue->chunks), TRUE);
return NETWORK_SOCKET_SUCCESS;
}
} else {
/* auth switch response */
gsize auth_data_len = packet.data->len - 4;
GString *auth_data = g_string_sized_new(auth_data_len);
network_mysqld_proto_get_gstr_len(&packet, auth_data_len, auth_data);
g_string_append_len(con->client->response->auth_plugin_data, S(auth_data));
g_string_free(auth_data, TRUE);
auth = con->client->response;
}
/* Check client addr in admin-allow-ip and admin-deny-ip */
gboolean check_ip;
char *ip_err_msg = NULL;

View File

@ -1118,7 +1118,6 @@ network_mysqld_auth_challenge_new()
shake = g_new0(network_mysqld_auth_challenge, 1);
shake->auth_plugin_data = g_string_new("");
shake->scrambled_password = g_string_new("");
shake->capabilities = CETUS_DEFAULT_FLAGS;
shake->auth_plugin_name = g_string_new(NULL);
@ -1135,8 +1134,6 @@ network_mysqld_auth_challenge_free(network_mysqld_auth_challenge *shake)
g_free(shake->server_version_str);
if (shake->auth_plugin_data)
g_string_free(shake->auth_plugin_data, TRUE);
if (shake->scrambled_password)
g_string_free(shake->scrambled_password, TRUE);
if (shake->auth_plugin_name)
g_string_free(shake->auth_plugin_name, TRUE);
@ -1179,8 +1176,19 @@ network_mysqld_auth_challenge_set_challenge(network_mysqld_auth_challenge *shake
shake->auth_plugin_data->str[i] = (94.0 * (rand() / (RAND_MAX + 1.0))) + 33;
}
shake->auth_plugin_data->len = 20;
shake->auth_plugin_data->len = 21;
shake->auth_plugin_data->str[shake->auth_plugin_data->len] = '\0';
g_string_assign(shake->auth_plugin_name, "mysql_native_password");
}
int network_mysqld_proto_append_auth_switch(GString *packet, char *method_name, GString *salt)
{
network_mysqld_proto_append_int8(packet, 0xfe);
/*TODO: different algorithm for methods */
g_string_append_len(packet, method_name, strlen(method_name));
g_string_append_c(packet, 0);
g_string_append_len(packet, salt->str, salt->len);
return 0;
}
int

View File

@ -95,16 +95,21 @@ typedef struct {
#define CLIENT_PLUGIN_AUTH (1UL << 19)
#endif
#define CETUS_DEFAULT_FLAGS CLIENT_BASIC_FLAGS \
& ~CLIENT_PLUGIN_AUTH /* not support plugin auth */ \
& ~CLIENT_NO_SCHEMA /* permit database.table.column */ \
& ~CLIENT_IGNORE_SPACE \
& ~CLIENT_CONNECT_ATTRS \
& ~CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA \
& ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS \
& ~CLIENT_SESSION_TRACK \
& ~CLIENT_DEPRECATE_EOF \
& ~CLIENT_LOCAL_FILES
#if MYSQL_VERSION_ID < 50606
#define COMPATIBLE_BASIC_FLAGS (CLIENT_BASIC_FLAGS \
|CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA \
|CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS \
|CLIENT_SESSION_TRACK)
#else
#define COMPATIBLE_BASIC_FLAGS CLIENT_BASIC_FLAGS
#endif
#define CETUS_DEFAULT_FLAGS (COMPATIBLE_BASIC_FLAGS \
& ~CLIENT_NO_SCHEMA /* permit database.table.column */ \
& ~CLIENT_IGNORE_SPACE \
& ~CLIENT_DEPRECATE_EOF \
& ~CLIENT_LOCAL_FILES \
& ~CLIENT_CONNECT_ATTRS)
NETWORK_API network_mysqld_com_query_result_t *network_mysqld_com_query_result_new(void);
NETWORK_API void network_mysqld_com_query_result_free(network_mysqld_com_query_result_t *);
@ -194,7 +199,6 @@ struct network_mysqld_auth_challenge {
guint32 server_version;
guint32 thread_id;
GString *auth_plugin_data;
GString *scrambled_password;
guint32 capabilities;
guint8 charset;
guint16 server_status;
@ -223,6 +227,7 @@ struct network_mysqld_auth_response {
NETWORK_API network_mysqld_auth_response *network_mysqld_auth_response_new(guint server_capabilities);
NETWORK_API void network_mysqld_auth_response_free(network_mysqld_auth_response *);
NETWORK_API int network_mysqld_proto_append_auth_response(GString *, network_mysqld_auth_response *);
int network_mysqld_proto_append_auth_switch(GString *, char *method_name, GString* salt);
NETWORK_API int network_mysqld_proto_get_auth_response(network_packet *, network_mysqld_auth_response *);
NETWORK_API int network_mysqld_proto_get_and_change_auth_response(network_packet *, network_mysqld_auth_response *);

View File

@ -1166,18 +1166,14 @@ plugin_call(chassis *srv, network_mysqld_con *con, int state)
*/
switch (con->auth_result_state) {
case MYSQLD_PACKET_OK:
if (con->login_failed) {
g_message("%s: clt login failed:%p", G_STRLOC, con);
} else {
/*
* OK, delivered to client,
* switch to command phase
*/
con->state = ST_READ_QUERY;
if (con->is_client_compressed) {
con->client->do_compress = 1;
network_socket_set_send_buffer_size(con->client, COMPRESS_BUF_SIZE);
}
/*
* OK, delivered to client,
* switch to command phase
*/
con->state = ST_READ_QUERY;
if (con->is_client_compressed) {
con->client->do_compress = 1;
network_socket_set_send_buffer_size(con->client, COMPRESS_BUF_SIZE);
}
break;
case MYSQLD_PACKET_ERR:
@ -1185,6 +1181,10 @@ plugin_call(chassis *srv, network_mysqld_con *con, int state)
con->prev_state = con->state;
con->state = ST_ERROR;
break;
case AUTH_SWITCH:
con->auth_result_state = MYSQLD_PACKET_OK;
con->state = ST_READ_AUTH;
break;
default:
g_debug("%s: unexpected st for SEND_AUTH_RESULT: %02x", G_STRLOC, con->auth_result_state);
con->prev_state = con->state;
@ -4359,6 +4359,7 @@ proxy_self_create_auth(chassis *srv, server_connection_state_t *con)
const network_mysqld_auth_challenge *challenge = send_sock->challenge;
network_mysqld_auth_response *auth = network_mysqld_auth_response_new(challenge->capabilities);
g_string_assign(auth->auth_plugin_name, "mysql_native_password");
auth->client_capabilities = CETUS_DEFAULT_FLAGS;
@ -4373,8 +4374,6 @@ proxy_self_create_auth(chassis *srv, server_connection_state_t *con)
auth->client_capabilities &= ~CLIENT_FOUND_ROWS;
}
auth->client_capabilities &= ~CLIENT_PLUGIN_AUTH;
auth->max_packet_size = 0x01000000;
auth->charset = con->charset_code;
con->is_multi_stmt_set = 1;
@ -4383,8 +4382,6 @@ proxy_self_create_auth(chassis *srv, server_connection_state_t *con)
g_string_truncate(auth->auth_plugin_data, 0);
network_mysqld_proto_password_scramble(auth->auth_plugin_data, S(challenge->auth_plugin_data), S(con->hashed_pwd));
g_string_assign_len(challenge->scrambled_password, S(auth->auth_plugin_data));
g_string_append_len(auth->database, S(send_sock->default_db));
g_string_assign_len(auth->username, S(send_sock->username));
g_debug("%s:username: %s ", G_STRLOC, send_sock->username->str);

View File

@ -385,6 +385,10 @@ typedef struct query_cache_item {
} query_cache_item;
struct query_queue_t;
enum {
AUTH_SWITCH = 3, /* for now, value not equal to 0 or 0xff is fine */
};
/**
* get the name of a connection state
*/
@ -511,7 +515,6 @@ struct network_mysqld_con {
unsigned int is_wait_server:1; /* first connect to backend failed, retrying */
unsigned int is_calc_found_rows:1;
unsigned int login_failed:1;
unsigned int is_auto_commit:1;
unsigned int is_start_tran_command:1;
unsigned int is_prepared:1;

View File

@ -81,11 +81,12 @@ do_read_auth(network_mysqld_con *con, GHashTable *allow_ip_table, GHashTable *de
packet.data = g_queue_peek_tail(recv_sock->recv_queue->chunks);
packet.offset = 0;
network_mysqld_proto_skip_network_header(&packet);
/* assume that we may get called twice:
*
* 1. for the initial packet
* 2. for the win-auth extra data
* 2. in case auth switch happened, for the auth switch response
*
* this is detected by con->client->response being NULL
*/
@ -99,8 +100,11 @@ do_read_auth(network_mysqld_con *con, GHashTable *allow_ip_table, GHashTable *de
guint32 capabilities = con->client->challenge->capabilities;
auth = network_mysqld_auth_response_new(capabilities);
network_mysqld_proto_skip_network_header(&packet);
int err = network_mysqld_proto_get_auth_response(&packet, auth);
if (err) {
network_mysqld_auth_response_free(auth);
return NETWORK_SOCKET_ERROR;
}
#ifdef HAVE_OPENSSL
if (con->srv->ssl && auth->ssl_request) {
@ -113,10 +117,6 @@ do_read_auth(network_mysqld_con *con, GHashTable *allow_ip_table, GHashTable *de
return NETWORK_SOCKET_SUCCESS;
}
#endif
if (err) {
network_mysqld_auth_response_free(auth);
return NETWORK_SOCKET_ERROR;
}
if (!(auth->client_capabilities & CLIENT_PROTOCOL_41)) {
/* should use packet-id 0 */
network_mysqld_queue_append(con->client, con->client->send_queue,
@ -124,6 +124,7 @@ do_read_auth(network_mysqld_con *con, GHashTable *allow_ip_table, GHashTable *de
network_mysqld_auth_response_free(auth);
return NETWORK_SOCKET_ERROR;
}
if (auth->client_capabilities & CLIENT_COMPRESS) {
con->is_client_compressed = 1;
g_message("%s: client compressed for con:%p", G_STRLOC, con);
@ -134,19 +135,25 @@ do_read_auth(network_mysqld_con *con, GHashTable *allow_ip_table, GHashTable *de
con->client->response = auth;
if (g_strcmp0(auth->auth_plugin_name->str, "mysql_native_password") != 0) {
GString *packet = g_string_new(0);
network_mysqld_proto_append_auth_switch(packet, "mysql_native_password",
con->client->challenge->auth_plugin_data);
network_mysqld_queue_append(con->client, con->client->send_queue, S(packet));
con->state = ST_SEND_AUTH_RESULT;
con->auth_result_state = AUTH_SWITCH;
g_string_free(packet, TRUE);
g_string_free(g_queue_pop_tail(recv_sock->recv_queue->chunks), TRUE);
return NETWORK_SOCKET_SUCCESS;
}
g_string_assign_len(con->client->default_db, S(auth->database));
g_debug("%s:1nd round auth and set default db:%s for con:%p", G_STRLOC, con->client->default_db->str, con);
} else {
GString *auth_data;
gsize auth_data_len;
/*
* get all the data from the packet and append it
* to the auth_plugin_data
*/
auth_data_len = packet.data->len - 4;
auth_data = g_string_sized_new(auth_data_len);
/* auth switch response */
gsize auth_data_len = packet.data->len - 4;
GString *auth_data = g_string_sized_new(auth_data_len);
network_mysqld_proto_get_gstr_len(&packet, auth_data_len, auth_data);
g_string_append_len(con->client->response->auth_plugin_data, S(auth_data));
@ -212,7 +219,6 @@ do_read_auth(network_mysqld_con *con, GHashTable *allow_ip_table, GHashTable *de
response->username->str, con->client->src->name->str);
network_mysqld_con_send_error_full(con->client, L(msg), ER_ACCESS_DENIED_ERROR, "28000");
g_message("%s", msg);
con->login_failed = 1;
con->state = ST_SEND_ERROR;
}
@ -372,6 +378,8 @@ do_connect_cetus(network_mysqld_con *con, network_backend_t **backend, int *back
challenge->capabilities &= ~CLIENT_SSL;
#endif
network_mysqld_auth_challenge_set_challenge(challenge);
challenge->server_status |= SERVER_STATUS_AUTOCOMMIT;
challenge->charset = 0xC0;
GString *version = g_string_new("");
network_backends_server_version(g->backends, version);
g_string_append(version, " (cetus)");