Duplicate SSL_SESSIONs when renewing them.
See also upstream's 27c76b9b8010b536687318739c6f631ce4194688, CVE-2015-1791.
Rather than write a dup function, serializing and deserializing the object is
simpler. It also fixes a bug in the original fix where it never calls
new_session_cb to store the new session (for clients which use that callback;
how clients should handle the session cache is much less clear).
The old session isn't pruned as we haven't processed the Finished message yet.
RFC 5077 says:
The server MUST NOT assume that the client actually received the updated
ticket until it successfully verifies the client's Finished message.
Moreover, because network messages are asynchronous, a new SSL connection may
have began just before the client received the new ticket, so any such servers
are broken regardless.
Change-Id: I13b3dc986dc58ea2ce66659dbb29e14cd02a641b
Reviewed-on: https://boringssl-review.googlesource.com/5122
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index c9e7fcd..90b59c5 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1501,6 +1501,27 @@
return n;
}
+ if (s->hit) {
+ /* The server is sending a new ticket for an existing session. Sessions are
+ * immutable once established, so duplicate all but the ticket of the
+ * existing session. */
+ uint8_t *bytes;
+ size_t bytes_len;
+ if (!SSL_SESSION_to_bytes_for_ticket(s->session, &bytes, &bytes_len)) {
+ goto err;
+ }
+ SSL_SESSION *new_session = SSL_SESSION_from_bytes(bytes, bytes_len);
+ OPENSSL_free(bytes);
+ if (new_session == NULL) {
+ /* This should never happen. */
+ OPENSSL_PUT_ERROR(SSL, ssl3_get_new_session_ticket, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ SSL_SESSION_free(s->session);
+ s->session = new_session;
+ }
+
CBS_init(&new_session_ticket, s->init_msg, n);
if (!CBS_get_u32(&new_session_ticket,
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index 709e15b..76052df 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -261,7 +261,7 @@
}
}
- if (in->tlsext_tick) {
+ if (in->tlsext_tick && !for_ticket) {
if (!CBB_add_asn1(&session, &child, kTicketTag) ||
!CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
!CBB_add_bytes(&child2, in->tlsext_tick, in->tlsext_ticklen)) {
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index e82447c..1328cd5 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1948,8 +1948,16 @@
return;
}
+ int has_new_session = !s->hit;
+ if (!s->server && s->tlsext_ticket_expected) {
+ /* A client may see new sessions on abbreviated handshakes if the server
+ * decides to renew the ticket. Once the handshake is completed, it should
+ * be inserted into the cache. */
+ has_new_session = 1;
+ }
+
SSL_CTX *ctx = s->initial_ctx;
- if ((ctx->session_cache_mode & mode) == mode && !s->hit &&
+ if ((ctx->session_cache_mode & mode) == mode && has_new_session &&
((ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE) ||
SSL_CTX_add_session(ctx, s->session)) &&
ctx->new_session_cb != NULL) {