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(