diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h index 2e19253c2..6b366bbde 100644 --- a/src/include/openssl/ssl.h +++ b/src/include/openssl/ssl.h @@ -1154,12 +1154,24 @@ OPENSSL_EXPORT int SSL_set_chain_and_key( // the return value is undefined and, even if not NULL, the stack itself may // contain nullptrs. Thus you shouldn't mix this function with // non-|CRYPTO_BUFFER| functions for manipulating the chain.) -// -// There is no |SSL*| version of this function because connections discard -// configuration after handshaking, thus making it of questionable utility. OPENSSL_EXPORT const STACK_OF(CRYPTO_BUFFER)* SSL_CTX_get0_chain(const SSL_CTX *ctx); +// SSL_get0_chain returns the list of |CRYPTO_BUFFER|s that were set by +// |SSL_set_chain_and_key|, unless they have been discarded. Reference counts +// are not incremented by this call. The return value may be |NULL| if no chain +// has been set. +// +// (Note: if a chain was configured by non-|CRYPTO_BUFFER|-based functions then +// the return value is undefined and, even if not NULL, the stack itself may +// contain nullptrs. Thus you shouldn't mix this function with +// non-|CRYPTO_BUFFER| functions for manipulating the chain.) +// +// This function may return nullptr if a handshake has completed even if +// |SSL_set_chain_and_key| was previously called, since the configuration +// containing the certificates is typically cleared after handshake completion. +OPENSSL_EXPORT const STACK_OF(CRYPTO_BUFFER) *SSL_get0_chain(const SSL *ssl); + // SSL_CTX_use_RSAPrivateKey sets |ctx|'s private key to |rsa|. It returns one // on success and zero on failure. OPENSSL_EXPORT int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); diff --git a/src/ssl/ssl_cert.cc b/src/ssl/ssl_cert.cc index aa46a8bb6..4be3481ab 100644 --- a/src/ssl/ssl_cert.cc +++ b/src/ssl/ssl_cert.cc @@ -891,6 +891,13 @@ const STACK_OF(CRYPTO_BUFFER)* SSL_CTX_get0_chain(const SSL_CTX *ctx) { return ctx->cert->chain.get(); } +const STACK_OF(CRYPTO_BUFFER) * SSL_get0_chain(const SSL *ssl) { + if (!ssl->config) { + return nullptr; + } + return ssl->config->cert->chain.get(); +} + int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, size_t der_len, const uint8_t *der) { UniquePtr buffer(CRYPTO_BUFFER_new(der, der_len, NULL)); diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc index 377d0dc2a..404e38d91 100644 --- a/src/ssl/ssl_test.cc +++ b/src/ssl/ssl_test.cc @@ -4550,12 +4550,12 @@ TEST(SSLTest, SetChainAndKeyMismatch) { }; // Should fail because |GetTestKey| doesn't match the chain-test certificate. - ASSERT_FALSE(SSL_CTX_set_chain_and_key(ctx.get(), &chain[0], chain.size(), + ASSERT_FALSE(SSL_CTX_set_chain_and_key(ctx.get(), chain.data(), chain.size(), key.get(), nullptr)); ERR_clear_error(); } -TEST(SSLTest, SetChainAndKey) { +TEST(SSLTest, SetChainAndKeyCtx) { bssl::UniquePtr client_ctx(SSL_CTX_new(TLS_with_buffers_method())); ASSERT_TRUE(client_ctx); bssl::UniquePtr server_ctx(SSL_CTX_new(TLS_with_buffers_method())); @@ -4573,7 +4573,7 @@ TEST(SSLTest, SetChainAndKey) { std::vector chain = { leaf.get(), intermediate.get(), }; - ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), &chain[0], + ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), chain.data(), chain.size(), key.get(), nullptr)); ASSERT_EQ(chain.size(), @@ -4590,6 +4590,49 @@ TEST(SSLTest, SetChainAndKey) { server_ctx.get())); } +TEST(SSLTest, SetChainAndKeySSL) { + bssl::UniquePtr client_ctx(SSL_CTX_new(TLS_with_buffers_method())); + ASSERT_TRUE(client_ctx); + bssl::UniquePtr server_ctx(SSL_CTX_new(TLS_with_buffers_method())); + ASSERT_TRUE(server_ctx); + + bssl::UniquePtr client, server; + ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get())); + SSL_set_shed_handshake_config(client.get(), true); + SSL_set_shed_handshake_config(server.get(), true); + + ASSERT_EQ(nullptr, SSL_get0_chain(server.get())); + + bssl::UniquePtr key = GetChainTestKey(); + ASSERT_TRUE(key); + bssl::UniquePtr leaf = GetChainTestCertificateBuffer(); + ASSERT_TRUE(leaf); + bssl::UniquePtr intermediate = + GetChainTestIntermediateBuffer(); + ASSERT_TRUE(intermediate); + std::vector chain = { + leaf.get(), intermediate.get(), + }; + ASSERT_TRUE(SSL_set_chain_and_key(server.get(), chain.data(), + chain.size(), key.get(), nullptr)); + + ASSERT_EQ(chain.size(), + sk_CRYPTO_BUFFER_num(SSL_get0_chain(server.get()))); + + SSL_set_custom_verify( + client.get(), SSL_VERIFY_PEER, + [](SSL *ssl, uint8_t *out_alert) -> ssl_verify_result_t { + return ssl_verify_ok; + }); + + ASSERT_TRUE(CompleteHandshakes(client.get(), server.get())); + + // The server is configured to shed handshake config, so the certificate is no + // longer available after the handshake. + ASSERT_EQ(nullptr, SSL_get0_chain(server.get())); +} + TEST(SSLTest, BuffersFailWithoutCustomVerify) { bssl::UniquePtr client_ctx(SSL_CTX_new(TLS_with_buffers_method())); ASSERT_TRUE(client_ctx); @@ -4601,7 +4644,7 @@ TEST(SSLTest, BuffersFailWithoutCustomVerify) { bssl::UniquePtr leaf = GetChainTestCertificateBuffer(); ASSERT_TRUE(leaf); std::vector chain = { leaf.get() }; - ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), &chain[0], + ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), chain.data(), chain.size(), key.get(), nullptr)); // Without SSL_CTX_set_custom_verify(), i.e. with everything in the default @@ -4631,7 +4674,7 @@ TEST(SSLTest, CustomVerify) { bssl::UniquePtr leaf = GetChainTestCertificateBuffer(); ASSERT_TRUE(leaf); std::vector chain = { leaf.get() }; - ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), &chain[0], + ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), chain.data(), chain.size(), key.get(), nullptr)); SSL_CTX_set_custom_verify( @@ -4684,7 +4727,7 @@ TEST(SSLTest, ClientCABuffers) { leaf.get(), intermediate.get(), }; - ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), &chain[0], + ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), chain.data(), chain.size(), key.get(), nullptr)); bssl::UniquePtr ca_name(