diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 5f39754f..96e76ea2 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -3806,6 +3806,12 @@ struct ssl_session_st { * early data. If zero, 0-RTT is disallowed. */ uint32_t ticket_max_early_data; + /* early_alpn is the ALPN protocol from the initial handshake. This is only + * stored for TLS 1.3 and above in order to enforce ALPN matching for 0-RTT + * resumptions. */ + uint8_t *early_alpn; + size_t early_alpn_len; + /* extended_master_secret is true if the master secret in this session was * generated using EMS and thus isn't vulnerable to the Triple Handshake * attack. */ diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c index 3582864e..3533225a 100644 --- a/ssl/ssl_asn1.c +++ b/ssl/ssl_asn1.c @@ -130,6 +130,7 @@ * peerSignatureAlgorithm [23] INTEGER OPTIONAL, * ticketMaxEarlyData [24] INTEGER OPTIONAL, * authTimeout [25] INTEGER OPTIONAL, -- defaults to timeout + * earlyALPN [26] OCTET STRING OPTIONAL, * } * * Note: historically this serialization has included other optional @@ -186,6 +187,8 @@ static const int kTicketMaxEarlyDataTag = CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 24; static const int kAuthTimeoutTag = CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 25; +static const int kEarlyALPNTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 26; static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data, size_t *out_len, int for_ticket) { @@ -412,6 +415,16 @@ static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data, goto err; } + if (in->early_alpn) { + if (!CBB_add_asn1(&session, &child, kEarlyALPNTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, (const uint8_t *)in->early_alpn, + in->early_alpn_len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + if (!CBB_finish(&cbb, out_data, out_len)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; @@ -800,6 +813,8 @@ SSL_SESSION *SSL_SESSION_parse(CBS *cbs, const SSL_X509_METHOD *x509_method, kTicketMaxEarlyDataTag, 0) || !SSL_SESSION_parse_long(&session, &ret->auth_timeout, kAuthTimeoutTag, ret->timeout) || + !SSL_SESSION_parse_octet_string(&session, &ret->early_alpn, + &ret->early_alpn_len, kEarlyALPNTag) || CBS_len(&session) != 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; diff --git a/ssl/ssl_session.c b/ssl/ssl_session.c index c30fe6e7..60f20f48 100644 --- a/ssl/ssl_session.c +++ b/ssl/ssl_session.c @@ -280,6 +280,15 @@ SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int dup_flags) { new_session->ticket_age_add = session->ticket_age_add; new_session->ticket_max_early_data = session->ticket_max_early_data; new_session->extended_master_secret = session->extended_master_secret; + + if (session->early_alpn != NULL) { + new_session->early_alpn = + BUF_memdup(session->early_alpn, session->early_alpn_len); + if (new_session->early_alpn == NULL) { + goto err; + } + } + new_session->early_alpn_len = session->early_alpn_len; } /* Copy the ticket. */ @@ -373,6 +382,7 @@ void SSL_SESSION_free(SSL_SESSION *session) { OPENSSL_free(session->tlsext_signed_cert_timestamp_list); OPENSSL_free(session->ocsp_response); OPENSSL_free(session->psk_identity); + OPENSSL_free(session->early_alpn); OPENSSL_cleanse(session, sizeof(*session)); OPENSSL_free(session); } diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c index 50f7e5a6..815b30ea 100644 --- a/ssl/tls13_client.c +++ b/ssl/tls13_client.c @@ -270,6 +270,17 @@ static enum ssl_hs_wait_t do_process_server_hello(SSL_HANDSHAKE *hs) { ssl->s3->new_session->cipher = cipher; ssl->s3->tmp.new_cipher = cipher; + /* Store the initial negotiated ALPN in the session. */ + if (ssl->s3->alpn_selected != NULL) { + ssl->s3->new_session->early_alpn = + BUF_memdup(ssl->s3->alpn_selected, ssl->s3->alpn_selected_len); + if (ssl->s3->new_session->early_alpn == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + ssl->s3->new_session->early_alpn_len = ssl->s3->alpn_selected_len; + } + /* The PRF hash is now known. Set up the key schedule. */ if (!tls13_init_key_schedule(hs)) { return ssl_hs_error; diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c index 0278b500..5c9e6db9 100644 --- a/ssl/tls13_server.c +++ b/ssl/tls13_server.c @@ -251,6 +251,17 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { return ssl_hs_error; } + /* Store the initial negotiated ALPN in the session. */ + if (ssl->s3->alpn_selected != NULL) { + ssl->s3->new_session->early_alpn = + BUF_memdup(ssl->s3->alpn_selected, ssl->s3->alpn_selected_len); + if (ssl->s3->new_session->early_alpn == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + ssl->s3->new_session->early_alpn_len = ssl->s3->alpn_selected_len; + } + /* Incorporate the PSK into the running secret. */ if (ssl->s3->session_reused) { if (!tls13_advance_key_schedule(hs, ssl->s3->new_session->master_key,