Remove the last of SESS_CERT.
Move cert_chain to the SSL_SESSION. Now everything on an SSL_SESSION is
properly serialized. The cert_chain field is, unfortunately, messed up
since it means different things between client and server.
There exists code which calls SSL_get_peer_cert_chain as both client and
server and assumes the existing semantics for each. Since that function
doesn't return a newly-allocated STACK_OF(X509), normalizing between the
two formats is a nuisance (we'd either need to store both cert_chain and
cert_chain_full on the SSL_SESSION or create one of the two variants
on-demand and stash it into the SSL).
This CL does not resolve this and retains the client/server difference
in SSL_SESSION. The SSL_SESSION serialization is a little inefficient
(two copies of the leaf certificate) for a client, but clients don't
typically serialize sessions. Should we wish to resolve it in the
future, we can use a different tag number. Because this was historically
unserialized, existing code must already allow for cert_chain not being
preserved across i2d/d2i.
In keeping with the semantics of retain_only_sha256_of_client_certs,
cert_chain is not retained when that flag is set.
Change-Id: Ieb72fc62c3076dd59750219e550902f1ad039651
Reviewed-on: https://boringssl-review.googlesource.com/5759
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index ab1fd74..2fd7f12 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -114,9 +114,10 @@
* signedCertTimestampList [15] OCTET STRING OPTIONAL,
* -- contents of SCT extension
* ocspResponse [16] OCTET STRING OPTIONAL,
- * -- stapled OCSP response from the server
+ * -- stapled OCSP response from the server
* extendedMasterSecret [17] BOOLEAN OPTIONAL,
* keyExchangeInfo [18] INTEGER OPTIONAL,
+ * certChain [19] SEQUENCE OF Certificate OPTIONAL,
* }
*
* Note: historically this serialization has included other optional
@@ -157,6 +158,24 @@
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 17;
static const int kKeyExchangeInfoTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 18;
+static const int kCertChainTag =
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19;
+
+static int add_X509(CBB *cbb, X509 *x509) {
+ int len = i2d_X509(x509, NULL);
+ if (len < 0) {
+ return 0;
+ }
+ uint8_t *buf;
+ if (!CBB_add_space(cbb, &buf, len)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ if (buf != NULL && i2d_X509(x509, &buf) < 0) {
+ return 0;
+ }
+ return 1;
+}
static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data,
size_t *out_len, int for_ticket) {
@@ -202,17 +221,11 @@
/* The peer certificate is only serialized if the SHA-256 isn't
* serialized instead. */
if (in->peer && !in->peer_sha256_valid) {
- uint8_t *buf;
- int len = i2d_X509(in->peer, NULL);
- if (len < 0) {
- goto err;
- }
- if (!CBB_add_asn1(&session, &child, kPeerTag) ||
- !CBB_add_space(&child, &buf, len)) {
+ if (!CBB_add_asn1(&session, &child, kPeerTag)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
- if (buf != NULL && i2d_X509(in->peer, &buf) < 0) {
+ if (!add_X509(&child, in->peer)) {
goto err;
}
}
@@ -325,6 +338,21 @@
goto err;
}
+ /* The certificate chain is only serialized if the leaf's SHA-256 isn't
+ * serialized instead. */
+ if (in->cert_chain != NULL && !in->peer_sha256_valid) {
+ if (!CBB_add_asn1(&session, &child, kCertChainTag)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ size_t i;
+ for (i = 0; i < sk_X509_num(in->cert_chain); i++) {
+ if (!add_X509(&child, sk_X509_value(in->cert_chain, i))) {
+ goto err;
+ }
+ }
+ }
+
if (!CBB_finish(&cbb, out_data, out_len)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
@@ -598,8 +626,40 @@
ret->extended_master_secret = !!extended_master_secret;
if (!SSL_SESSION_parse_u32(&session, &ret->key_exchange_info,
- kKeyExchangeInfoTag, 0) ||
- CBS_len(&session) != 0) {
+ kKeyExchangeInfoTag, 0)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
+ goto err;
+ }
+
+ CBS cert_chain;
+ int has_cert_chain;
+ if (!CBS_get_optional_asn1(&session, &cert_chain, &has_cert_chain,
+ kCertChainTag)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
+ goto err;
+ }
+ sk_X509_pop_free(ret->cert_chain, X509_free);
+ ret->cert_chain = NULL;
+ if (has_cert_chain) {
+ ret->cert_chain = sk_X509_new_null();
+ if (ret->cert_chain == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ while (CBS_len(&cert_chain) > 0) {
+ X509 *x509 = parse_x509(&cert_chain);
+ if (x509 == NULL) {
+ goto err;
+ }
+ if (!sk_X509_push(ret->cert_chain, x509)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ X509_free(x509);
+ goto err;
+ }
+ }
+ }
+
+ if (CBS_len(&session) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
goto err;
}