From 4ee1f304cac528ad8d92fc80f65df9d31061ed29 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 18 Mar 2016 11:22:13 +0200 Subject: [PATCH] MXS-585 regression: Authentication packets are now processed in contiguous memory The client side authentication assumed that it was processing contiguous memory. This caused the authentication to fail when packets were received in multiple parts. Transforming the buffer chain into one contiguous buffer fixes this problem. --- server/modules/protocol/mysql_client.c | 105 ++++++++++++++----------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 40295015b..33171ea89 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -84,6 +84,7 @@ static int MySQLSendHandshake(DCB* dcb); static int route_by_statement(SESSION *, GWBUF **); static void mysql_client_auth_error_handling(DCB *dcb, int auth_val); extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db,int); +static bool ensure_complete_packet(DCB *dcb, GWBUF **read_buffer, int nbytes_read); /* * The "module object" for the mysqld client protocol module. @@ -480,6 +481,14 @@ int gw_read_client_event(DCB* dcb) packet_number = ssl_required_by_dcb(dcb) ? 3 : 2; + if (!ensure_complete_packet(dcb, &read_buffer, nbytes_read)) + { + return 0; + } + + /** Currently authentication requires that the buffer is contiguous */ + read_buffer = gwbuf_make_contiguous(read_buffer); + /** * The first step in the authentication process is to extract the * relevant information from the buffer supplied and place it @@ -603,55 +612,10 @@ int gw_read_client_event(DCB* dcb) if (stmt_input || protocol->protocol_auth_state == MYSQL_AUTH_SENT) { - /** - * if read queue existed appent read to it. - * if length of read buffer is less than 3 or less than mysql packet - * then return. - * else copy mysql packets to separate buffers from read buffer and - * continue. - * else - * if read queue didn't exist, length of read is less than 3 or less - * than mysql packet then - * create read queue and append to it and return. - * if length read is less than mysql packet length append to read queue - * append to it and return. - * else (complete packet was read) continue. - */ - if (dcb->dcb_readqueue) + if (!ensure_complete_packet(dcb, &read_buffer, nbytes_read)) { - uint8_t* data; - - dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); - nbytes_read = gwbuf_length(dcb->dcb_readqueue); - data = (uint8_t *)GWBUF_DATA(dcb->dcb_readqueue); - int plen = MYSQL_GET_PACKET_LEN(data); - if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data) + 4) - { - rc = 0; - goto return_rc; - } - else - { - /** - * There is at least one complete mysql packet in - * read_buffer. - */ - read_buffer = dcb->dcb_readqueue; - dcb->dcb_readqueue = NULL; - } + return 0; } - else - { - uint8_t* data = (uint8_t *)GWBUF_DATA(read_buffer); - - if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data) + 4) - { - dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); - rc = 0; - goto return_rc; - } - } - } /** @@ -1572,3 +1536,50 @@ static int route_by_statement(SESSION* session, GWBUF** p_readbuf) return_rc: return rc; } + +/** + * if read queue existed appent read to it. if length of read buffer is less + * than 3 or less than mysql packet then return. else copy mysql packets to + * separate buffers from read buffer and continue. else if read queue didn't + * exist, length of read is less than 3 or less than mysql packet then + * create read queue and append to it and return. if length read is less than + * mysql packet length append to read queue append to it and return. + * else (complete packet was read) continue. + * + * @return True if we have a complete packet, otherwise false + */ +static bool ensure_complete_packet(DCB *dcb, GWBUF **read_buffer, int nbytes_read) +{ + if (dcb->dcb_readqueue) + { + dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, *read_buffer); + nbytes_read = gwbuf_length(dcb->dcb_readqueue); + int plen = MYSQL_GET_PACKET_LEN((uint8_t *) GWBUF_DATA(dcb->dcb_readqueue)); + + if (nbytes_read < 3 || nbytes_read < plen + 4) + { + return false; + } + else + { + /** + * There is at least one complete mysql packet in + * read_buffer. + */ + *read_buffer = dcb->dcb_readqueue; + dcb->dcb_readqueue = NULL; + } + } + else + { + uint8_t* data = (uint8_t *) GWBUF_DATA(*read_buffer); + + if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data) + 4) + { + dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, *read_buffer); + return false; + } + } + + return true; +}