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/include/openssl/ssl.h b/include/openssl/ssl.h
index 65fa4a5..e908108 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1037,17 +1037,14 @@
* disable session caching and tickets. */
int not_resumable;
- /* The cert is the certificate used to establish this connection
- *
- * TODO(davidben): Remove this field. It is not serialized as part of the
- * session, but some APIs access it. Certificate-related fields, where not
- * redundant with |peer|, should be added to the session. Others should
- * probably not be retained across resumptions. */
- struct sess_cert_st /* SESS_CERT */ *sess_cert;
-
/* peer is the peer's certificate. */
X509 *peer;
+ /* cert_chain is the certificate chain sent by the peer. NOTE: for historical
+ * reasons, when a client (so the peer is a server), the chain includes
+ * |peer|, but when a server it does not. */
+ STACK_OF(X509) *cert_chain;
+
/* when app_verify_callback accepts a session where the peer's certificate is
* not ok, we must remember the error for session reuse: */
long verify_result; /* only for servers */
@@ -2225,9 +2222,20 @@
OPENSSL_EXPORT SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp,
long length);
-OPENSSL_EXPORT X509 *SSL_get_peer_certificate(const SSL *s);
+/* SSL_get_peer_certificate returns the peer's leaf certificate or NULL if the
+ * peer did not use certificates. The caller must call |X509_free| on the
+ * result to release it. */
+OPENSSL_EXPORT X509 *SSL_get_peer_certificate(const SSL *ssl);
-OPENSSL_EXPORT STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *s);
+/* SSL_get_peer_cert_chain returns the peer's certificate chain or NULL if
+ * unavailable or the peer did not use certificates. For historical reasons,
+ * this may not be available if resuming a serialized |SSL_SESSION|. The caller
+ * does not take ownership of the result.
+ *
+ * WARNING: This function behaves differently between client and server. If
+ * |ssl| is a server, the returned chain does not include the leaf certificate.
+ * If a client, it does. */
+OPENSSL_EXPORT STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *ssl);
OPENSSL_EXPORT int SSL_CTX_get_verify_mode(const SSL_CTX *ctx);
OPENSSL_EXPORT int SSL_CTX_get_verify_depth(const SSL_CTX *ctx);
diff --git a/ssl/internal.h b/ssl/internal.h
index 7d9edd0..d26fd6d 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -778,13 +778,6 @@
void *cert_cb_arg;
} CERT;
-typedef struct sess_cert_st {
- /* cert_chain is the certificate chain sent by the peer. NOTE: for a client,
- * this does includes the server's leaf certificate, but, for a server, this
- * does NOT include the client's leaf. */
- STACK_OF(X509) *cert_chain;
-} SESS_CERT;
-
/* SSL_METHOD is a compatibility structure to support the legacy version-locked
* methods. */
struct ssl_method_st {
@@ -965,9 +958,6 @@
CERT *ssl_cert_dup(CERT *cert);
void ssl_cert_clear_certs(CERT *c);
void ssl_cert_free(CERT *c);
-SESS_CERT *ssl_sess_cert_new(void);
-SESS_CERT *ssl_sess_cert_dup(const SESS_CERT *sess_cert);
-void ssl_sess_cert_free(SESS_CERT *sess_cert);
int ssl_get_new_session(SSL *s, int session);
enum ssl_session_result_t {
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 576a861..485c29f 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -950,7 +950,6 @@
unsigned long n;
X509 *x = NULL;
STACK_OF(X509) *sk = NULL;
- SESS_CERT *sc;
EVP_PKEY *pkey = NULL;
CBS cbs, certificate_list;
const uint8_t *data;
@@ -1019,17 +1018,10 @@
goto f_err;
}
- sc = ssl_sess_cert_new();
- if (sc == NULL) {
- goto err;
- }
-
- ssl_sess_cert_free(s->session->sess_cert);
- s->session->sess_cert = sc;
-
/* NOTE: Unlike the server half, the client's copy of |cert_chain| includes
* the leaf. */
- sc->cert_chain = sk;
+ sk_X509_pop_free(s->session->cert_chain, X509_free);
+ s->session->cert_chain = sk;
sk = NULL;
X509_free(s->session->peer);
@@ -1080,19 +1072,9 @@
return -1;
}
- /* In plain PSK ciphersuite, ServerKeyExchange can be
- omitted if no identity hint is sent. Set session->sess_cert anyway to
- avoid problems later.*/
+ /* In plain PSK ciphersuite, ServerKeyExchange may be omitted to send no
+ * identity hint. */
if (s->s3->tmp.new_cipher->algorithm_auth & SSL_aPSK) {
- /* PSK ciphersuites that also send a Certificate would have already
- * initialized |sess_cert|. */
- if (s->session->sess_cert == NULL) {
- s->session->sess_cert = ssl_sess_cert_new();
- if (s->session->sess_cert == NULL) {
- return -1;
- }
- }
-
/* TODO(davidben): This should be reset in one place with the rest of the
* handshake state. */
OPENSSL_free(s->s3->tmp.peer_psk_identity_hint);
@@ -1106,13 +1088,6 @@
CBS_init(&server_key_exchange, s->init_msg, n);
server_key_exchange_orig = server_key_exchange;
- if (s->session->sess_cert == NULL) {
- s->session->sess_cert = ssl_sess_cert_new();
- if (s->session->sess_cert == NULL) {
- return -1;
- }
- }
-
alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
alg_a = s->s3->tmp.new_cipher->algorithm_auth;
EVP_MD_CTX_init(&md_ctx);
@@ -1482,15 +1457,6 @@
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
- if (s->session->sess_cert != NULL) {
- /* |sess_cert| is not serialized and must be duplicated explicitly. */
- assert(new_session->sess_cert == NULL);
- new_session->sess_cert = ssl_sess_cert_dup(s->session->sess_cert);
- if (new_session->sess_cert == NULL) {
- SSL_SESSION_free(new_session);
- goto err;
- }
- }
SSL_SESSION_free(s->session);
s->session = new_session;
@@ -1677,12 +1643,6 @@
goto err;
}
- if (s->session->sess_cert == NULL) {
- /* We should always have a server certificate with SSL_kRSA. */
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- goto err;
- }
-
pkey = X509_get_pubkey(s->session->peer);
if (pkey == NULL ||
pkey->type != EVP_PKEY_RSA ||
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index b5fa946..037f232 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -558,6 +558,8 @@
if (s->ctx->retain_only_sha256_of_client_certs) {
X509_free(s->session->peer);
s->session->peer = NULL;
+ sk_X509_pop_free(s->session->cert_chain, X509_free);
+ s->session->cert_chain = NULL;
}
s->s3->initial_handshake_complete = 1;
@@ -2226,17 +2228,8 @@
s->session->peer = sk_X509_shift(sk);
s->session->verify_result = s->verify_result;
- /* With the current implementation, sess_cert will always be NULL when we
- * arrive here. */
- if (s->session->sess_cert == NULL) {
- s->session->sess_cert = ssl_sess_cert_new();
- if (s->session->sess_cert == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- goto err;
- }
- }
- sk_X509_pop_free(s->session->sess_cert->cert_chain, X509_free);
- s->session->sess_cert->cert_chain = sk;
+ sk_X509_pop_free(s->session->cert_chain, X509_free);
+ s->session->cert_chain = sk;
/* Inconsistency alert: cert_chain does *not* include the peer's own
* certificate, while we do include it in s3_clnt.c */
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;
}
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 66ef342..fd62a41 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -297,45 +297,6 @@
c->cert_cb_arg = arg;
}
-SESS_CERT *ssl_sess_cert_new(void) {
- SESS_CERT *ret;
-
- ret = OPENSSL_malloc(sizeof *ret);
- if (ret == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return NULL;
- }
-
- memset(ret, 0, sizeof *ret);
-
- return ret;
-}
-
-SESS_CERT *ssl_sess_cert_dup(const SESS_CERT *sess_cert) {
- SESS_CERT *ret = ssl_sess_cert_new();
- if (ret == NULL) {
- return NULL;
- }
-
- if (sess_cert->cert_chain != NULL) {
- ret->cert_chain = X509_chain_up_ref(sess_cert->cert_chain);
- if (ret->cert_chain == NULL) {
- ssl_sess_cert_free(ret);
- return NULL;
- }
- }
- return ret;
-}
-
-void ssl_sess_cert_free(SESS_CERT *sess_cert) {
- if (sess_cert == NULL) {
- return;
- }
-
- sk_X509_pop_free(sess_cert->cert_chain, X509_free);
- OPENSSL_free(sess_cert);
-}
-
int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk) {
X509 *x;
int i;
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 2964c37..bc01568 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -753,34 +753,18 @@
: 0;
}
-X509 *SSL_get_peer_certificate(const SSL *s) {
- X509 *r;
-
- if (s == NULL || s->session == NULL) {
- r = NULL;
- } else {
- r = s->session->peer;
- }
-
- if (r == NULL) {
+X509 *SSL_get_peer_certificate(const SSL *ssl) {
+ if (ssl == NULL || ssl->session == NULL || ssl->session->peer == NULL) {
return NULL;
}
-
- return X509_up_ref(r);
+ return X509_up_ref(ssl->session->peer);
}
-STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *s) {
- STACK_OF(X509) *r;
-
- if (s == NULL || s->session == NULL || s->session->sess_cert == NULL) {
- r = NULL;
- } else {
- r = s->session->sess_cert->cert_chain;
+STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *ssl) {
+ if (ssl == NULL || ssl->session == NULL) {
+ return NULL;
}
-
- /* If we are a client, cert_chain includes the peer's own certificate; if we
- * are a server, it does not. */
- return r;
+ return ssl->session->cert_chain;
}
/* Fix this so it checks all the valid key/cert options */
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index 1c644fd..93d7afc 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -604,8 +604,8 @@
OPENSSL_cleanse(session->master_key, sizeof(session->master_key));
OPENSSL_cleanse(session->session_id, sizeof(session->session_id));
- ssl_sess_cert_free(session->sess_cert);
X509_free(session->peer);
+ sk_X509_pop_free(session->cert_chain, X509_free);
OPENSSL_free(session->tlsext_hostname);
OPENSSL_free(session->tlsext_tick);
OPENSSL_free(session->tlsext_signed_cert_timestamp_list);
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index bfb6b50..ad96e06 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -337,6 +337,104 @@
"q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG"
"BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBLADBAEF";
+// kBoringSSLSession is a serialized SSL_SESSION generated from bssl client.
+static const char kBoringSSLSession[] =
+ "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";
+
// kBadSessionExtraField is a custom serialized SSL_SESSION generated by replacing
// the final (optional) element of |kCustomSession| with tag number 30.
static const char kBadSessionExtraField[] =
@@ -665,6 +763,7 @@
if (!TestCipherRules() ||
!TestSSL_SESSIONEncoding(kOpenSSLSession) ||
!TestSSL_SESSIONEncoding(kCustomSession) ||
+ !TestSSL_SESSIONEncoding(kBoringSSLSession) ||
!TestBadSSL_SESSIONEncoding(kBadSessionExtraField) ||
!TestBadSSL_SESSIONEncoding(kBadSessionVersion) ||
!TestBadSSL_SESSIONEncoding(kBadSessionTrailingData) ||