Preserve session->sess_cert on ticket renewal.
Turns out the safer/simpler method still wasn't quite right. :-)
session->sess_cert isn't serialized and deserialized, which is poor. Duplicate
it manually for now. Leave a TODO to get rid of that field altogether as it's
not especially helpful. The certificate-related fields should be in the
session. The others probably have no reason to be preserved on resumptions at
all.
Test by making bssl_shim.cc assert the peer cert chain is there or not as
expected.
BUG=501220
Change-Id: I44034167629720d6e2b7b0b938d58bcab3ab0abe
Reviewed-on: https://boringssl-review.googlesource.com/5170
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index caac446..7e278ae 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -650,7 +650,12 @@
* disable session caching and tickets. */
int not_resumable;
- /* The cert is the certificate used to establish this connection */
+ /* 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;
/* This is the cert for the other end. On clients, it will be the same as
diff --git a/ssl/internal.h b/ssl/internal.h
index 074db0d..00eccfa 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -797,7 +797,8 @@
void ssl_cert_clear_certs(CERT *c);
void ssl_cert_free(CERT *c);
SESS_CERT *ssl_sess_cert_new(void);
-void ssl_sess_cert_free(SESS_CERT *sc);
+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);
int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx);
STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs);
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 90b59c5..8d192b6 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1517,6 +1517,15 @@
OPENSSL_PUT_ERROR(SSL, ssl3_get_new_session_ticket, 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;
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index f27685d..85aa079 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -119,11 +119,13 @@
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/buf.h>
+#include <openssl/ec_key.h>
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
#include <openssl/pem.h>
+#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include "../crypto/dh/internal.h"
@@ -413,17 +415,44 @@
return ret;
}
-void ssl_sess_cert_free(SESS_CERT *sc) {
- if (sc == NULL) {
+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;
+ }
+ }
+ if (sess_cert->peer_cert != NULL) {
+ ret->peer_cert = X509_up_ref(sess_cert->peer_cert);
+ }
+ if (sess_cert->peer_dh_tmp != NULL) {
+ ret->peer_dh_tmp = sess_cert->peer_dh_tmp;
+ DH_up_ref(ret->peer_dh_tmp);
+ }
+ if (sess_cert->peer_ecdh_tmp != NULL) {
+ ret->peer_ecdh_tmp = sess_cert->peer_ecdh_tmp;
+ EC_KEY_up_ref(ret->peer_ecdh_tmp);
+ }
+ return ret;
+}
+
+void ssl_sess_cert_free(SESS_CERT *sess_cert) {
+ if (sess_cert == NULL) {
return;
}
- sk_X509_pop_free(sc->cert_chain, X509_free);
- X509_free(sc->peer_cert);
- DH_free(sc->peer_dh_tmp);
- EC_KEY_free(sc->peer_ecdh_tmp);
+ sk_X509_pop_free(sess_cert->cert_chain, X509_free);
+ X509_free(sess_cert->peer_cert);
+ DH_free(sess_cert->peer_dh_tmp);
+ EC_KEY_free(sess_cert->peer_ecdh_tmp);
- OPENSSL_free(sc);
+ OPENSSL_free(sess_cert);
}
int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk) {
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 40cb149..3b95d7e 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -838,6 +838,20 @@
return false;
}
}
+
+ if (!config->is_server) {
+ /* Clients should expect a peer certificate chain iff this was not a PSK
+ * cipher suite. */
+ if (config->psk.empty()) {
+ if (SSL_get_peer_cert_chain(ssl.get()) == nullptr) {
+ fprintf(stderr, "Missing peer certificate chain!\n");
+ return false;
+ }
+ } else if (SSL_get_peer_cert_chain(ssl.get()) != nullptr) {
+ fprintf(stderr, "Unexpected peer certificate chain!\n");
+ return false;
+ }
+ }
}
if (config->export_keying_material > 0) {