summaryrefslogtreecommitdiff
path: root/sql/sql_acl.cc
diff options
context:
space:
mode:
authorKristofer Pettersson <kristofer.pettersson@oracle.com>2011-08-23 16:20:21 +0200
committerKristofer Pettersson <kristofer.pettersson@oracle.com>2011-08-23 16:20:21 +0200
commitc8840c973c27dbb2ba74dd68e12bb7ee15acc4c2 (patch)
tree8d173bc6c8a8fcdf7e0d1f035c24cdb283841734 /sql/sql_acl.cc
parentaf6f0876ade33ad67e4a3f458a88ec21649656e2 (diff)
downloadmariadb-git-c8840c973c27dbb2ba74dd68e12bb7ee15acc4c2.tar.gz
Bug10064164 Clean up of the protocol to avoid ambiguities
The client-server protocol has left some room for interpretation which this patch fixes by introducing byte counters and enforced logic for SSL handshakes.
Diffstat (limited to 'sql/sql_acl.cc')
-rw-r--r--sql/sql_acl.cc145
1 files changed, 105 insertions, 40 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 06a67edda36..d3f03d40d22 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -520,23 +520,9 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
1 + USERNAME_LENGTH + 1)
-#if defined(HAVE_OPENSSL)
-/*
- Without SSL the handshake consists of one packet. This packet
- has both client capabilities 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_capabilities 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 && !EMBEDDED_LIBRARY */
+/** 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
static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
static MEM_ROOT mem, memex;
@@ -8552,37 +8538,92 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
#ifndef EMBEDDED_LIBRARY
NET *net= mpvio->net;
char *end;
-
+ bool packet_has_required_size= false;
DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
- if (pkt_len < MIN_HANDSHAKE_SIZE)
- return packet_error;
-
if (mpvio->connect_errors)
reset_host_errors(mpvio->ip);
- ulong client_capabilities= uint2korr(net->read_pos);
- if (client_capabilities & CLIENT_PROTOCOL_41)
+ 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)
+ return packet_error;
+
+ mpvio->client_capabilities= uint2korr(end);
+
+ /*
+ JConnector only sends server capabilities before starting SSL
+ negotiation. The below code is patch for this.
+ */
+ if (bytes_remaining_in_packet == 4 &&
+ mpvio->client_capabilities & CLIENT_SSL)
{
- client_capabilities|= ((ulong) uint2korr(net->read_pos + 2)) << 16;
- mpvio->max_client_packet_length= uint4korr(net->read_pos + 4);
- DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
- if (mpvio->charset_adapter->init_client_charset((uint) net->read_pos[8]))
+ mpvio->client_capabilities= uint4korr(end);
+ mpvio->max_client_packet_length= 0xfffff;
+ charset_code= default_charset_info->number;
+ if (mpvio->charset_adapter->init_client_charset(charset_code))
return packet_error;
- end= (char*) net->read_pos + 32;
+ goto skip_to_ssl;
}
+
+ if (mpvio->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)
+ return packet_error;
+
+ if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
{
- mpvio->max_client_packet_length= uint3korr(net->read_pos + 2);
- end= (char*) net->read_pos + 5;
+ mpvio->client_capabilities= uint4korr(end);
+ mpvio->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
+ {
+ mpvio->client_capabilities= uint2korr(end);
+ mpvio->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;
}
- /* Disable those bits which are not supported by the client. */
- mpvio->client_capabilities&= client_capabilities;
-
+ DBUG_PRINT("info", ("client_character_set: %u", charset_code));
+ if (mpvio->charset_adapter->init_client_charset(charset_code))
+ return packet_error;
#if defined(HAVE_OPENSSL)
DBUG_PRINT("info", ("client capabilities: %lu", mpvio->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.
+ */
+skip_to_ssl:
if (mpvio->client_capabilities & CLIENT_SSL)
{
unsigned long errptr;
@@ -8599,18 +8640,42 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
}
DBUG_PRINT("info", ("Reading user information over SSL layer"));
- pkt_len= my_net_read(net);
- if (pkt_len == 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));
return packet_error;
}
- }
-#endif
+ /*
+ 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)
- return packet_error;
+ /*
+ 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 (mpvio->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)
+ return packet_error;
+ }
+#endif /* HAVE_OPENSSL */
if ((mpvio->client_capabilities & CLIENT_TRANSACTIONS) &&
opt_using_transactions)
@@ -8634,7 +8699,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
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);
+ 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);