Skip to content

Commit 7bf4aed

Browse files
authored
Merge pull request #428 from tedjpoole/fix_ssl_get_servername_leak_1_34
[bp/1.34] Fix leak in bssl-compat SSL_get_servername()
2 parents 7d41aba + 40bd054 commit 7bf4aed

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

bssl-compat/source/SSL_get_servername.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,18 @@ const char *SSL_get_servername(const SSL *ssl, const int type) {
5353
size_t len;
5454

5555
// Extract the bytes from the client hello SNI extension, if present.
56-
if (ossl_SSL_client_hello_get0_ext(const_cast<SSL*>(ssl), TLSEXT_TYPE_server_name, &p, &len)) {
56+
if (ossl_SSL_client_hello_get0_ext(const_cast<SSL*>(ssl), type, &p, &len)) {
5757
if ((p = extract_ext_str_value(p, len, type)) != nullptr) {
5858
// The string pointed to by p is len bytes long but NOT null-terminated.
5959
// Therefore, we have to make a null-terminated copy of it for returning.
6060
char *copy {ossl_OPENSSL_strndup(reinterpret_cast<const char*>(p), len)};
6161

62+
// The free func (registered with SSL_get_ex_new_index() above) is only
63+
// called when the SSL object is finally freed. Therefore, we need to
64+
// explicitly free any ex data value that may in the slot from previous
65+
// calls on the same SSL object.
66+
ossl_OPENSSL_free(SSL_get_ex_data(ssl, index));
67+
6268
// Squirel away the copy in the SSL object's ext data so it won't leak.
6369
if (SSL_set_ex_data(const_cast<SSL*>(ssl), index, copy) == 0) {
6470
ossl_OPENSSL_free(copy);

bssl-compat/source/test/test_ssl.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,59 @@ TEST(SSLTest, test_SSL_get_servername_inside_select_certificate_cb) {
14631463
}
14641464

14651465

1466+
/**
1467+
* @brief This test exercises a leak in SSL_get_servername()
1468+
*
1469+
* If SSL_get_servername() was invoked multiple times from the same certificate
1470+
* selection callback, it was leaking the string value that was returned from
1471+
* the previous invocation(s).
1472+
*
1473+
* Note that the string returned by the _last_ SSL_get_servername() invocation,
1474+
* inside a certificate selection callback, does _not_ leak i.e. if
1475+
* SSL_get_servername() is only called once during a callback, there is no leak.
1476+
* It only leaks when SSL_get_servername() is called more than once during the
1477+
* same callback.
1478+
*/
1479+
TEST(SSLTest, test_SSL_get_servername_leak_inside_select_certificate_cb) {
1480+
static const char SERVERNAME[] { "www.example.com" };
1481+
1482+
TempFile server_2_key_pem { server_2_key_pem_str };
1483+
TempFile server_2_cert_chain_pem { server_2_cert_chain_pem_str };
1484+
1485+
int sockets[2];
1486+
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sockets));
1487+
SocketCloser close[] { sockets[0], sockets[1] };
1488+
1489+
bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_server_method()));
1490+
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_client_method()));
1491+
1492+
// Set up a certificate selection callback which calls SSL_get_servername() 5 times.
1493+
// This will result in 4 leaks if the SSL_get_servername() fix is not in place.
1494+
SSL_CTX_set_select_certificate_cb(server_ctx.get(), [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t {
1495+
SSL_get_servername(client_hello->ssl, TLSEXT_NAMETYPE_host_name);
1496+
SSL_get_servername(client_hello->ssl, TLSEXT_NAMETYPE_host_name);
1497+
SSL_get_servername(client_hello->ssl, TLSEXT_NAMETYPE_host_name);
1498+
SSL_get_servername(client_hello->ssl, TLSEXT_NAMETYPE_host_name);
1499+
SSL_get_servername(client_hello->ssl, TLSEXT_NAMETYPE_host_name);
1500+
return ssl_select_cert_success;
1501+
});
1502+
ASSERT_TRUE(SSL_CTX_use_certificate_chain_file(server_ctx.get(), server_2_cert_chain_pem.path()));
1503+
ASSERT_TRUE(SSL_CTX_use_PrivateKey_file(server_ctx.get(), server_2_key_pem.path(), SSL_FILETYPE_PEM));
1504+
bssl::UniquePtr<SSL> server_ssl(SSL_new(server_ctx.get()));
1505+
ASSERT_TRUE(SSL_set_fd(server_ssl.get(), sockets[0]));
1506+
SSL_set_accept_state(server_ssl.get());
1507+
1508+
// Set up client
1509+
SSL_CTX_set_verify(client_ctx.get(), SSL_VERIFY_NONE, nullptr);
1510+
bssl::UniquePtr<SSL> client_ssl(SSL_new(client_ctx.get()));
1511+
ASSERT_TRUE(SSL_set_fd(client_ssl.get(), sockets[1]));
1512+
ASSERT_TRUE(SSL_set_tlsext_host_name(client_ssl.get(), SERVERNAME));
1513+
SSL_set_connect_state(client_ssl.get());
1514+
1515+
ASSERT_TRUE(CompleteHandshakes(client_ssl.get(), server_ssl.get()));
1516+
}
1517+
1518+
14661519
TEST(SSLTest, test_SSL_get_servername_null_inside_select_certificate_cb) {
14671520
TempFile server_2_key_pem { server_2_key_pem_str };
14681521
TempFile server_2_cert_chain_pem { server_2_cert_chain_pem_str };

0 commit comments

Comments
 (0)