Commit a57ac5d324 for openssl.org

commit a57ac5d3243a915f1579d723105274163e144009
Author: Matt Caswell <matt@openssl.org>
Date:   Fri Feb 27 11:50:15 2026 +0000

    Remove further remnants of SSLv2 ClientHello handling

    We recently removed support for SSLv2 ClientHello messages - but some
    remnants were still remaining in the record layer. We remove those too.

    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Tomas Mraz <tomas@openssl.org>
    MergeDate: Fri Mar 13 15:12:39 2026
    (Merged from https://github.com/openssl/openssl/pull/30204)

diff --git a/ssl/record/methods/tls_common.c b/ssl/record/methods/tls_common.c
index aa43dba6a4..64a53b0cb0 100644
--- a/ssl/record/methods/tls_common.c
+++ b/ssl/record/methods/tls_common.c
@@ -553,7 +553,7 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
     size_t mac_size = 0;
     int imac_size;
     size_t num_recs = 0, max_recs, j;
-    PACKET pkt, sslv2pkt;
+    PACKET pkt;
     SSL_MAC_BUF *macbufs = NULL;
     int ret = OSSL_RECORD_RETURN_FATAL;

@@ -576,7 +576,6 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)

         /* check if we have the header */
         if ((rl->rstate != SSL_ST_READ_BODY) || (rl->packet_length < SSL3_RT_HEADER_LENGTH)) {
-            size_t sslv2len;
             unsigned int type;

             rret = rl->funcs->read_n(rl, SSL3_RT_HEADER_LENGTH,
@@ -593,74 +592,26 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
                 RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
                 return OSSL_RECORD_RETURN_FATAL;
             }
-            sslv2pkt = pkt;
-            if (!PACKET_get_net_2_len(&sslv2pkt, &sslv2len)
-                || !PACKET_get_1(&sslv2pkt, &type)) {
+
+            /* Pull apart the header into the TLS_RL_RECORD */
+            if (!PACKET_get_1(&pkt, &type)
+                || !PACKET_get_net_2(&pkt, &version)
+                || !PACKET_get_net_2_len(&pkt, &thisrr->length)) {
+                if (rl->msg_callback != NULL)
+                    rl->msg_callback(0, 0, SSL3_RT_HEADER, p, 5, rl->cbarg);
                 RLAYERfatal(rl, SSL_AD_DECODE_ERROR, ERR_R_INTERNAL_ERROR);
                 return OSSL_RECORD_RETURN_FATAL;
             }
-            /*
-             * The first record received by the server may be a V2ClientHello.
-             */
-            if (rl->role == OSSL_RECORD_ROLE_SERVER
-                && rl->is_first_record
-                && (sslv2len & 0x8000) != 0
-                && (type == SSL2_MT_CLIENT_HELLO)) {
-                /*
-                 *  SSLv2 style record
-                 *
-                 * |num_recs| here will actually always be 0 because
-                 * |num_recs > 0| only ever occurs when we are processing
-                 * multiple app data records - which we know isn't the case here
-                 * because it is an SSLv2ClientHello. We keep it using
-                 * |num_recs| for the sake of consistency
-                 */
-                thisrr->type = SSL3_RT_HANDSHAKE;
-                thisrr->rec_version = SSL2_VERSION;
-
-                thisrr->length = sslv2len & 0x7fff;
-
-                if (thisrr->length > TLS_BUFFER_get_len(rbuf)
-                        - SSL2_RT_HEADER_LENGTH) {
-                    RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW,
-                        SSL_R_PACKET_LENGTH_TOO_LONG);
-                    return OSSL_RECORD_RETURN_FATAL;
-                }
-            } else {
-                /* SSLv3+ style record */
-
-                /* Pull apart the header into the TLS_RL_RECORD */
-                if (!PACKET_get_1(&pkt, &type)
-                    || !PACKET_get_net_2(&pkt, &version)
-                    || !PACKET_get_net_2_len(&pkt, &thisrr->length)) {
-                    if (rl->msg_callback != NULL)
-                        rl->msg_callback(0, 0, SSL3_RT_HEADER, p, 5, rl->cbarg);
-                    RLAYERfatal(rl, SSL_AD_DECODE_ERROR, ERR_R_INTERNAL_ERROR);
-                    return OSSL_RECORD_RETURN_FATAL;
-                }
-                thisrr->type = type;
-                thisrr->rec_version = version;
+            thisrr->type = type;
+            thisrr->rec_version = version;

-                /*
-                 * When we call validate_record_header() only records actually
-                 * received in SSLv2 format should have the record version set
-                 * to SSL2_VERSION. This way validate_record_header() can know
-                 * what format the record was in based on the version.
-                 */
-                if (thisrr->rec_version == SSL2_VERSION) {
-                    RLAYERfatal(rl, SSL_AD_PROTOCOL_VERSION,
-                        SSL_R_WRONG_VERSION_NUMBER);
-                    return OSSL_RECORD_RETURN_FATAL;
-                }
+            if (rl->msg_callback != NULL)
+                rl->msg_callback(0, version, SSL3_RT_HEADER, p, 5, rl->cbarg);

-                if (rl->msg_callback != NULL)
-                    rl->msg_callback(0, version, SSL3_RT_HEADER, p, 5, rl->cbarg);
-
-                if (thisrr->length > TLS_BUFFER_get_len(rbuf) - SSL3_RT_HEADER_LENGTH) {
-                    RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW,
-                        SSL_R_PACKET_LENGTH_TOO_LONG);
-                    return OSSL_RECORD_RETURN_FATAL;
-                }
+            if (thisrr->length > TLS_BUFFER_get_len(rbuf) - SSL3_RT_HEADER_LENGTH) {
+                RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW,
+                    SSL_R_PACKET_LENGTH_TOO_LONG);
+                return OSSL_RECORD_RETURN_FATAL;
             }

             if (!rl->funcs->validate_record_header(rl, thisrr)) {
diff --git a/ssl/record/methods/tlsany_meth.c b/ssl/record/methods/tlsany_meth.c
index 9961725439..2b9a02146e 100644
--- a/ssl/record/methods/tlsany_meth.c
+++ b/ssl/record/methods/tlsany_meth.c
@@ -43,75 +43,64 @@ static int tls_any_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs,

 static int tls_validate_record_header(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec)
 {
-    if (rec->rec_version == SSL2_VERSION) {
-        /* SSLv2 format ClientHello */
-        if (!ossl_assert(rl->version == TLS_ANY_VERSION)) {
-            RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            return 0;
-        }
-        if (rec->length < MIN_SSL2_RECORD_LEN) {
-            RLAYERfatal(rl, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_TOO_SHORT);
-            return 0;
-        }
-    } else {
-        if (rl->version == TLS_ANY_VERSION) {
-            if ((rec->rec_version >> 8) != SSL3_VERSION_MAJOR) {
-                if (rl->is_first_record) {
-                    unsigned char *p;
-
-                    /*
-                     * Go back to start of packet, look at the five bytes that
-                     * we have.
-                     */
-                    p = rl->packet;
-                    if (HAS_PREFIX((char *)p, "GET ") || HAS_PREFIX((char *)p, "POST ") || HAS_PREFIX((char *)p, "HEAD ") || HAS_PREFIX((char *)p, "PATCH") || HAS_PREFIX((char *)p, "OPTIO") || HAS_PREFIX((char *)p, "DELET") || HAS_PREFIX((char *)p, "TRACE") || HAS_PREFIX((char *)p, "PUT ")) {
-                        RLAYERfatal(rl, SSL_AD_NO_ALERT, SSL_R_HTTP_REQUEST);
-                        return 0;
-                    } else if (HAS_PREFIX((char *)p, "CONNE")) {
-                        RLAYERfatal(rl, SSL_AD_NO_ALERT,
-                            SSL_R_HTTPS_PROXY_REQUEST);
-                        return 0;
-                    }
-
-                    /* Doesn't look like TLS - don't send an alert */
-                    RLAYERfatal(rl, SSL_AD_NO_ALERT,
-                        SSL_R_WRONG_VERSION_NUMBER);
-                    return 0;
-                } else {
-                    RLAYERfatal(rl, SSL_AD_PROTOCOL_VERSION,
-                        SSL_R_WRONG_VERSION_NUMBER);
+    if (rl->version == TLS_ANY_VERSION) {
+        if ((rec->rec_version >> 8) != SSL3_VERSION_MAJOR) {
+            if (rl->is_first_record) {
+                unsigned char *p;
+
+                /*
+                 * Go back to start of packet, look at the five bytes that
+                 * we have.
+                 */
+                p = rl->packet;
+                if (HAS_PREFIX((char *)p, "GET ") || HAS_PREFIX((char *)p, "POST ") || HAS_PREFIX((char *)p, "HEAD ") || HAS_PREFIX((char *)p, "PATCH") || HAS_PREFIX((char *)p, "OPTIO") || HAS_PREFIX((char *)p, "DELET") || HAS_PREFIX((char *)p, "TRACE") || HAS_PREFIX((char *)p, "PUT ")) {
+                    RLAYERfatal(rl, SSL_AD_NO_ALERT, SSL_R_HTTP_REQUEST);
                     return 0;
-                }
-            }
-        } else if (rl->version == TLS1_3_VERSION) {
-            /*
-             * In this case we know we are going to negotiate TLSv1.3, but we've
-             * had an HRR, so we haven't actually done so yet. In TLSv1.3 we
-             * must ignore the legacy record version in plaintext records.
-             */
-        } else if (rec->rec_version != rl->version) {
-            if ((rl->version & 0xFF00) == (rec->rec_version & 0xFF00)) {
-                if (rec->type == SSL3_RT_ALERT) {
-                    /*
-                     * The record is using an incorrect version number,
-                     * but what we've got appears to be an alert. We
-                     * haven't read the body yet to check whether its a
-                     * fatal or not - but chances are it is. We probably
-                     * shouldn't send a fatal alert back. We'll just
-                     * end.
-                     */
+                } else if (HAS_PREFIX((char *)p, "CONNE")) {
                     RLAYERfatal(rl, SSL_AD_NO_ALERT,
-                        SSL_R_WRONG_VERSION_NUMBER);
+                        SSL_R_HTTPS_PROXY_REQUEST);
                     return 0;
                 }
-                /* Send back error using their minor version number */
-                rl->version = (unsigned short)rec->rec_version;
+
+                /* Doesn't look like TLS - don't send an alert */
+                RLAYERfatal(rl, SSL_AD_NO_ALERT,
+                    SSL_R_WRONG_VERSION_NUMBER);
+                return 0;
+            } else {
+                RLAYERfatal(rl, SSL_AD_PROTOCOL_VERSION,
+                    SSL_R_WRONG_VERSION_NUMBER);
+                return 0;
             }
-            RLAYERfatal(rl, SSL_AD_PROTOCOL_VERSION,
-                SSL_R_WRONG_VERSION_NUMBER);
-            return 0;
         }
+    } else if (rl->version == TLS1_3_VERSION) {
+        /*
+         * In this case we know we are going to negotiate TLSv1.3, but we've
+         * had an HRR, so we haven't actually done so yet. In TLSv1.3 we
+         * must ignore the legacy record version in plaintext records.
+         */
+    } else if (rec->rec_version != rl->version) {
+        if ((rl->version & 0xFF00) == (rec->rec_version & 0xFF00)) {
+            if (rec->type == SSL3_RT_ALERT) {
+                /*
+                 * The record is using an incorrect version number,
+                 * but what we've got appears to be an alert. We
+                 * haven't read the body yet to check whether its a
+                 * fatal or not - but chances are it is. We probably
+                 * shouldn't send a fatal alert back. We'll just
+                 * end.
+                 */
+                RLAYERfatal(rl, SSL_AD_NO_ALERT,
+                    SSL_R_WRONG_VERSION_NUMBER);
+                return 0;
+            }
+            /* Send back error using their minor version number */
+            rl->version = (unsigned short)rec->rec_version;
+        }
+        RLAYERfatal(rl, SSL_AD_PROTOCOL_VERSION,
+            SSL_R_WRONG_VERSION_NUMBER);
+        return 0;
     }
+
     if (rec->length > SSL3_RT_MAX_PLAIN_LENGTH) {
         /*
          * We use SSL_R_DATA_LENGTH_TOO_LONG instead of