Add SSL_get0_chain method Change-Id: I010f4dcf4d284e09326611c346369f74f65519cd Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66087 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 2e19253..6b366bb 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h
@@ -1154,12 +1154,24 @@ // 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/ssl/ssl_cert.cc b/ssl/ssl_cert.cc index aa46a8b..4be3481 100644 --- a/ssl/ssl_cert.cc +++ b/ssl/ssl_cert.cc
@@ -891,6 +891,13 @@ 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<CRYPTO_BUFFER> buffer(CRYPTO_BUFFER_new(der, der_len, NULL));
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 377d0dc..404e38d 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc
@@ -4550,12 +4550,12 @@ }; // 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<SSL_CTX> client_ctx(SSL_CTX_new(TLS_with_buffers_method())); ASSERT_TRUE(client_ctx); bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_with_buffers_method())); @@ -4573,7 +4573,7 @@ std::vector<CRYPTO_BUFFER*> 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 @@ server_ctx.get())); } +TEST(SSLTest, SetChainAndKeySSL) { + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_with_buffers_method())); + ASSERT_TRUE(client_ctx); + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_with_buffers_method())); + ASSERT_TRUE(server_ctx); + + bssl::UniquePtr<SSL> 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<EVP_PKEY> key = GetChainTestKey(); + ASSERT_TRUE(key); + bssl::UniquePtr<CRYPTO_BUFFER> leaf = GetChainTestCertificateBuffer(); + ASSERT_TRUE(leaf); + bssl::UniquePtr<CRYPTO_BUFFER> intermediate = + GetChainTestIntermediateBuffer(); + ASSERT_TRUE(intermediate); + std::vector<CRYPTO_BUFFER*> 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<SSL_CTX> client_ctx(SSL_CTX_new(TLS_with_buffers_method())); ASSERT_TRUE(client_ctx); @@ -4601,7 +4644,7 @@ bssl::UniquePtr<CRYPTO_BUFFER> leaf = GetChainTestCertificateBuffer(); ASSERT_TRUE(leaf); std::vector<CRYPTO_BUFFER*> 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 @@ bssl::UniquePtr<CRYPTO_BUFFER> leaf = GetChainTestCertificateBuffer(); ASSERT_TRUE(leaf); std::vector<CRYPTO_BUFFER*> 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 @@ 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<CRYPTO_BUFFER> ca_name(