Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions bssl-compat/source/SSL_CTX_set_select_certificate_cb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ typedef enum ssl_select_cert_result_t (*select_certificate_cb_t)(const SSL_CLIEN
* in_select_certificate_cb(ssl) function to query whether or not we are
* executing within a SSL_CTX_set_select_certificate_cb() callback for that SSL
* object, or not.
*
* This mechanism is used by the SSL_get_servername() function to provide a
* different implementation depending on it's invocation context.
*
* This mechanism is used by SSL_get_servername() & SSL_set_ocsp_response()
* to provide different behavior depending on invocation context.
*/
class ActiveSelectCertificateCb {
public:
Expand All @@ -27,14 +27,14 @@ class ActiveSelectCertificateCb {
~ActiveSelectCertificateCb() {
SSL_set_ex_data(ssl_, index(), nullptr);
}
static bool isActive(const SSL *ssl) {
return SSL_get_ex_data(ssl, index()) != nullptr;
}
private:
static int index() {
static int index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr,
+[](void *, void *ptr, CRYPTO_EX_DATA *, int, long, void*) {
if (ptr) ossl_OPENSSL_free(ptr);
});
static int index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
return index;
}
private:
SSL *ssl_;
};

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


Expand Down Expand Up @@ -101,15 +101,19 @@ static int ssl_ctx_client_hello_cb(SSL *ssl, int *alert, void *arg) {
return ossl_SSL_CLIENT_HELLO_ERROR;
}

// Ensure extensions are freed even if the callback throws
std::unique_ptr<uint8_t, decltype(&OPENSSL_free)> cleanup(
const_cast<uint8_t*>(client_hello.extensions),
OPENSSL_free
);

enum ssl_select_cert_result_t result;

{
ActiveSelectCertificateCb active(ssl);
result = callback(&client_hello);
}

OPENSSL_free((void*)client_hello.extensions);

switch (result) {
case ssl_select_cert_success: return ossl_SSL_CLIENT_HELLO_SUCCESS;
case ssl_select_cert_retry: return ossl_SSL_CLIENT_HELLO_RETRY;
Expand Down
43 changes: 43 additions & 0 deletions bssl-compat/source/test/test_ssl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1671,6 +1671,49 @@ TEST(SSLTest, test_SSL_set_ocsp_response_leak_inside_select_certificate_cb) {
}


/**
* @brief This test exercises a leak in SSL_CTX_set_select_certificate_cb() when
* the certificate selection callback throws an exception.
*
* Without a fix for the leak, running this test under valgrind or similar
* memory checker tool will report the memory leak.
*/
TEST(SSLTest, test_SSL_CTX_set_select_certificate_cb_leak_from_callback_exception) {
TempFile server_2_key_pem { server_2_key_pem_str };
TempFile server_2_cert_chain_pem { server_2_cert_chain_pem_str };

int sockets[2];
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sockets));
SocketCloser close[] { sockets[0], sockets[1] };

bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_server_method()));
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_client_method()));

// Set up server with a select certificate callback that raises an exception
SSL_CTX_set_select_certificate_cb(server_ctx.get(), [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t {
throw std::runtime_error("Intentional exception to test for memory leaks");
});

ASSERT_TRUE(SSL_CTX_use_certificate_chain_file(server_ctx.get(), server_2_cert_chain_pem.path()));
ASSERT_TRUE(SSL_CTX_use_PrivateKey_file(server_ctx.get(), server_2_key_pem.path(), SSL_FILETYPE_PEM));
bssl::UniquePtr<SSL> server_ssl(SSL_new(server_ctx.get()));
ASSERT_TRUE(SSL_set_fd(server_ssl.get(), sockets[0]));
SSL_set_accept_state(server_ssl.get());

// Set up client
SSL_CTX_set_verify(client_ctx.get(), SSL_VERIFY_NONE, nullptr);
bssl::UniquePtr<SSL> client_ssl(SSL_new(client_ctx.get()));
ASSERT_TRUE(SSL_set_fd(client_ssl.get(), sockets[1]));
SSL_set_connect_state(client_ssl.get());

// Handshake will fail because of the exception in the callback
EXPECT_THROW(
CompleteHandshakes(client_ssl.get(), server_ssl.get()),
std::runtime_error
);
}


/**
* Test that setting a TLS alert and returning ssl_verify_invalid, from a
* callback installed via SSL_CTX_set_custom_verify(), results in a handshake
Expand Down