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 */