Add |X509_METHOD| and, using it, move many functions to ssl_x509.c.
Change-Id: I266af0c2bdcebcc1dd1026f816b9ef6ece5a592f
Reviewed-on: https://boringssl-review.googlesource.com/13581
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/dtls_method.c b/ssl/dtls_method.c
index 7a35b39..6084789 100644
--- a/ssl/dtls_method.c
+++ b/ssl/dtls_method.c
@@ -165,6 +165,7 @@
static const SSL_METHOD kMethod = {
0,
&kDTLSProtocolMethod,
+ &ssl_crypto_x509_method,
};
return &kMethod;
}
@@ -175,6 +176,7 @@
static const SSL_METHOD kMethod = {
DTLS1_2_VERSION,
&kDTLSProtocolMethod,
+ &ssl_crypto_x509_method,
};
return &kMethod;
}
@@ -183,6 +185,7 @@
static const SSL_METHOD kMethod = {
DTLS1_VERSION,
&kDTLSProtocolMethod,
+ &ssl_crypto_x509_method,
};
return &kMethod;
}
diff --git a/ssl/internal.h b/ssl/internal.h
index b3f3b0f..a53bd07 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1256,6 +1256,10 @@
* operations. */
const SSL_PRIVATE_KEY_METHOD *key_method;
+ /* x509_method contains pointers to functions that might deal with |X509|
+ * compatibility, or might be a no-op, depending on the application. */
+ const SSL_X509_METHOD *x509_method;
+
DH *dh_tmp;
DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize);
@@ -1287,6 +1291,9 @@
/* method is the underlying SSL_PROTOCOL_METHOD that initializes the
* SSL_CTX. */
const SSL_PROTOCOL_METHOD *method;
+ /* x509_method contains pointers to functions that might deal with |X509|
+ * compatibility, or might be a no-op, depending on the application. */
+ const SSL_X509_METHOD *x509_method;
};
/* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
@@ -1368,6 +1375,21 @@
int (*set_write_state)(SSL *ssl, SSL_AEAD_CTX *aead_ctx);
};
+struct ssl_x509_method_st {
+ /* cert_clear frees and NULLs all X509-related state. */
+ void (*cert_clear)(CERT *cert);
+ /* cert_flush_cached_chain drops any cached |X509|-based certificate chain
+ * from |cert|. */
+ void (*cert_flush_cached_chain)(CERT *cert);
+ /* cert_flush_cached_chain drops any cached |X509|-based leaf certificate
+ * from |cert|. */
+ void (*cert_flush_cached_leaf)(CERT *cert);
+};
+
+/* ssl_crypto_x509_method provides the |ssl_x509_method_st| functions using
+ * crypto/x509. */
+extern const struct ssl_x509_method_st ssl_crypto_x509_method;
+
/* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit
* of a mess of functions, but hell, think of it as an opaque structure. */
typedef struct ssl3_enc_method {
@@ -1855,13 +1877,11 @@
#define SSL_KEY_UPDATE_NOT_REQUESTED 0
#define SSL_KEY_UPDATE_REQUESTED 1
-CERT *ssl_cert_new(void);
+CERT *ssl_cert_new(const SSL_X509_METHOD *x509_method);
CERT *ssl_cert_dup(CERT *cert);
void ssl_cert_clear_certs(CERT *c);
void ssl_cert_free(CERT *c);
-CRYPTO_BUFFER *x509_to_buffer(X509 *x509);
-void ssl_cert_flush_cached_x509_leaf(CERT *cert);
-int ssl_cert_cache_leaf_cert(CERT *cert);
+int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer);
/* ssl_compare_public_and_private_key returns one if |pubkey| is the public
* counterpart to |privkey|. Otherwise it returns zero and pushes a helpful
* message on the error queue. */
@@ -1931,7 +1951,7 @@
const SSL *ssl);
int ssl_verify_cert_chain(SSL *ssl, long *out_verify_result,
- STACK_OF(X509) * cert_chain);
+ STACK_OF(X509) *cert_chain);
void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
int ssl_verify_alarm_type(long type);
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 5c5fc54..1a79327 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -141,13 +141,14 @@
return 0;
}
-CERT *ssl_cert_new(void) {
+CERT *ssl_cert_new(const SSL_X509_METHOD *x509_method) {
CERT *ret = OPENSSL_malloc(sizeof(CERT));
if (ret == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return NULL;
}
OPENSSL_memset(ret, 0, sizeof(CERT));
+ ret->x509_method = x509_method;
return ret;
}
@@ -174,6 +175,7 @@
}
ret->key_method = cert->key_method;
+ ret->x509_method = cert->x509_method;
if (cert->dh_tmp != NULL) {
ret->dh_tmp = DHparams_dup(cert->dh_tmp);
@@ -208,27 +210,13 @@
return NULL;
}
-void ssl_cert_flush_cached_x509_leaf(CERT *cert) {
- X509_free(cert->x509_leaf);
- cert->x509_leaf = NULL;
-}
-
-static void ssl_cert_flush_cached_x509_chain(CERT *cert) {
- sk_X509_pop_free(cert->x509_chain, X509_free);
- cert->x509_chain = NULL;
-}
-
/* Free up and clear all certificates and chains */
void ssl_cert_clear_certs(CERT *cert) {
if (cert == NULL) {
return;
}
- ssl_cert_flush_cached_x509_leaf(cert);
- ssl_cert_flush_cached_x509_chain(cert);
-
- X509_free(cert->x509_stash);
- cert->x509_stash = NULL;
+ cert->x509_method->cert_clear(cert);
sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
cert->chain = NULL;
@@ -251,151 +239,6 @@
OPENSSL_free(c);
}
-/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
-static STACK_OF(CRYPTO_BUFFER) *new_leafless_chain(void) {
- STACK_OF(CRYPTO_BUFFER) *chain = sk_CRYPTO_BUFFER_new_null();
- if (chain == NULL) {
- return NULL;
- }
-
- if (!sk_CRYPTO_BUFFER_push(chain, NULL)) {
- sk_CRYPTO_BUFFER_free(chain);
- return NULL;
- }
-
- return chain;
-}
-
-/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
- * contents of |x509|. */
-CRYPTO_BUFFER *x509_to_buffer(X509 *x509) {
- uint8_t *buf = NULL;
- int cert_len = i2d_X509(x509, &buf);
- if (cert_len <= 0) {
- return 0;
- }
-
- CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(buf, cert_len, NULL);
- OPENSSL_free(buf);
-
- return buffer;
-}
-
-/* ssl_cert_set_chain sets elements 1.. of |cert->chain| to the serialised
- * forms of elements of |chain|. It returns one on success or zero on error, in
- * which case no change to |cert->chain| is made. It preverses the existing
- * leaf from |cert->chain|, if any. */
-static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
- STACK_OF(CRYPTO_BUFFER) *new_chain = NULL;
-
- if (cert->chain != NULL) {
- new_chain = sk_CRYPTO_BUFFER_new_null();
- if (new_chain == NULL) {
- return 0;
- }
-
- CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
- if (!sk_CRYPTO_BUFFER_push(new_chain, leaf)) {
- goto err;
- }
- /* |leaf| might be NULL if it's a “leafless” chain. */
- if (leaf != NULL) {
- CRYPTO_BUFFER_up_ref(leaf);
- }
- }
-
- for (size_t i = 0; i < sk_X509_num(chain); i++) {
- if (new_chain == NULL) {
- new_chain = new_leafless_chain();
- if (new_chain == NULL) {
- goto err;
- }
- }
-
- CRYPTO_BUFFER *buffer = x509_to_buffer(sk_X509_value(chain, i));
- if (buffer == NULL ||
- !sk_CRYPTO_BUFFER_push(new_chain, buffer)) {
- CRYPTO_BUFFER_free(buffer);
- goto err;
- }
- }
-
- sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
- cert->chain = new_chain;
-
- return 1;
-
-err:
- sk_CRYPTO_BUFFER_pop_free(new_chain, CRYPTO_BUFFER_free);
- return 0;
-}
-
-static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
- if (!ssl_cert_set_chain(cert, chain)) {
- return 0;
- }
-
- sk_X509_pop_free(chain, X509_free);
- ssl_cert_flush_cached_x509_chain(cert);
- return 1;
-}
-
-static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
- if (!ssl_cert_set_chain(cert, chain)) {
- return 0;
- }
-
- ssl_cert_flush_cached_x509_chain(cert);
- return 1;
-}
-
-static int ssl_cert_append_cert(CERT *cert, X509 *x509) {
- CRYPTO_BUFFER *buffer = x509_to_buffer(x509);
- if (buffer == NULL) {
- return 0;
- }
-
- if (cert->chain != NULL) {
- if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
- CRYPTO_BUFFER_free(buffer);
- return 0;
- }
-
- return 1;
- }
-
- cert->chain = new_leafless_chain();
- if (cert->chain == NULL ||
- !sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
- CRYPTO_BUFFER_free(buffer);
- sk_CRYPTO_BUFFER_free(cert->chain);
- cert->chain = NULL;
- return 0;
- }
-
- return 1;
-}
-
-static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
- if (!ssl_cert_append_cert(cert, x509)) {
- return 0;
- }
-
- X509_free(cert->x509_stash);
- cert->x509_stash = x509;
- ssl_cert_flush_cached_x509_chain(cert);
- return 1;
-}
-
-static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
- if (!ssl_cert_append_cert(cert, x509)) {
- return 0;
- }
-
- ssl_cert_flush_cached_x509_chain(cert);
- return 1;
-}
-
static void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg),
void *arg) {
c->cert_cb = cb;
@@ -653,49 +496,6 @@
return 0;
}
-int ssl_auto_chain_if_needed(SSL *ssl) {
- /* Only build a chain if there are no intermediates configured and the feature
- * isn't disabled. */
- if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
- !ssl_has_certificate(ssl) ||
- ssl->cert->chain == NULL ||
- sk_CRYPTO_BUFFER_num(ssl->cert->chain) > 1) {
- return 1;
- }
-
- X509 *leaf =
- X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0));
- if (!leaf) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
- return 0;
- }
-
- X509_STORE_CTX ctx;
- if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, NULL)) {
- X509_free(leaf);
- OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
- return 0;
- }
-
- /* Attempt to build a chain, ignoring the result. */
- X509_verify_cert(&ctx);
- X509_free(leaf);
- ERR_clear_error();
-
- /* Remove the leaf from the generated chain. */
- X509_free(sk_X509_shift(ctx.chain));
-
- const int ok = ssl_cert_set_chain(ssl->cert, ctx.chain);
- X509_STORE_CTX_cleanup(&ctx);
- if (!ok) {
- return 0;
- }
-
- ssl_cert_flush_cached_x509_chain(ssl->cert);
-
- return 1;
-}
-
/* ssl_cert_skip_to_spki parses a DER-encoded, X.509 certificate from |in| and
* positions |*out_tbs_cert| to cover the TBSCertificate, starting at the
* subjectPublicKeyInfo. */
@@ -987,54 +787,6 @@
return set_cert_store(&ssl->cert->verify_store, store, 1);
}
-int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
- return ssl_cert_set0_chain(ctx->cert, chain);
-}
-
-int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
- return ssl_cert_set1_chain(ctx->cert, chain);
-}
-
-int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) {
- return ssl_cert_set0_chain(ssl->cert, chain);
-}
-
-int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) {
- return ssl_cert_set1_chain(ssl->cert, chain);
-}
-
-int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) {
- return ssl_cert_add0_chain_cert(ctx->cert, x509);
-}
-
-int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) {
- return ssl_cert_add1_chain_cert(ctx->cert, x509);
-}
-
-int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) {
- return SSL_CTX_add0_chain_cert(ctx, x509);
-}
-
-int SSL_add0_chain_cert(SSL *ssl, X509 *x509) {
- return ssl_cert_add0_chain_cert(ssl->cert, x509);
-}
-
-int SSL_add1_chain_cert(SSL *ssl, X509 *x509) {
- return ssl_cert_add1_chain_cert(ssl->cert, x509);
-}
-
-int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) {
- return SSL_CTX_set0_chain(ctx, NULL);
-}
-
-int SSL_CTX_clear_extra_chain_certs(SSL_CTX *ctx) {
- return SSL_CTX_clear_chain_certs(ctx);
-}
-
-int SSL_clear_chain_certs(SSL *ssl) {
- return SSL_set0_chain(ssl, NULL);
-}
-
void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg),
void *arg) {
ssl_cert_set_cert_cb(ctx->cert, cb, arg);
@@ -1044,80 +796,6 @@
ssl_cert_set_cert_cb(ssl->cert, cb, arg);
}
-/* ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
- * first element of |cert->chain|. */
-int ssl_cert_cache_leaf_cert(CERT *cert) {
- if (cert->x509_leaf != NULL ||
- cert->chain == NULL) {
- return 1;
- }
-
- CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
- if (!leaf) {
- return 1;
- }
-
- cert->x509_leaf = X509_parse_from_buffer(leaf);
- return cert->x509_leaf != NULL;
-}
-
-/* ssl_cert_cache_chain_certs fills in |cert->x509_chain| from elements 1.. of
- * |cert->chain|. */
-static int ssl_cert_cache_chain_certs(CERT *cert) {
- if (cert->x509_chain != NULL ||
- cert->chain == NULL ||
- sk_CRYPTO_BUFFER_num(cert->chain) < 2) {
- return 1;
- }
-
- STACK_OF(X509) *chain = sk_X509_new_null();
- if (chain == NULL) {
- return 0;
- }
-
- for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
- CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(cert->chain, i);
- X509 *x509 = X509_parse_from_buffer(buffer);
- if (x509 == NULL ||
- !sk_X509_push(chain, x509)) {
- X509_free(x509);
- goto err;
- }
- }
-
- cert->x509_chain = chain;
- return 1;
-
-err:
- sk_X509_pop_free(chain, X509_free);
- return 0;
-}
-
-int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
- if (!ssl_cert_cache_chain_certs(ctx->cert)) {
- *out_chain = NULL;
- return 0;
- }
-
- *out_chain = ctx->cert->x509_chain;
- return 1;
-}
-
-int SSL_CTX_get_extra_chain_certs(const SSL_CTX *ctx,
- STACK_OF(X509) **out_chain) {
- return SSL_CTX_get0_chain_certs(ctx, out_chain);
-}
-
-int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
- if (!ssl_cert_cache_chain_certs(ssl->cert)) {
- *out_chain = NULL;
- return 0;
- }
-
- *out_chain = ssl->cert->x509_chain;
- return 1;
-}
-
int ssl_check_leaf_certificate(SSL *ssl, EVP_PKEY *pkey,
const CRYPTO_BUFFER *leaf) {
assert(ssl3_protocol_version(ssl) < TLS1_3_VERSION);
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 96ea64b..7fc7233 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -248,6 +248,7 @@
OPENSSL_memset(ret, 0, sizeof(SSL_CTX));
ret->method = method->method;
+ ret->x509_method = method->x509_method;
CRYPTO_MUTEX_init(&ret->lock);
@@ -261,7 +262,7 @@
ret->max_cert_list = SSL_MAX_CERT_LIST_DEFAULT;
ret->verify_mode = SSL_VERIFY_NONE;
- ret->cert = ssl_cert_new();
+ ret->cert = ssl_cert_new(method->x509_method);
if (ret->cert == NULL) {
goto err;
}
@@ -2058,6 +2059,12 @@
return ssl->ctx;
}
+ /* One cannot change the X.509 callbacks during a connection. */
+ if (ssl->ctx->x509_method != ctx->x509_method) {
+ assert(0);
+ return NULL;
+ }
+
if (ctx == NULL) {
ctx = ssl->initial_ctx;
}
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index 6ad2b71..f3e8bf3 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -70,29 +70,12 @@
#include "internal.h"
-static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer);
static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey);
static int is_key_type_supported(int key_type) {
return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC;
}
-int SSL_use_certificate(SSL *ssl, X509 *x) {
- if (x == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
- return 0;
- }
-
- CRYPTO_BUFFER *buffer = x509_to_buffer(x);
- if (buffer == NULL) {
- return 0;
- }
-
- const int ok = ssl_set_cert(ssl->cert, buffer);
- CRYPTO_BUFFER_free(buffer);
- return ok;
-}
-
int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
if (buffer == NULL) {
@@ -179,23 +162,7 @@
return ret;
}
-int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) {
- if (x == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
- return 0;
- }
-
- CRYPTO_BUFFER *buffer = x509_to_buffer(x);
- if (buffer == NULL) {
- return 0;
- }
-
- const int ok = ssl_set_cert(ctx->cert, buffer);
- CRYPTO_BUFFER_free(buffer);
- return ok;
-}
-
-static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
+int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
CBS cert_cbs;
CRYPTO_BUFFER_init_CBS(buffer, &cert_cbs);
EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs);
@@ -235,7 +202,7 @@
EVP_PKEY_free(pubkey);
- ssl_cert_flush_cached_x509_leaf(cert);
+ cert->x509_method->cert_flush_cached_leaf(cert);
if (cert->chain != NULL) {
CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_value(cert->chain, 0));
diff --git a/ssl/ssl_x509.c b/ssl/ssl_x509.c
index 5d78deb..9077b61 100644
--- a/ssl/ssl_x509.c
+++ b/ssl/ssl_x509.c
@@ -140,8 +140,11 @@
#include <openssl/ssl.h>
+#include <assert.h>
+
#include <openssl/stack.h>
#include <openssl/x509.h>
+#include <openssl/x509v3.h>
#include <openssl/x509_vfy.h>
#include "internal.h"
@@ -281,23 +284,6 @@
X509_VERIFY_PARAM_set_depth(ctx->param, depth);
}
-static X509 *ssl_cert_get0_leaf(CERT *cert) {
- if (cert->x509_leaf == NULL &&
- !ssl_cert_cache_leaf_cert(cert)) {
- return NULL;
- }
-
- return cert->x509_leaf;
-}
-
-X509 *SSL_get_certificate(const SSL *ssl) {
- return ssl_cert_get0_leaf(ssl->cert);
-}
-
-X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
- return ssl_cert_get0_leaf(ctx->cert);
-}
-
int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) {
return X509_STORE_set_default_paths(ctx->cert_store);
}
@@ -329,3 +315,384 @@
X509_STORE_free(ctx->cert_store);
ctx->cert_store = store;
}
+
+static void ssl_crypto_x509_flush_cached_leaf(CERT *cert) {
+ X509_free(cert->x509_leaf);
+ cert->x509_leaf = NULL;
+}
+
+static void ssl_crypto_x509_flush_cached_chain(CERT *cert) {
+ sk_X509_pop_free(cert->x509_chain, X509_free);
+ cert->x509_chain = NULL;
+}
+
+static void ssl_crypto_x509_clear(CERT *cert) {
+ ssl_crypto_x509_flush_cached_leaf(cert);
+ ssl_crypto_x509_flush_cached_chain(cert);
+
+ X509_free(cert->x509_stash);
+ cert->x509_stash = NULL;
+}
+
+const SSL_X509_METHOD ssl_crypto_x509_method = {
+ ssl_crypto_x509_clear,
+ ssl_crypto_x509_flush_cached_chain,
+ ssl_crypto_x509_flush_cached_leaf,
+};
+
+/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
+ * contents of |x509|. */
+static CRYPTO_BUFFER *x509_to_buffer(X509 *x509) {
+ uint8_t *buf = NULL;
+ int cert_len = i2d_X509(x509, &buf);
+ if (cert_len <= 0) {
+ return 0;
+ }
+
+ CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(buf, cert_len, NULL);
+ OPENSSL_free(buf);
+
+ return buffer;
+}
+
+static int ssl_use_certificate(CERT *cert, X509 *x) {
+ if (x == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ CRYPTO_BUFFER *buffer = x509_to_buffer(x);
+ if (buffer == NULL) {
+ return 0;
+ }
+
+ const int ok = ssl_set_cert(cert, buffer);
+ CRYPTO_BUFFER_free(buffer);
+ return ok;
+}
+
+int SSL_use_certificate(SSL *ssl, X509 *x) {
+ return ssl_use_certificate(ssl->cert, x);
+}
+
+int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) {
+ return ssl_use_certificate(ctx->cert, x);
+}
+
+/* ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
+ * first element of |cert->chain|. */
+static int ssl_cert_cache_leaf_cert(CERT *cert) {
+ assert(cert->x509_method);
+
+ if (cert->x509_leaf != NULL ||
+ cert->chain == NULL) {
+ return 1;
+ }
+
+ CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+ if (!leaf) {
+ return 1;
+ }
+
+ cert->x509_leaf = X509_parse_from_buffer(leaf);
+ return cert->x509_leaf != NULL;
+}
+
+static X509 *ssl_cert_get0_leaf(CERT *cert) {
+ if (cert->x509_leaf == NULL &&
+ !ssl_cert_cache_leaf_cert(cert)) {
+ return NULL;
+ }
+
+ return cert->x509_leaf;
+}
+
+X509 *SSL_get_certificate(const SSL *ssl) {
+ return ssl_cert_get0_leaf(ssl->cert);
+}
+
+X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
+ return ssl_cert_get0_leaf(ctx->cert);
+}
+
+/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
+static STACK_OF(CRYPTO_BUFFER) *new_leafless_chain(void) {
+ STACK_OF(CRYPTO_BUFFER) *chain = sk_CRYPTO_BUFFER_new_null();
+ if (chain == NULL) {
+ return NULL;
+ }
+
+ if (!sk_CRYPTO_BUFFER_push(chain, NULL)) {
+ sk_CRYPTO_BUFFER_free(chain);
+ return NULL;
+ }
+
+ return chain;
+}
+
+/* ssl_cert_set_chain sets elements 1.. of |cert->chain| to the serialised
+ * forms of elements of |chain|. It returns one on success or zero on error, in
+ * which case no change to |cert->chain| is made. It preverses the existing
+ * leaf from |cert->chain|, if any. */
+static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
+ STACK_OF(CRYPTO_BUFFER) *new_chain = NULL;
+
+ if (cert->chain != NULL) {
+ new_chain = sk_CRYPTO_BUFFER_new_null();
+ if (new_chain == NULL) {
+ return 0;
+ }
+
+ CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+ if (!sk_CRYPTO_BUFFER_push(new_chain, leaf)) {
+ goto err;
+ }
+ /* |leaf| might be NULL if it's a “leafless” chain. */
+ if (leaf != NULL) {
+ CRYPTO_BUFFER_up_ref(leaf);
+ }
+ }
+
+ for (size_t i = 0; i < sk_X509_num(chain); i++) {
+ if (new_chain == NULL) {
+ new_chain = new_leafless_chain();
+ if (new_chain == NULL) {
+ goto err;
+ }
+ }
+
+ CRYPTO_BUFFER *buffer = x509_to_buffer(sk_X509_value(chain, i));
+ if (buffer == NULL ||
+ !sk_CRYPTO_BUFFER_push(new_chain, buffer)) {
+ CRYPTO_BUFFER_free(buffer);
+ goto err;
+ }
+ }
+
+ sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
+ cert->chain = new_chain;
+
+ return 1;
+
+err:
+ sk_CRYPTO_BUFFER_pop_free(new_chain, CRYPTO_BUFFER_free);
+ return 0;
+}
+
+static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
+ if (!ssl_cert_set_chain(cert, chain)) {
+ return 0;
+ }
+
+ sk_X509_pop_free(chain, X509_free);
+ ssl_crypto_x509_flush_cached_chain(cert);
+ return 1;
+}
+
+static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
+ if (!ssl_cert_set_chain(cert, chain)) {
+ return 0;
+ }
+
+ ssl_crypto_x509_flush_cached_chain(cert);
+ return 1;
+}
+
+static int ssl_cert_append_cert(CERT *cert, X509 *x509) {
+ assert(cert->x509_method);
+
+ CRYPTO_BUFFER *buffer = x509_to_buffer(x509);
+ if (buffer == NULL) {
+ return 0;
+ }
+
+ if (cert->chain != NULL) {
+ if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+ CRYPTO_BUFFER_free(buffer);
+ return 0;
+ }
+
+ return 1;
+ }
+
+ cert->chain = new_leafless_chain();
+ if (cert->chain == NULL ||
+ !sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+ CRYPTO_BUFFER_free(buffer);
+ sk_CRYPTO_BUFFER_free(cert->chain);
+ cert->chain = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
+ if (!ssl_cert_append_cert(cert, x509)) {
+ return 0;
+ }
+
+ X509_free(cert->x509_stash);
+ cert->x509_stash = x509;
+ ssl_crypto_x509_flush_cached_chain(cert);
+ return 1;
+}
+
+static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
+ if (!ssl_cert_append_cert(cert, x509)) {
+ return 0;
+ }
+
+ ssl_crypto_x509_flush_cached_chain(cert);
+ return 1;
+}
+
+int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
+ return ssl_cert_set0_chain(ctx->cert, chain);
+}
+
+int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
+ return ssl_cert_set1_chain(ctx->cert, chain);
+}
+
+int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) {
+ return ssl_cert_set0_chain(ssl->cert, chain);
+}
+
+int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) {
+ return ssl_cert_set1_chain(ssl->cert, chain);
+}
+
+int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) {
+ return ssl_cert_add0_chain_cert(ctx->cert, x509);
+}
+
+int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) {
+ return ssl_cert_add1_chain_cert(ctx->cert, x509);
+}
+
+int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) {
+ return SSL_CTX_add0_chain_cert(ctx, x509);
+}
+
+int SSL_add0_chain_cert(SSL *ssl, X509 *x509) {
+ return ssl_cert_add0_chain_cert(ssl->cert, x509);
+}
+
+int SSL_add1_chain_cert(SSL *ssl, X509 *x509) {
+ return ssl_cert_add1_chain_cert(ssl->cert, x509);
+}
+
+int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) {
+ return SSL_CTX_set0_chain(ctx, NULL);
+}
+
+int SSL_CTX_clear_extra_chain_certs(SSL_CTX *ctx) {
+ return SSL_CTX_clear_chain_certs(ctx);
+}
+
+int SSL_clear_chain_certs(SSL *ssl) {
+ return SSL_set0_chain(ssl, NULL);
+}
+
+int ssl_auto_chain_if_needed(SSL *ssl) {
+ /* Only build a chain if there are no intermediates configured and the feature
+ * isn't disabled. */
+ if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
+ !ssl_has_certificate(ssl) ||
+ ssl->cert->chain == NULL ||
+ sk_CRYPTO_BUFFER_num(ssl->cert->chain) > 1) {
+ return 1;
+ }
+
+ X509 *leaf =
+ X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0));
+ if (!leaf) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
+ return 0;
+ }
+
+ X509_STORE_CTX ctx;
+ if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, NULL)) {
+ X509_free(leaf);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
+ return 0;
+ }
+
+ /* Attempt to build a chain, ignoring the result. */
+ X509_verify_cert(&ctx);
+ X509_free(leaf);
+ ERR_clear_error();
+
+ /* Remove the leaf from the generated chain. */
+ X509_free(sk_X509_shift(ctx.chain));
+
+ const int ok = ssl_cert_set_chain(ssl->cert, ctx.chain);
+ X509_STORE_CTX_cleanup(&ctx);
+ if (!ok) {
+ return 0;
+ }
+
+ ssl_crypto_x509_flush_cached_chain(ssl->cert);
+
+ return 1;
+}
+
+/* ssl_cert_cache_chain_certs fills in |cert->x509_chain| from elements 1.. of
+ * |cert->chain|. */
+static int ssl_cert_cache_chain_certs(CERT *cert) {
+ assert(cert->x509_method);
+
+ if (cert->x509_chain != NULL ||
+ cert->chain == NULL ||
+ sk_CRYPTO_BUFFER_num(cert->chain) < 2) {
+ return 1;
+ }
+
+ STACK_OF(X509) *chain = sk_X509_new_null();
+ if (chain == NULL) {
+ return 0;
+ }
+
+ for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
+ CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(cert->chain, i);
+ X509 *x509 = X509_parse_from_buffer(buffer);
+ if (x509 == NULL ||
+ !sk_X509_push(chain, x509)) {
+ X509_free(x509);
+ goto err;
+ }
+ }
+
+ cert->x509_chain = chain;
+ return 1;
+
+err:
+ sk_X509_pop_free(chain, X509_free);
+ return 0;
+}
+
+int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
+ if (!ssl_cert_cache_chain_certs(ctx->cert)) {
+ *out_chain = NULL;
+ return 0;
+ }
+
+ *out_chain = ctx->cert->x509_chain;
+ return 1;
+}
+
+int SSL_CTX_get_extra_chain_certs(const SSL_CTX *ctx,
+ STACK_OF(X509) **out_chain) {
+ return SSL_CTX_get0_chain_certs(ctx, out_chain);
+}
+
+int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
+ if (!ssl_cert_cache_chain_certs(ssl->cert)) {
+ *out_chain = NULL;
+ return 0;
+ }
+
+ *out_chain = ssl->cert->x509_chain;
+ return 1;
+}
diff --git a/ssl/tls_method.c b/ssl/tls_method.c
index 70683e4..5533dfd 100644
--- a/ssl/tls_method.c
+++ b/ssl/tls_method.c
@@ -161,6 +161,7 @@
static const SSL_METHOD kMethod = {
0,
&kTLSProtocolMethod,
+ &ssl_crypto_x509_method,
};
return &kMethod;
}
@@ -175,6 +176,7 @@
static const SSL_METHOD kMethod = {
TLS1_2_VERSION,
&kTLSProtocolMethod,
+ &ssl_crypto_x509_method,
};
return &kMethod;
}
@@ -183,6 +185,7 @@
static const SSL_METHOD kMethod = {
TLS1_1_VERSION,
&kTLSProtocolMethod,
+ &ssl_crypto_x509_method,
};
return &kMethod;
}
@@ -191,6 +194,7 @@
static const SSL_METHOD kMethod = {
TLS1_VERSION,
&kTLSProtocolMethod,
+ &ssl_crypto_x509_method,
};
return &kMethod;
}
@@ -199,6 +203,7 @@
static const SSL_METHOD kMethod = {
SSL3_VERSION,
&kTLSProtocolMethod,
+ &ssl_crypto_x509_method,
};
return &kMethod;
}