Skip to content

Commit 6e43035

Browse files
committed
[MDEV-31585] Stop trusting or relying on client identifying information sent prior to the TLS handshake
The server has heretofore improperly mishandled—and TRUSTED—information sent in the plaintext login request packet sent prior to the TLS handshake. As a result of this, the client is *forced* to send excessive and exploitable identifying information in the pre-TLS-handshake plaintext login packet. That client-side vulnerability is CONC-654. This modifies the server to stop relying on any of the information in the pre-TLS-handshake plaintext login packet EXCEPT for the single bit that tells it that a TLS handshake will follow. It furthermore adds a capability bit to the server greeting packet, which informs the client that it is safe to send a bare-bones dummy packet containing ONLY the instruction that a TLS handshake will follow: /* This capability is set if: * * - The CLIENT knows how to send a truncated 2-byte SSLRequest * packet, containing no information other than the CLIENT_SSL flag * which is necessary to trigger the TLS handshake, and to send its * complete capability flags and other identifying information after * the TLS handshake. * - The SERVER knows how to receive this truncated 2-byte SSLRequest * packet, and to receive the client's complete capability bits * after the TLS handshake. * */ #define CLIENT_CAN_SSL_V2 (1ULL << 37) Because the client cannot safely send the SSL_V2 SSLRequest packet unless the server has advertised support for it in its (plaintext) Server Greeting packet, an active MITM could strip the CLIENT_CAN_SSL_V2 bit from that Server Greeting packet. This downgrade attack will force the client to continue exhibiting the CONC-654 vulnerability. The server is also modified to detect this case and abort the connection; this won't fix the one-time client information leakage of the CONC-654 vulnerability, but it is intended to discourage the MITM attack by making it highly visible. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc.
1 parent 42f1426 commit 6e43035

File tree

3 files changed

+90
-23
lines changed

3 files changed

+90
-23
lines changed

include/mysql_com.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,20 @@ enum enum_indicator_type
260260
/* Client no longer needs EOF packet */
261261
#define CLIENT_DEPRECATE_EOF (1ULL << 24)
262262

263+
/* This capability is set if:
264+
*
265+
* - The CLIENT knows how to send a truncated 2-byte SSLRequest
266+
* packet, containing no information other than the CLIENT_SSL flag
267+
* which is necessary to trigger the TLS handshake, and to send its
268+
* complete capability flags and other identifying information after
269+
* the TLS handshake.
270+
* - The SERVER knows how to receive this truncated 2-byte SSLRequest
271+
* packet, and to receive the client's complete capability bits
272+
* after the TLS handshake.
273+
*
274+
*/
275+
#define CLIENT_CAN_SSL_V2 (1ULL << 37)
276+
263277
#define CLIENT_PROGRESS_OBSOLETE (1ULL << 29)
264278
#define CLIENT_SSL_VERIFY_SERVER_CERT (1ULL << 30)
265279
/*

libmariadb

sql/sql_acl.cc

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12229,6 +12229,7 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
1222912229
{
1223012230
thd->client_capabilities |= CLIENT_SSL;
1223112231
thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
12232+
thd->client_capabilities |= CLIENT_CAN_SSL_V2; /* See parse_client_handshake_packet */
1223212233
}
1223312234

1223412235
if (data_len)
@@ -12714,30 +12715,31 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
1271412715
*/
1271512716
DBUG_ASSERT(net->read_pos[pkt_len] == 0);
1271612717

12717-
ulonglong client_capabilities= uint2korr(net->read_pos);
12718-
compile_time_assert(sizeof(client_capabilities) >= 8);
12719-
if (client_capabilities & CLIENT_PROTOCOL_41)
12720-
{
12721-
if (pkt_len < 32)
12722-
DBUG_RETURN(packet_error);
12723-
client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
12724-
if (!(client_capabilities & CLIENT_MYSQL))
12725-
{
12726-
// it is client with mariadb extensions
12727-
ulonglong ext_client_capabilities=
12728-
(((ulonglong)uint4korr(net->read_pos + 28)) << 32);
12729-
client_capabilities|= ext_client_capabilities;
12730-
}
12731-
}
12732-
12733-
/* Disable those bits which are not supported by the client. */
12734-
compile_time_assert(sizeof(thd->client_capabilities) >= 8);
12735-
thd->client_capabilities&= client_capabilities;
12718+
ushort first_two_bytes_of_client_capabilities= uint2korr(net->read_pos);
12719+
bool pre_tls_client_packet_is_ssl_v2= (pkt_len==2 && first_two_bytes_of_client_capabilities == CLIENT_SSL);
12720+
bool pre_tls_client_packet_wants_ssl= !!(first_two_bytes_of_client_capabilities & CLIENT_SSL);
12721+
12722+
if (pre_tls_client_packet_wants_ssl)
12723+
{
12724+
/* Client wants to use TLS. This SSLRequest packet, sent in
12725+
* plaintext before the TLS handshake, is basically just a vestige
12726+
* that triggers the server (us) to start the TLS handshake.
12727+
*
12728+
* We ignore everything else in this pre-TLS packet, even though
12729+
* older clients send much of the same information that they will
12730+
* re-send over the TLS channel.
12731+
*
12732+
* This pre-TLS packet is untrustworthy AND if the server acts on
12733+
* its content, that FORCES the client to send more information
12734+
* in the clear.
12735+
*/
1273612736

12737-
DBUG_PRINT("info", ("client capabilities: %llu", thd->client_capabilities));
12738-
if (thd->client_capabilities & CLIENT_SSL)
12739-
{
1274012737
unsigned long errptr __attribute__((unused));
12738+
if (pre_tls_client_packet_is_ssl_v2)
12739+
DBUG_PRINT("info", ("client sent SSL_V2 SSLRequest packet (2 bytes with only TLS/SSL bit set)"));
12740+
else
12741+
DBUG_PRINT("info", ("client sent old SSLRequest packet (%ld bytes including TLS/SSL bit; capabilities & 0xffff == 0x%04x)",
12742+
pkt_len, first_two_bytes_of_client_capabilities));
1274112743

1274212744
/* Do the SSL layering. */
1274312745
if (!ssl_acceptor_fd)
@@ -12753,6 +12755,10 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
1275312755
DBUG_PRINT("info", ("Immediately following IO layer change: vio_type=%s",
1275412756
safe_vio_type_name(thd->net.vio)));
1275512757

12758+
/* Now we are using TLS. The client will resend its REAL
12759+
* handshake packet, containing complete credentials and
12760+
* capability information.
12761+
*/
1275612762
DBUG_PRINT("info", ("Reading user information over SSL layer"));
1275712763
pkt_len= my_net_read(net);
1275812764
if (pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE)
@@ -12761,8 +12767,55 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
1276112767
pkt_len));
1276212768
DBUG_RETURN(packet_error);
1276312769
}
12770+
12771+
/* Re-load the FIRST TWO BYTES of the capabilities from the packet sent over TLS. */
12772+
first_two_bytes_of_client_capabilities = uint2korr(net->read_pos);
12773+
}
12774+
12775+
ulonglong client_capabilities= (ulonglong) first_two_bytes_of_client_capabilities;
12776+
compile_time_assert(sizeof(client_capabilities) >= 8);
12777+
12778+
DBUG_PRINT("info", ("client capabilities: %llu", thd->client_capabilities));
12779+
if (client_capabilities & CLIENT_PROTOCOL_41)
12780+
{
12781+
if (pkt_len < 32)
12782+
DBUG_RETURN(packet_error);
12783+
client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
12784+
if (!(client_capabilities & CLIENT_MYSQL))
12785+
{
12786+
// it is client with mariadb extensions
12787+
ulonglong ext_client_capabilities=
12788+
(((ulonglong)uint4korr(net->read_pos + 28)) << 32);
12789+
client_capabilities|= ext_client_capabilities;
12790+
}
12791+
}
12792+
bool post_tls_client_packet_indicates_ssl_v2= (client_capabilities & CLIENT_CAN_SSL_V2);
12793+
12794+
if (pre_tls_client_packet_wants_ssl
12795+
&& post_tls_client_packet_indicates_ssl_v2
12796+
&& !pre_tls_client_packet_is_ssl_v2)
12797+
{
12798+
/* 1. We told the client in our server greeting that we support the pre-TLS client packet containing only the TLS/SSL flag,
12799+
* CLIENT_CAN_SSL_V2. [Server greeting packet is sent in the clear, may be MITM'ed en route to the client.]
12800+
* 2. The client told us in its pre-TLS SSLRequest packet that it wants to use SSL. (CLIENT_SSL flag)
12801+
* 3. The client told us in its post-TLS packet that it too supports the pre-TLS client packet containing only the TLS/SSL flag,
12802+
* CLIENT_CAN_SSL_V2. [We received this information via TLS; assuming the client validated our server certificate
12803+
* to avoid a 2-sided TLS MITM, we know that this packet is authentically from the client.]
12804+
* 4. Nevertheless, the client DID NOT SEND us an SSL_V2-style SSLRequest packet.
12805+
*
12806+
* The only way this can happen is if the client is being downgraded by an active MITM attacker which
12807+
* disables the CLIENT_CAN_SSL_V2 bit in our server greeting packet.
12808+
*/
12809+
sql_print_warning("Aborting connection %lld because it is being actively MITM'ed to downgrade TLS security (attacker "
12810+
"is stripping the CLIENT_CAN_SSL_V2 bit from our server capabilities)",
12811+
thd->thread_id);
12812+
DBUG_RETURN(packet_error);
1276412813
}
1276512814

12815+
/* Disable those bits which are not supported by the client. */
12816+
compile_time_assert(sizeof(thd->client_capabilities) >= 8);
12817+
thd->client_capabilities&= client_capabilities;
12818+
1276612819
if (client_capabilities & CLIENT_PROTOCOL_41)
1276712820
{
1276812821
thd->max_client_packet_length= uint4korr(net->read_pos+4);

0 commit comments

Comments
 (0)