]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
NewSessionTickets with an early_data extension must have a valid max value
authorMatt Caswell <matt@openssl.org>
Mon, 7 Aug 2023 11:21:20 +0000 (12:21 +0100)
committerMatt Caswell <matt@openssl.org>
Tue, 15 Aug 2023 13:41:31 +0000 (14:41 +0100)
The max_early_data value must be 0xffffffff if the extension is present in
a NewSessionTicket message in QUIC. Otherwise it is a PROTOCOL_VIOLATION.

Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21686)

include/internal/quic_tls.h
ssl/quic/quic_channel.c
ssl/quic/quic_tls.c
ssl/statem/extensions_clnt.c

index 7d9c533728456489a267a36162accbad1983bd75..30a521a7a8b3c4d67901e82b3027f3269197d29b 100644 (file)
@@ -101,5 +101,6 @@ int ossl_quic_tls_get_error(QUIC_TLS *qtls,
                             ERR_STATE **error_state);
 
 int ossl_quic_tls_is_cert_request(QUIC_TLS *qtls);
+int ossl_quic_tls_has_bad_max_early_data(QUIC_TLS *qtls);
 
 #endif
index 7cabcb5ccf0d0e65c45f8263b8b539b792c0518c..a27e19781beac0a6e4c3836b283cb2d2f2e28afa 100644 (file)
@@ -994,7 +994,7 @@ static int ch_on_handshake_alert(void *arg, unsigned char alert_code)
      * TLS CertificateRequest messages, and clients MUST treat receipt of such
      * messages as a connection error of type PROTOCOL_VIOLATION.
      */
-    if (alert_code == SSL3_AD_UNEXPECTED_MESSAGE
+    if (alert_code == SSL_AD_UNEXPECTED_MESSAGE
             && ch->handshake_complete
             && ossl_quic_tls_is_cert_request(ch->qtls))
         ossl_quic_channel_raise_protocol_error(ch,
@@ -1002,6 +1002,20 @@ static int ch_on_handshake_alert(void *arg, unsigned char alert_code)
                                                0,
                                                "Post-handshake TLS "
                                                "CertificateRequest received");
+    /*
+     * RFC 9001 s. 4.6.1: Servers MUST NOT send the early_data extension with a
+     * max_early_data_size field set to any value other than 0xffffffff. A
+     * client MUST treat receipt of a NewSessionTicket that contains an
+     * early_data extension with any other value as a connection error of type
+     * PROTOCOL_VIOLATION.
+     */
+    else if (alert_code == SSL_AD_ILLEGAL_PARAMETER
+             && ch->handshake_complete
+             && ossl_quic_tls_has_bad_max_early_data(ch->qtls))
+        ossl_quic_channel_raise_protocol_error(ch,
+                                               QUIC_ERR_PROTOCOL_VIOLATION,
+                                               0,
+                                               "Bad max_early_data received");
     else
         ossl_quic_channel_raise_protocol_error(ch,
                                                QUIC_ERR_CRYPTO_ERR_BEGIN
index f3ef2c979e4eb573facf43a3e2b5e7faddc8fa32..8fd02fba75409d6f0064c0d7999d316566941c44 100644 (file)
@@ -853,3 +853,19 @@ int ossl_quic_tls_is_cert_request(QUIC_TLS *qtls)
 
     return sc->s3.tmp.message_type == SSL3_MT_CERTIFICATE_REQUEST;
 }
+
+/*
+ * Returns true if the last session associated with the connection has an
+ * invalid max_early_data value for QUIC.
+ */
+int ossl_quic_tls_has_bad_max_early_data(QUIC_TLS *qtls)
+{
+    uint32_t max_early_data = SSL_get0_session(qtls->args.s)->ext.max_early_data;
+
+    /*
+     * If max_early_data was present we always ensure a non-zero value is
+     * stored in the session for QUIC. Therefore if max_early_data == 0 here
+     * we can be confident that it was not present in the NewSessionTicket
+     */
+    return max_early_data != 0xffffffff && max_early_data != 0;
+}
index d32dcfbd069afbfde0e2c35d42037e8bf0d8a65f..381a6c9d7b9099b30eb49746f8c1710fdc99091f 100644 (file)
@@ -1934,6 +1934,22 @@ int tls_parse_stoc_early_data(SSL_CONNECTION *s, PACKET *pkt,
 
         s->session->ext.max_early_data = max_early_data;
 
+        if (SSL_IS_QUIC_HANDSHAKE(s) && max_early_data != 0xffffffff) {
+            /*
+             * QUIC allows missing max_early_data, or a max_early_data value
+             * of 0xffffffff. Missing max_early_data is stored in the session
+             * as 0. This is indistinguishable in OpenSSL from a present
+             * max_early_data value that was 0. In order that later checks for
+             * invalid max_early_data correctly treat as an error the case where
+             * max_early_data is present and it is 0, we store any invalid
+             * value in the same (non-zero) way. Otherwise we would have to
+             * introduce a new flag just for this.
+             */
+            s->session->ext.max_early_data = 1;
+            SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_INVALID_MAX_EARLY_DATA);
+            return 0;
+        }
+
         return 1;
     }