Skip to content

Commit 12df805

Browse files
committed
Fixed potential leak in certificate selection callback
Signed-off-by: Ted Poole <tpoole@redhat.com>
1 parent f5c049b commit 12df805

File tree

2 files changed

+58
-11
lines changed

2 files changed

+58
-11
lines changed

bssl-compat/source/SSL_CTX_set_select_certificate_cb.cc

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ typedef enum ssl_select_cert_result_t (*select_certificate_cb_t)(const SSL_CLIEN
1515
* in_select_certificate_cb(ssl) function to query whether or not we are
1616
* executing within a SSL_CTX_set_select_certificate_cb() callback for that SSL
1717
* object, or not.
18-
*
19-
* This mechanism is used by the SSL_get_servername() function to provide a
20-
* different implementation depending on it's invocation context.
18+
*
19+
* This mechanism is used by SSL_get_servername() & SSL_set_ocsp_response()
20+
* to provide different behavior depending on invocation context.
2121
*/
2222
class ActiveSelectCertificateCb {
2323
public:
@@ -27,14 +27,14 @@ class ActiveSelectCertificateCb {
2727
~ActiveSelectCertificateCb() {
2828
SSL_set_ex_data(ssl_, index(), nullptr);
2929
}
30+
static bool isActive(const SSL *ssl) {
31+
return SSL_get_ex_data(ssl, index()) != nullptr;
32+
}
33+
private:
3034
static int index() {
31-
static int index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr,
32-
+[](void *, void *ptr, CRYPTO_EX_DATA *, int, long, void*) {
33-
if (ptr) ossl_OPENSSL_free(ptr);
34-
});
35+
static int index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
3536
return index;
3637
}
37-
private:
3838
SSL *ssl_;
3939
};
4040

@@ -43,7 +43,7 @@ class ActiveSelectCertificateCb {
4343
* callback invocation for the specified SSL object.
4444
*/
4545
bool in_select_certificate_cb(const SSL *ssl) {
46-
return SSL_get_ex_data(ssl, ActiveSelectCertificateCb::index()) != nullptr;
46+
return ActiveSelectCertificateCb::isActive(ssl);
4747
}
4848

4949

@@ -101,15 +101,19 @@ static int ssl_ctx_client_hello_cb(SSL *ssl, int *alert, void *arg) {
101101
return ossl_SSL_CLIENT_HELLO_ERROR;
102102
}
103103

104+
// Ensure extensions are freed even if the callback throws
105+
std::unique_ptr<uint8_t, decltype(&OPENSSL_free)> cleanup(
106+
const_cast<uint8_t*>(client_hello.extensions),
107+
OPENSSL_free
108+
);
109+
104110
enum ssl_select_cert_result_t result;
105111

106112
{
107113
ActiveSelectCertificateCb active(ssl);
108114
result = callback(&client_hello);
109115
}
110116

111-
OPENSSL_free((void*)client_hello.extensions);
112-
113117
switch (result) {
114118
case ssl_select_cert_success: return ossl_SSL_CLIENT_HELLO_SUCCESS;
115119
case ssl_select_cert_retry: return ossl_SSL_CLIENT_HELLO_RETRY;

bssl-compat/source/test/test_ssl.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,49 @@ TEST(SSLTest, test_SSL_set_ocsp_response_early_return_leak_inside_select_certifi
17341734
#endif // BSSL_COMPAT
17351735

17361736

1737+
/**
1738+
* @brief This test exercises a leak in SSL_CTX_set_select_certificate_cb() when
1739+
* the certificate selection callback throws an exception.
1740+
*
1741+
* Without a fix for the leak, running this test under valgrind or similar
1742+
* memory checker tool will report the memory leak.
1743+
*/
1744+
TEST(SSLTest, test_SSL_CTX_set_select_certificate_cb_leak_from_callback_exception) {
1745+
TempFile server_2_key_pem { server_2_key_pem_str };
1746+
TempFile server_2_cert_chain_pem { server_2_cert_chain_pem_str };
1747+
1748+
int sockets[2];
1749+
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sockets));
1750+
SocketCloser close[] { sockets[0], sockets[1] };
1751+
1752+
bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_server_method()));
1753+
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_client_method()));
1754+
1755+
// Set up server with a select certificate callback that raises an exception
1756+
SSL_CTX_set_select_certificate_cb(server_ctx.get(), [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t {
1757+
throw std::runtime_error("Intentional exception to test for memory leaks");
1758+
});
1759+
1760+
ASSERT_TRUE(SSL_CTX_use_certificate_chain_file(server_ctx.get(), server_2_cert_chain_pem.path()));
1761+
ASSERT_TRUE(SSL_CTX_use_PrivateKey_file(server_ctx.get(), server_2_key_pem.path(), SSL_FILETYPE_PEM));
1762+
bssl::UniquePtr<SSL> server_ssl(SSL_new(server_ctx.get()));
1763+
ASSERT_TRUE(SSL_set_fd(server_ssl.get(), sockets[0]));
1764+
SSL_set_accept_state(server_ssl.get());
1765+
1766+
// Set up client
1767+
SSL_CTX_set_verify(client_ctx.get(), SSL_VERIFY_NONE, nullptr);
1768+
bssl::UniquePtr<SSL> client_ssl(SSL_new(client_ctx.get()));
1769+
ASSERT_TRUE(SSL_set_fd(client_ssl.get(), sockets[1]));
1770+
SSL_set_connect_state(client_ssl.get());
1771+
1772+
// Handshake will fail because of the exception in the callback
1773+
EXPECT_THROW(
1774+
CompleteHandshakes(client_ssl.get(), server_ssl.get()),
1775+
std::runtime_error
1776+
);
1777+
}
1778+
1779+
17371780
/**
17381781
* Test that setting a TLS alert and returning ssl_verify_invalid, from a
17391782
* callback installed via SSL_CTX_set_custom_verify(), results in a handshake

0 commit comments

Comments
 (0)