This hangs a few bits off SSL_SESSION and populates them on receipt of a
NewSessionTicket. Since the current session may already be in the cache,
and as such should not be changed, we dup the session using a new
ssl_session_dup() and modify that one. I added an include_ticket
parameter which we could use if we should need the SSL_SESSION_dup() API.

The actual parse function is directly modeled after RFC 8446, section
4.6.1, with the EVP_Digest() for the session_id matching what's done in
ssl3_get_new_session_ticket().

As always with PHH messages, there is a bit of a DOS question. For now
the general rate limiting of 100 PHH messages per hour is all we have.

Index: ssl_locl.h
===================================================================
RCS file: /cvs/src/lib/libssl/ssl_locl.h,v
retrieving revision 1.427
diff -u -p -r1.427 ssl_locl.h
--- ssl_locl.h  2 Oct 2022 16:36:41 -0000       1.427
+++ ssl_locl.h  13 Oct 2022 13:21:17 -0000
@@ -507,6 +507,10 @@ struct ssl_session_st {
        size_t tlsext_ticklen;                  /* Session ticket length */
        uint32_t tlsext_tick_lifetime_hint;     /* Session lifetime hint in 
seconds */
 
+       /* RFC 8446 info */
+       uint32_t tlsext_tick_age_add;
+       struct tls13_secret resumption_master_secret;
+
        CRYPTO_EX_DATA ex_data; /* application specific data */
 
        /* These are used to make removal of session-ids more
@@ -1311,6 +1315,7 @@ int ssl_security_cert_chain(const SSL *s
 int ssl_security_shared_group(const SSL *ssl, uint16_t group_id);
 int ssl_security_supported_group(const SSL *ssl, uint16_t group_id);
 
+SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int include_ticket);
 int ssl_get_new_session(SSL *s, int session);
 int ssl_get_prev_session(SSL *s, CBS *session_id, CBS *ext_block,
     int *alert);
Index: ssl_sess.c
===================================================================
RCS file: /cvs/src/lib/libssl/ssl_sess.c,v
retrieving revision 1.118
diff -u -p -r1.118 ssl_sess.c
--- ssl_sess.c  2 Oct 2022 16:36:41 -0000       1.118
+++ ssl_sess.c  13 Oct 2022 13:21:17 -0000
@@ -222,6 +222,12 @@ SSL_SESSION_new(void)
                return (NULL);
        }
 
+       if (!tls13_secret_init(&ss->resumption_master_secret, 256)) {
+               SSLerrorx(ERR_R_MALLOC_FAILURE);
+               free(ss);
+               return (NULL);
+       }
+
        ss->verify_result = 1; /* avoid 0 (= X509_V_OK) just in case */
        ss->references = 1;
        ss->timeout = 60 * 5 + 4; /* 5 minutes 4 seconds timeout by default */
@@ -242,6 +248,108 @@ SSL_SESSION_new(void)
        return (ss);
 }
 
+SSL_SESSION *
+ssl_session_dup(SSL_SESSION *src, int include_ticket)
+{
+       SSL_SESSION *dest;
+       CBS cbs;
+
+       if ((dest = calloc(1, sizeof(*dest))) == NULL) {
+               SSLerrorx(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (!tls13_secret_init(&dest->resumption_master_secret, 256)) {
+               SSLerrorx(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       CBS_init(&cbs, src->master_key, src->master_key_length);
+       if (!CBS_write_bytes(&cbs, dest->master_key, sizeof(dest->master_key),
+           &dest->master_key_length))
+               goto err;
+
+       CBS_init(&cbs, src->session_id, src->session_id_length);
+       if (!CBS_write_bytes(&cbs, dest->session_id, sizeof(dest->session_id),
+           &dest->session_id_length))
+               goto err;
+
+       CBS_init(&cbs, src->sid_ctx, src->sid_ctx_length);
+       if (!CBS_write_bytes(&cbs, dest->sid_ctx, sizeof(dest->sid_ctx),
+           &dest->sid_ctx_length))
+               goto err;
+
+       if (src->peer_cert != NULL) {
+               if (!X509_up_ref(src->peer_cert))
+                       goto err;
+               dest->peer_cert = src->peer_cert;
+       }
+       dest->peer_cert_type = src->peer_cert_type;
+
+       dest->verify_result = src->verify_result;
+
+       dest->timeout = src->timeout;
+       dest->time = src->time;
+       dest->references = 1;
+
+       dest->cipher = src->cipher;
+       dest->cipher_id = src->cipher_id;
+
+       if (src->ciphers != NULL) {
+               if ((dest->ciphers = sk_SSL_CIPHER_dup(src->ciphers)) == NULL)
+                       goto err;
+       }
+
+       if (include_ticket) {
+               if (src->tlsext_tick != NULL) {
+                       CBS_init(&cbs, src->tlsext_tick, src->tlsext_ticklen);
+                       if (!CBS_stow(&cbs, &dest->tlsext_tick,
+                           &dest->tlsext_ticklen))
+                               goto err;
+                       dest->tlsext_tick_lifetime_hint =
+                           src->tlsext_tick_lifetime_hint;
+               }
+
+               /* XXX - include TLSv1.3 NST here? */
+       }
+
+       if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, dest,
+           &dest->ex_data))
+               goto err;
+
+       if (!CRYPTO_dup_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, &dest->ex_data,
+           &src->ex_data))
+               goto err;
+
+       /* Omit prev/next: the new session gets its own slot in the cache. */
+
+       dest->not_resumable = src->not_resumable;
+
+       if (src->tlsext_ecpointformatlist != NULL) {
+               CBS_init(&cbs, src->tlsext_ecpointformatlist,
+                   src->tlsext_ecpointformatlist_length);
+               if (!CBS_stow(&cbs, &dest->tlsext_ecpointformatlist,
+                   &dest->tlsext_ecpointformatlist_length))
+                       goto err;
+       }
+
+       if (src->tlsext_supportedgroups != NULL) {
+               if ((dest->tlsext_supportedgroups = calloc(sizeof(uint16_t),
+                   src->tlsext_supportedgroups_length)) == NULL)
+                       goto err;
+               memcpy(dest->tlsext_supportedgroups, 
src->tlsext_supportedgroups,
+                   sizeof(uint16_t) * src->tlsext_supportedgroups_length);
+               dest->tlsext_supportedgroups_length =
+                   src->tlsext_supportedgroups_length;
+       }
+
+       return dest;
+
+ err:
+       SSL_SESSION_free(dest);
+       return NULL;
+}
+
 const unsigned char *
 SSL_SESSION_get_id(const SSL_SESSION *ss, unsigned int *len)
 {
@@ -771,6 +879,8 @@ SSL_SESSION_free(SSL_SESSION *ss)
        free(ss->tlsext_tick);
        free(ss->tlsext_ecpointformatlist);
        free(ss->tlsext_supportedgroups);
+
+       tls13_secret_cleanup(&ss->resumption_master_secret);
 
        freezero(ss, sizeof(*ss));
 }
Index: tls13_lib.c
===================================================================
RCS file: /cvs/src/lib/libssl/tls13_lib.c,v
retrieving revision 1.72
diff -u -p -r1.72 tls13_lib.c
--- tls13_lib.c 2 Oct 2022 16:36:42 -0000       1.72
+++ tls13_lib.c 13 Oct 2022 13:21:17 -0000
@@ -25,6 +25,13 @@
 #include "tls13_internal.h"
 
 /*
+ * RFC 8446, section 4.6.1. Servers must not indicate a lifetime longer than
+ * 7 days and clients must not cache tickets for longer than 7 days.
+ */
+
+#define TLS13_MAX_TICKET_LIFETIME      (7 * 24 * 3600)
+
+/*
  * Downgrade sentinels - RFC 8446 section 4.1.3, magic values which must be set
  * by the server in server random if it is willing to downgrade but supports
  * TLSv1.3
@@ -328,6 +335,98 @@ tls13_key_update_recv(struct tls13_ctx *
        return tls13_send_alert(ctx->rl, alert);
 }
 
+/* RFC 8446 section 4.6.1 */
+static ssize_t
+tls13_new_session_ticket_recv(struct tls13_ctx *ctx, CBS *cbs)
+{
+       int alert = TLS13_ALERT_DECODE_ERROR;
+       uint32_t ticket_lifetime, ticket_age_add;
+       CBS ticket_nonce, ticket;
+       SSL_SESSION *sess;
+       int session_id_length;
+       struct tls13_secrets *secrets = ctx->hs->tls13.secrets;
+       struct tls13_secret nonce;
+
+       memset(&nonce, 0, sizeof(nonce));
+
+       if (ctx->mode != TLS13_HS_CLIENT) {
+               alert = TLS13_ALERT_UNEXPECTED_MESSAGE;
+               goto err;
+       }
+
+       if (!CBS_get_u32(cbs, &ticket_lifetime))
+               goto err;
+       if (!CBS_get_u32(cbs, &ticket_age_add))
+               goto err;
+       if (!CBS_get_u8_length_prefixed(cbs, &ticket_nonce))
+               goto err;
+       if (!CBS_get_u16_length_prefixed(cbs, &ticket))
+               goto err;
+       /* Extensions only contain early_data, which we ignore. */
+       if (!tlsext_client_parse(ctx->ssl, SSL_TLSEXT_MSG_NST, cbs, &alert))
+               goto err;
+
+       if (CBS_len(cbs) != 0)
+               goto err;
+
+       /* Servers MUST NOT use any value larger than 7 days. */
+       if (ticket_lifetime > TLS13_MAX_TICKET_LIFETIME) {
+               alert = TLS13_ALERT_ILLEGAL_PARAMETER;
+               goto err;
+       }
+       /* Zero indicates that the ticket should be discarded immediately. */
+       if (ticket_lifetime == 0) {
+               alert = 0;
+               goto err;
+       }
+
+       alert = TLS13_ALERT_INTERNAL_ERROR;
+
+       /*
+        * Create new session instead of modifying the current session.
+        * The current session could already be in the session cache.
+        */
+       if ((sess = ssl_session_dup(ctx->ssl->session, 0)) == NULL)
+               goto err;
+       SSL_SESSION_free(ctx->ssl->session);
+       ctx->ssl->session = sess;
+
+       sess->time = time(NULL);
+
+       sess->tlsext_tick_lifetime_hint = ticket_lifetime;
+       sess->tlsext_tick_age_add = ticket_age_add;
+
+       if (!CBS_stow(&ticket, &sess->tlsext_tick, &sess->tlsext_ticklen))
+               goto err;
+
+       if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket),
+           sess->session_id, &session_id_length, EVP_sha256(), NULL))
+               goto err;
+       sess->session_id_length = session_id_length;
+
+       if (!CBS_stow(&ticket_nonce, &nonce.data, &nonce.len))
+               goto err;
+
+       if (!tls13_derive_secret(&sess->resumption_master_secret,
+           secrets->digest, &secrets->resumption_master, "resumption",
+           &nonce))
+               goto err;
+
+       tls13_secret_cleanup(&nonce);
+
+       ssl_update_cache(ctx->ssl, SSL_SESS_CACHE_CLIENT);
+
+       return TLS13_IO_SUCCESS;
+
+ err:
+       tls13_secret_cleanup(&nonce);
+
+       if (alert == 0)
+               return TLS13_IO_SUCCESS;
+
+       return tls13_send_alert(ctx->rl, alert);
+}
+
 ssize_t
 tls13_phh_received_cb(void *cb_arg)
 {
@@ -354,7 +453,7 @@ tls13_phh_received_cb(void *cb_arg)
                ret = tls13_key_update_recv(ctx, &cbs);
                break;
        case TLS13_MT_NEW_SESSION_TICKET:
-               /* XXX do nothing for now and ignore this */
+               ret = tls13_new_session_ticket_recv(ctx, &cbs);
                break;
        case TLS13_MT_CERTIFICATE_REQUEST:
                /* XXX add support if we choose to advertise this */

Reply via email to