Add new functions for configuring the client CA list.
This is needed to switch Chromium's SSLServerSocket and parts of
Conscrypt to CRYPTO_BUFFER.
Bug: 54
Change-Id: Iacd417970607bc1a162057676b576956a3bdfa3f
Reviewed-on: https://boringssl-review.googlesource.com/17965
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/pool.h b/include/openssl/pool.h
index 8a07af5..4972b93 100644
--- a/include/openssl/pool.h
+++ b/include/openssl/pool.h
@@ -82,6 +82,8 @@
BORINGSSL_MAKE_DELETER(CRYPTO_BUFFER_POOL, CRYPTO_BUFFER_POOL_free)
BORINGSSL_MAKE_DELETER(CRYPTO_BUFFER, CRYPTO_BUFFER_free)
+BORINGSSL_MAKE_STACK_DELETER(CRYPTO_BUFFER, CRYPTO_BUFFER_free)
+
} // namespace bssl
} /* extern C++ */
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 328ec1c..cee90a1 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -2440,6 +2440,18 @@
OPENSSL_EXPORT void SSL_CTX_set_client_CA_list(SSL_CTX *ctx,
STACK_OF(X509_NAME) *name_list);
+/* SSL_set0_client_CAs sets |ssl|'s client certificate CA list to |name_list|,
+ * which should contain DER-encoded distinguished names (RFC 5280). It takes
+ * ownership of |name_list|. */
+OPENSSL_EXPORT void SSL_set0_client_CAs(SSL *ssl,
+ STACK_OF(CRYPTO_BUFFER) *name_list);
+
+/* SSL_CTX_set0_client_CAs sets |ctx|'s client certificate CA list to
+ * |name_list|, which should contain DER-encoded distinguished names (RFC 5280).
+ * It takes ownership of |name_list|. */
+OPENSSL_EXPORT void SSL_CTX_set0_client_CAs(SSL_CTX *ctx,
+ STACK_OF(CRYPTO_BUFFER) *name_list);
+
/* SSL_get_client_CA_list returns |ssl|'s client certificate CA list. If |ssl|
* has not been configured as a client, this is the list configured by
* |SSL_CTX_set_client_CA_list|.
diff --git a/ssl/ssl_cert.cc b/ssl/ssl_cert.cc
index a410aef..a9f334e 100644
--- a/ssl/ssl_cert.cc
+++ b/ssl/ssl_cert.cc
@@ -904,3 +904,15 @@
ssl->cert->ocsp_response = CRYPTO_BUFFER_new(response, response_len, NULL);
return ssl->cert->ocsp_response != NULL;
}
+
+void SSL_CTX_set0_client_CAs(SSL_CTX *ctx, STACK_OF(CRYPTO_BUFFER) *name_list) {
+ ctx->x509_method->ssl_ctx_flush_cached_client_CA(ctx);
+ sk_CRYPTO_BUFFER_pop_free(ctx->client_CA, CRYPTO_BUFFER_free);
+ ctx->client_CA = name_list;
+}
+
+void SSL_set0_client_CAs(SSL *ssl, STACK_OF(CRYPTO_BUFFER) *name_list) {
+ ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl);
+ sk_CRYPTO_BUFFER_pop_free(ssl->client_CA, CRYPTO_BUFFER_free);
+ ssl->client_CA = name_list;
+}
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 640718a..bf546da 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -1293,6 +1293,15 @@
PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
}
+static const uint8_t kTestName[] = {
+ 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49,
+ 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
+ 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64,
+};
+
static bool CompleteHandshakes(SSL *client, SSL *server) {
// Drive both their handshakes to completion.
for (;;) {
@@ -3288,6 +3297,70 @@
nullptr /* no session */));
}
+TEST(SSLTest, ClientCABuffers) {
+ 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<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_CTX_set_chain_and_key(server_ctx.get(), &chain[0],
+ chain.size(), key.get(), nullptr));
+
+ bssl::UniquePtr<CRYPTO_BUFFER> ca_name(
+ CRYPTO_BUFFER_new(kTestName, sizeof(kTestName), nullptr));
+ ASSERT_TRUE(ca_name);
+ bssl::UniquePtr<STACK_OF(CRYPTO_BUFFER)> ca_names(
+ sk_CRYPTO_BUFFER_new_null());
+ ASSERT_TRUE(ca_names);
+ ASSERT_TRUE(sk_CRYPTO_BUFFER_push(ca_names.get(), ca_name.get()));
+ ca_name.release();
+ SSL_CTX_set0_client_CAs(server_ctx.get(), ca_names.release());
+
+ // Configure client and server to accept all certificates.
+ SSL_CTX_set_custom_verify(
+ client_ctx.get(), SSL_VERIFY_PEER,
+ [](SSL *ssl, uint8_t *out_alert) -> ssl_verify_result_t {
+ return ssl_verify_ok;
+ });
+ SSL_CTX_set_custom_verify(
+ server_ctx.get(), SSL_VERIFY_PEER,
+ [](SSL *ssl, uint8_t *out_alert) -> ssl_verify_result_t {
+ return ssl_verify_ok;
+ });
+
+ bool cert_cb_called = false;
+ SSL_CTX_set_cert_cb(
+ client_ctx.get(),
+ [](SSL *ssl, void *arg) -> int {
+ STACK_OF(CRYPTO_BUFFER) *peer_names =
+ SSL_get0_server_requested_CAs(ssl);
+ EXPECT_EQ(1u, sk_CRYPTO_BUFFER_num(peer_names));
+ CRYPTO_BUFFER *peer_name = sk_CRYPTO_BUFFER_value(peer_names, 0);
+ EXPECT_EQ(Bytes(kTestName), Bytes(CRYPTO_BUFFER_data(peer_name),
+ CRYPTO_BUFFER_len(peer_name)));
+ *reinterpret_cast<bool *>(arg) = true;
+ return 1;
+ },
+ &cert_cb_called);
+
+ bssl::UniquePtr<SSL> client, server;
+ ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get(),
+ nullptr /* no session */));
+ EXPECT_TRUE(cert_cb_called);
+}
+
// Configuring the empty cipher list, though an error, should still modify the
// configuration.
TEST(SSLTest, EmptyCipherList) {