Skip to content

Commit ad523c1

Browse files
authored
Merge pull request #429 from tedjpoole/fix_ssl_get_servername_leak_1_32
[bp/1.32] Fix leak in bssl-compat SSL_get_servername()
2 parents f644146 + 68e0e78 commit ad523c1

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
@@ -1464,6 +1464,59 @@ TEST(SSLTest, test_SSL_get_servername_inside_select_certificate_cb) {
14641464
}
14651465

14661466

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

0 commit comments

Comments
 (0)