Hold CA names as |CRYPTO_BUFFER|s.
This change converts the CA names that are parsed from a server's
CertificateRequest, as well as the CA names that are configured for
sending to clients in the same, to use |CRYPTO_BUFFER|.
The |X509_NAME|-based interfaces are turned into compatibility wrappers.
Change-Id: I95304ecc988ee39320499739a0866c7f8ff5ed98
Reviewed-on: https://boringssl-review.googlesource.com/13585
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/ssl_x509.c b/ssl/ssl_x509.c
index 2955c21..8a132ff 100644
--- a/ssl/ssl_x509.c
+++ b/ssl/ssl_x509.c
@@ -152,6 +152,7 @@
#include <openssl/x509_vfy.h>
#include "internal.h"
+#include "../crypto/internal.h"
X509 *SSL_get_peer_certificate(const SSL *ssl) {
@@ -410,6 +411,21 @@
session->x509_chain_without_leaf = NULL;
}
+static void ssl_crypto_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {
+ sk_X509_NAME_pop_free(hs->cached_x509_ca_names, X509_NAME_free);
+ hs->cached_x509_ca_names = NULL;
+}
+
+static void ssl_crypto_x509_ssl_flush_cached_client_CA(SSL *ssl) {
+ sk_X509_NAME_pop_free(ssl->cached_x509_client_CA, X509_NAME_free);
+ ssl->cached_x509_client_CA = NULL;
+}
+
+static void ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {
+ sk_X509_NAME_pop_free(ctx->cached_x509_client_CA, X509_NAME_free);
+ ctx->cached_x509_client_CA = NULL;
+}
+
const SSL_X509_METHOD ssl_crypto_x509_method = {
ssl_crypto_x509_clear,
ssl_crypto_x509_flush_cached_chain,
@@ -417,6 +433,9 @@
ssl_crypto_x509_session_cache_objects,
ssl_crypto_x509_session_dup,
ssl_crypto_x509_session_clear,
+ ssl_crypto_x509_hs_flush_cached_ca_names,
+ ssl_crypto_x509_ssl_flush_cached_client_CA,
+ ssl_crypto_x509_ssl_ctx_flush_cached_client_CA,
};
/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
@@ -491,7 +510,10 @@
}
X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
- return ssl_cert_get0_leaf(ctx->cert);
+ CRYPTO_MUTEX_lock_write((CRYPTO_MUTEX *) &ctx->lock);
+ X509 *ret = ssl_cert_get0_leaf(ctx->cert);
+ CRYPTO_MUTEX_unlock_write((CRYPTO_MUTEX *) &ctx->lock);
+ return ret;
}
/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
@@ -752,7 +774,11 @@
}
int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
- if (!ssl_cert_cache_chain_certs(ctx->cert)) {
+ CRYPTO_MUTEX_lock_write((CRYPTO_MUTEX *) &ctx->lock);
+ const int ret = ssl_cert_cache_chain_certs(ctx->cert);
+ CRYPTO_MUTEX_unlock_write((CRYPTO_MUTEX *) &ctx->lock);
+
+ if (!ret) {
*out_chain = NULL;
return 0;
}
@@ -813,3 +839,179 @@
*pp = CBS_data(&cbs);
return ret;
}
+
+STACK_OF(X509_NAME) *SSL_dup_CA_list(STACK_OF(X509_NAME) *list) {
+ return sk_X509_NAME_deep_copy(list, X509_NAME_dup, X509_NAME_free);
+}
+
+static void set_client_CA_list(STACK_OF(CRYPTO_BUFFER) **ca_list,
+ const STACK_OF(X509_NAME) *name_list,
+ CRYPTO_BUFFER_POOL *pool) {
+ STACK_OF(CRYPTO_BUFFER) *buffers = sk_CRYPTO_BUFFER_new_null();
+ if (buffers == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sk_X509_NAME_num(name_list); i++) {
+ X509_NAME *name = sk_X509_NAME_value(name_list, i);
+ uint8_t *outp = NULL;
+ int len = i2d_X509_NAME(name, &outp);
+ if (len < 0) {
+ goto err;
+ }
+
+ CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(outp, len, pool);
+ OPENSSL_free(outp);
+ if (buffer == NULL ||
+ !sk_CRYPTO_BUFFER_push(buffers, buffer)) {
+ CRYPTO_BUFFER_free(buffer);
+ goto err;
+ }
+ }
+
+ sk_CRYPTO_BUFFER_pop_free(*ca_list, CRYPTO_BUFFER_free);
+ *ca_list = buffers;
+ return;
+
+err:
+ sk_CRYPTO_BUFFER_pop_free(buffers, CRYPTO_BUFFER_free);
+}
+
+void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list) {
+ ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl);
+ set_client_CA_list(&ssl->client_CA, name_list, ssl->ctx->pool);
+ sk_X509_NAME_pop_free(name_list, X509_NAME_free);
+}
+
+void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list) {
+ ctx->x509_method->ssl_ctx_flush_cached_client_CA(ctx);
+ set_client_CA_list(&ctx->client_CA, name_list, ctx->pool);
+ sk_X509_NAME_pop_free(name_list, X509_NAME_free);
+}
+
+static STACK_OF(X509_NAME) *
+ buffer_names_to_x509(const STACK_OF(CRYPTO_BUFFER) *names,
+ STACK_OF(X509_NAME) **cached) {
+ if (names == NULL) {
+ return NULL;
+ }
+
+ if (*cached != NULL) {
+ return *cached;
+ }
+
+ STACK_OF(X509_NAME) *new_cache = sk_X509_NAME_new_null();
+ if (new_cache == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+
+ for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(names); i++) {
+ const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(names, i);
+ const uint8_t *inp = CRYPTO_BUFFER_data(buffer);
+ X509_NAME *name = d2i_X509_NAME(NULL, &inp, CRYPTO_BUFFER_len(buffer));
+ if (name == NULL ||
+ inp != CRYPTO_BUFFER_data(buffer) + CRYPTO_BUFFER_len(buffer) ||
+ !sk_X509_NAME_push(new_cache, name)) {
+ X509_NAME_free(name);
+ goto err;
+ }
+ }
+
+ *cached = new_cache;
+ return new_cache;
+
+err:
+ sk_X509_NAME_pop_free(new_cache, X509_NAME_free);
+ return NULL;
+}
+
+STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl) {
+ /* For historical reasons, this function is used both to query configuration
+ * state on a server as well as handshake state on a client. However, whether
+ * |ssl| is a client or server is not known until explicitly configured with
+ * |SSL_set_connect_state|. If |handshake_func| is NULL, |ssl| is in an
+ * indeterminate mode and |ssl->server| is unset. */
+ if (ssl->handshake_func != NULL && !ssl->server) {
+ if (ssl->s3->hs != NULL) {
+ return buffer_names_to_x509(ssl->s3->hs->ca_names,
+ &ssl->s3->hs->cached_x509_ca_names);
+ }
+
+ return NULL;
+ }
+
+ if (ssl->client_CA != NULL) {
+ return buffer_names_to_x509(
+ ssl->client_CA, (STACK_OF(X509_NAME) **)&ssl->cached_x509_client_CA);
+ }
+ return buffer_names_to_x509(ssl->ctx->client_CA,
+ &ssl->ctx->cached_x509_client_CA);
+}
+
+STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) {
+ CRYPTO_MUTEX_lock_write((CRYPTO_MUTEX *) &ctx->lock);
+ STACK_OF(X509_NAME) *ret = buffer_names_to_x509(
+ ctx->client_CA, (STACK_OF(X509_NAME) **)&ctx->cached_x509_client_CA);
+ CRYPTO_MUTEX_unlock_write((CRYPTO_MUTEX *) &ctx->lock);
+ return ret;
+}
+
+static int add_client_CA(STACK_OF(CRYPTO_BUFFER) **names, X509 *x509,
+ CRYPTO_BUFFER_POOL *pool) {
+ if (x509 == NULL) {
+ return 0;
+ }
+
+ uint8_t *outp = NULL;
+ int len = i2d_X509_NAME(X509_get_subject_name(x509), &outp);
+ if (len < 0) {
+ return 0;
+ }
+
+ CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(outp, len, pool);
+ OPENSSL_free(outp);
+ if (buffer == NULL) {
+ return 0;
+ }
+
+ int alloced = 0;
+ if (*names == NULL) {
+ *names = sk_CRYPTO_BUFFER_new_null();
+ alloced = 1;
+
+ if (*names == NULL) {
+ CRYPTO_BUFFER_free(buffer);
+ return 0;
+ }
+ }
+
+ if (!sk_CRYPTO_BUFFER_push(*names, buffer)) {
+ CRYPTO_BUFFER_free(buffer);
+ if (alloced) {
+ sk_CRYPTO_BUFFER_pop_free(*names, CRYPTO_BUFFER_free);
+ *names = NULL;
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+int SSL_add_client_CA(SSL *ssl, X509 *x509) {
+ if (!add_client_CA(&ssl->client_CA, x509, ssl->ctx->pool)) {
+ return 0;
+ }
+
+ ssl_crypto_x509_ssl_flush_cached_client_CA(ssl);
+ return 1;
+}
+
+int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x509) {
+ if (!add_client_CA(&ctx->client_CA, x509, ctx->pool)) {
+ return 0;
+ }
+
+ ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(ctx);
+ return 1;
+}