diff options
author | Karen Langford <karen.langford@oracle.com> | 2011-07-06 00:56:51 +0200 |
---|---|---|
committer | Karen Langford <karen.langford@oracle.com> | 2011-07-06 00:56:51 +0200 |
commit | f6398a86dde400bc97009350c61dd9a9d3c1d715 (patch) | |
tree | eefc26771a8f290ae8b29e9d7ecb41b19010a909 /sql/sql_connect.cc | |
parent | c2e8aacb3f4fa952d4a9f6757dad1654ec70f49b (diff) | |
parent | ae46a66099cfe66e9423a2cc9ef0c5e2097ae73d (diff) | |
download | mariadb-git-f6398a86dde400bc97009350c61dd9a9d3c1d715.tar.gz |
Merge from mysql-5.1.58-release
Diffstat (limited to 'sql/sql_connect.cc')
-rw-r--r-- | sql/sql_connect.cc | 217 |
1 files changed, 138 insertions, 79 deletions
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 2a2f11e7f46..44b4af74ef1 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -21,23 +21,10 @@ #include "mysql_priv.h" -#ifdef HAVE_OPENSSL -/* - Without SSL the handshake consists of one packet. This packet - has both client capabilites and scrambled password. - With SSL the handshake might consist of two packets. If the first - packet (client capabilities) has CLIENT_SSL flag set, we have to - switch to SSL and read the second packet. The scrambled password - is in the second packet and client_capabilites field will be ignored. - Maybe it is better to accept flags other than CLIENT_SSL from the - second packet? -*/ -#define SSL_HANDSHAKE_SIZE 2 -#define NORMAL_HANDSHAKE_SIZE 6 -#define MIN_HANDSHAKE_SIZE 2 -#else -#define MIN_HANDSHAKE_SIZE 6 -#endif /* HAVE_OPENSSL */ +/** Size of the header fields of an authentication packet. */ +#define AUTH_PACKET_HEADER_SIZE_PROTO_41 32 +#define AUTH_PACKET_HEADER_SIZE_PROTO_40 5 +#define AUTH_PACKET_HEADER_SIZE_CONNJ_SSL 4 #ifdef __WIN__ extern void win_install_sigabrt_handler(); @@ -822,6 +809,14 @@ static int check_connection(THD *thd) ulong pkt_len= 0; char *end; + bool packet_has_required_size= false; + char *db; + size_t db_len; + char *passwd; + size_t passwd_len; + char *user; + size_t user_len; + DBUG_PRINT("info", ("New connection received on %s", vio_description(net->vio))); #ifdef SIGNAL_WITH_VIO_CLOSE @@ -930,8 +925,7 @@ static int check_connection(THD *thd) /* At this point we write connection message and read reply */ if (net_write_command(net, (uchar) protocol_version, (uchar*) "", 0, (uchar*) buff, (size_t) (end-buff)) || - (pkt_len= my_net_read(net)) == packet_error || - pkt_len < MIN_HANDSHAKE_SIZE) + (pkt_len= my_net_read(net)) == packet_error) { inc_host_errors(&thd->remote.sin_addr); my_error(ER_HANDSHAKE_ERROR, MYF(0)); @@ -946,22 +940,82 @@ static int check_connection(THD *thd) if (thd->packet.alloc(thd->variables.net_buffer_length)) return 1; /* The error is set by alloc(). */ - thd->client_capabilities= uint2korr(net->read_pos); + uint charset_code= 0; + end= (char *)net->read_pos; + /* + In order to safely scan a head for '\0' string terminators + we must keep track of how many bytes remain in the allocated + buffer or we might read past the end of the buffer. + */ + size_t bytes_remaining_in_packet= pkt_len; + + /* + Peek ahead on the client capability packet and determine which version of + the protocol should be used. + */ + if (bytes_remaining_in_packet < 2) + goto error; + + thd->client_capabilities= uint2korr(end); + + /* + Connector/J only sends client capabilities (4 bytes) before starting SSL + negotiation so we don't have char_set and other information for client in + packet read. In that case, skip reading those information. The below code + is patch for this. + */ + if(bytes_remaining_in_packet == AUTH_PACKET_HEADER_SIZE_CONNJ_SSL && + (thd->client_capabilities & CLIENT_SSL)) + { + thd->client_capabilities= uint4korr(end); + thd->max_client_packet_length= global_system_variables.max_allowed_packet; + charset_code= default_charset_info->number; + end+= AUTH_PACKET_HEADER_SIZE_CONNJ_SSL; + bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_CONNJ_SSL; + goto skip_to_ssl; + } + + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + packet_has_required_size= bytes_remaining_in_packet >= + AUTH_PACKET_HEADER_SIZE_PROTO_41; + else + packet_has_required_size= bytes_remaining_in_packet >= + AUTH_PACKET_HEADER_SIZE_PROTO_40; + + if (!packet_has_required_size) + goto error; + if (thd->client_capabilities & CLIENT_PROTOCOL_41) { - thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; - thd->max_client_packet_length= uint4korr(net->read_pos+4); - DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); - if (thd_init_client_charset(thd, (uint) net->read_pos[8])) - return 1; - thd->update_charset(); - end= (char*) net->read_pos+32; + thd->client_capabilities= uint4korr(end); + thd->max_client_packet_length= uint4korr(end + 4); + charset_code= (uint)(uchar)*(end + 8); + /* + Skip 23 remaining filler bytes which have no particular meaning. + */ + end+= AUTH_PACKET_HEADER_SIZE_PROTO_41; + bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_41; } else { - thd->max_client_packet_length= uint3korr(net->read_pos+2); - end= (char*) net->read_pos+5; + thd->client_capabilities= uint2korr(end); + thd->max_client_packet_length= uint3korr(end + 2); + end+= AUTH_PACKET_HEADER_SIZE_PROTO_40; + bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_40; + /** + Old clients didn't have their own charset. Instead the assumption + was that they used what ever the server used. + */ + charset_code= default_charset_info->number; } + +skip_to_ssl: + + DBUG_PRINT("info", ("client_character_set: %u", charset_code)); + if (thd_init_client_charset(thd, charset_code)) + goto error; + thd->update_charset(); + /* Disable those bits which are not supported by the server. This is a precautionary measure, if the client lies. See Bug#27944. @@ -972,42 +1026,63 @@ static int check_connection(THD *thd) thd->variables.sql_mode|= MODE_IGNORE_SPACE; #ifdef HAVE_OPENSSL DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities)); + + /* + If client requested SSL then we must stop parsing, try to switch to SSL, + and wait for the client to send a new handshake packet. + The client isn't expected to send any more bytes until SSL is initialized. + */ if (thd->client_capabilities & CLIENT_SSL) { /* Do the SSL layering. */ if (!ssl_acceptor_fd) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0)); - return 1; - } + goto error; + DBUG_PRINT("info", ("IO layer change in progress...")); if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout)) { DBUG_PRINT("error", ("Failed to accept new SSL connection")); - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0)); - return 1; + goto error; } + DBUG_PRINT("info", ("Reading user information over SSL layer")); - if ((pkt_len= my_net_read(net)) == packet_error || - pkt_len < NORMAL_HANDSHAKE_SIZE) + if ((pkt_len= my_net_read(net)) == packet_error) { DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", pkt_len)); - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0)); - return 1; + goto error; } - } -#endif /* HAVE_OPENSSL */ + /* + A new packet was read and the statistics reflecting the remaining bytes + in the packet must be updated. + */ + bytes_remaining_in_packet= pkt_len; - if (end > (char *)net->read_pos + pkt_len) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0)); - return 1; + /* + After the SSL handshake is performed the client resends the handshake + packet but because of legacy reasons we chose not to parse the packet + fields a second time and instead only assert the length of the packet. + */ + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + { + + packet_has_required_size= bytes_remaining_in_packet >= + AUTH_PACKET_HEADER_SIZE_PROTO_41; + end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_41; + bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_41; + } + else + { + packet_has_required_size= bytes_remaining_in_packet >= + AUTH_PACKET_HEADER_SIZE_PROTO_40; + end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_40; + bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_40; + } + + if (!packet_has_required_size) + goto error; } +#endif /* HAVE_OPENSSL */ if (thd->client_capabilities & CLIENT_INTERACTIVE) thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; @@ -1028,29 +1103,17 @@ static int check_connection(THD *thd) else get_string= get_40_protocol_string; - /* - In order to safely scan a head for '\0' string terminators - we must keep track of how many bytes remain in the allocated - buffer or we might read past the end of the buffer. - */ - size_t bytes_remaining_in_packet= pkt_len - (end - (char *)net->read_pos); - - size_t user_len; - char *user= get_string(&end, &bytes_remaining_in_packet, &user_len); + user= get_string(&end, &bytes_remaining_in_packet, &user_len); if (user == NULL) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0)); - return 1; - } + goto error; /* Old clients send a null-terminated string as password; new clients send the size (1 byte) + string (not null-terminated). Hence in case of empty password both send '\0'. */ - size_t passwd_len= 0; - char *passwd= NULL; + passwd_len= 0; + passwd= NULL; if (thd->client_capabilities & CLIENT_SECURE_CONNECTION) { @@ -1069,24 +1132,16 @@ static int check_connection(THD *thd) } if (passwd == NULL) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0)); - return 1; - } + goto error; - size_t db_len= 0; - char *db= NULL; + db_len= 0; + db= NULL; if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) { db= get_string(&end, &bytes_remaining_in_packet, &db_len); if (db == NULL) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0)); - return 1; - } + goto error; } char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 @@ -1134,11 +1189,14 @@ static int check_connection(THD *thd) user[user_len]= '\0'; } - if (thd->main_security_ctx.user) - x_free(thd->main_security_ctx.user); if (!(thd->main_security_ctx.user= my_strdup(user, MYF(MY_WME)))) return 1; /* The error is set by my_strdup(). */ return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); + +error: + inc_host_errors(&thd->remote.sin_addr); + my_error(ER_HANDSHAKE_ERROR, MYF(0)); + return 1; } @@ -1388,3 +1446,4 @@ end_thread: } } #endif /* EMBEDDED_LIBRARY */ + |