Provide a way to determine our maximum legacy version.
authorjsing <jsing@openbsd.org>
Sat, 23 Oct 2021 14:40:54 +0000 (14:40 +0000)
committerjsing <jsing@openbsd.org>
Sat, 23 Oct 2021 14:40:54 +0000 (14:40 +0000)
With the introduction of TLSv1.3, we need the ability to determine our
maximum legacy version and to track our peer's maximum legacy version.
This is needed for both the TLS record layer when using TLSv1.3, plus
it is needed for RSA key exhange in TLS prior to TLSv1.3, where the
maximum legacy version is incorporated in the pre-master secret to
avoid downgrade attacks.

This unbreaks RSA KEX for the TLS client when the non-version specific
method is used with TLSv1.0 or TLSv1.1 (clearly no one does this).

ok tb@

lib/libssl/ssl_clnt.c
lib/libssl/ssl_locl.h
lib/libssl/ssl_srvr.c
lib/libssl/ssl_versions.c
lib/libssl/tls13_client.c
lib/libssl/tls13_legacy.c
lib/libssl/tls13_server.c

index ea13f81..2e7047e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_clnt.c,v 1.114 2021/10/23 13:36:03 jsing Exp $ */
+/* $OpenBSD: ssl_clnt.c,v 1.115 2021/10/23 14:40:54 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -650,7 +650,7 @@ ssl3_send_client_hello(SSL *s)
                        SSLerror(s, SSL_R_NO_PROTOCOLS_AVAILABLE);
                        return (-1);
                }
-               s->client_version = s->version = max_version;
+               s->version = max_version;
 
                if (sess == NULL ||
                    sess->ssl_version != s->version ||
@@ -673,37 +673,7 @@ ssl3_send_client_hello(SSL *s)
                    SSL3_MT_CLIENT_HELLO))
                        goto err;
 
-               /*
-                * Version indicates the negotiated version: for example from
-                * an SSLv2/v3 compatible client hello). The client_version
-                * field is the maximum version we permit and it is also
-                * used in RSA encrypted premaster secrets. Some servers can
-                * choke if we initially report a higher version then
-                * renegotiate to a lower one in the premaster secret. This
-                * didn't happen with TLS 1.0 as most servers supported it
-                * but it can with TLS 1.1 or later if the server only supports
-                * 1.0.
-                *
-                * Possible scenario with previous logic:
-                *      1. Client hello indicates TLS 1.2
-                *      2. Server hello says TLS 1.0
-                *      3. RSA encrypted premaster secret uses 1.2.
-                *      4. Handhaked proceeds using TLS 1.0.
-                *      5. Server sends hello request to renegotiate.
-                *      6. Client hello indicates TLS v1.0 as we now
-                *         know that is maximum server supports.
-                *      7. Server chokes on RSA encrypted premaster secret
-                *         containing version 1.0.
-                *
-                * For interoperability it should be OK to always use the
-                * maximum version we support in client hello and then rely
-                * on the checking of version to ensure the servers isn't
-                * being inconsistent: for example initially negotiating with
-                * TLS 1.0 and renegotiating with TLS 1.2. We do this by using
-                * client_version in client hello and not resetting it to
-                * the negotiated version.
-                */
-               if (!CBB_add_u16(&client_hello, s->client_version))
+               if (!CBB_add_u16(&client_hello, s->version))
                        goto err;
 
                /* Random stuff */
@@ -889,6 +859,7 @@ ssl3_get_server_hello(SSL *s)
                al = SSL_AD_PROTOCOL_VERSION;
                goto fatal_err;
        }
+       S3I(s)->hs.peer_legacy_version = server_version;
        s->version = server_version;
 
        S3I(s)->hs.negotiated_tls_version = ssl_tls_version(server_version);
@@ -1952,6 +1923,7 @@ ssl3_send_client_kex_rsa(SSL *s, SESS_CERT *sess_cert, CBB *cbb)
 {
        unsigned char pms[SSL_MAX_MASTER_KEY_LENGTH];
        unsigned char *enc_pms = NULL;
+       uint16_t max_legacy_version;
        EVP_PKEY *pkey = NULL;
        int ret = -1;
        int enc_len;
@@ -1968,9 +1940,17 @@ ssl3_send_client_kex_rsa(SSL *s, SESS_CERT *sess_cert, CBB *cbb)
                goto err;
        }
 
-       /* XXX - our max protocol version. */
-       pms[0] = s->client_version >> 8;
-       pms[1] = s->client_version & 0xff;
+       /*
+        * Our maximum legacy protocol version - while RFC 5246 section 7.4.7.1
+        * says "The latest (newest) version supported by the client", if we're
+        * doing RSA key exchange then we have to presume that we're talking to
+        * a server that does not understand the supported versions extension
+        * and therefore our maximum version is that sent in the ClientHello.
+        */
+       if (!ssl_max_legacy_version(s, &max_legacy_version))
+               goto err;
+       pms[0] = max_legacy_version >> 8;
+       pms[1] = max_legacy_version & 0xff;
        arc4random_buf(&pms[2], sizeof(pms) - 2);
 
        if ((enc_pms = malloc(RSA_size(pkey->pkey.rsa))) == NULL) {
index b41a5d8..3c58e5a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_locl.h,v 1.362 2021/10/23 11:41:52 beck Exp $ */
+/* $OpenBSD: ssl_locl.h,v 1.363 2021/10/23 14:40:54 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -582,6 +582,13 @@ typedef struct ssl_handshake_st {
         */
        uint16_t negotiated_tls_version;
 
+       /*
+        * Legacy version advertised by our peer. For a server this is the
+        * version specified by the client in the ClientHello message. For a
+        * client, this is the version provided in the ServerHello message.
+        */
+       uint16_t peer_legacy_version;
+
        /*
         * Current handshake state - contains one of the SSL3_ST_* values and
         * is used by the TLSv1.2 state machine, as well as being updated by
@@ -1291,6 +1298,7 @@ int ssl_supported_tls_version_range(SSL *s, uint16_t *min_ver, uint16_t *max_ver
 uint16_t ssl_tls_version(uint16_t version);
 uint16_t ssl_effective_tls_version(SSL *s);
 int ssl_max_supported_version(SSL *s, uint16_t *max_ver);
+int ssl_max_legacy_version(SSL *s, uint16_t *max_ver);
 int ssl_max_shared_version(SSL *s, uint16_t peer_ver, uint16_t *max_ver);
 int ssl_check_version_from_server(SSL *s, uint16_t server_version);
 int ssl_legacy_stack_version(SSL *s, uint16_t version);
index 1aa0324..ec1e69a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_srvr.c,v 1.121 2021/10/23 13:36:03 jsing Exp $ */
+/* $OpenBSD: ssl_srvr.c,v 1.122 2021/10/23 14:40:54 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -837,19 +837,19 @@ ssl3_get_client_hello(SSL *s)
         * (may differ: see RFC 2246, Appendix E, second paragraph)
         */
        if (!ssl_max_shared_version(s, client_version, &shared_version)) {
-               if ((s->client_version >> 8) == SSL3_VERSION_MAJOR &&
+               if ((client_version >> 8) == SSL3_VERSION_MAJOR &&
                    !tls12_record_layer_write_protected(s->internal->rl)) {
                        /*
                         * Similar to ssl3_get_record, send alert using remote
                         * version number.
                         */
-                       s->version = s->client_version;
+                       s->version = client_version;
                }
                SSLerror(s, SSL_R_WRONG_VERSION_NUMBER);
                al = SSL_AD_PROTOCOL_VERSION;
                goto fatal_err;
        }
-       s->client_version = client_version;
+       S3I(s)->hs.peer_legacy_version = client_version;
        s->version = shared_version;
 
        S3I(s)->hs.negotiated_tls_version = ssl_tls_version(shared_version);
@@ -1723,9 +1723,8 @@ ssl3_get_client_kex_rsa(SSL *s, CBS *cbs)
 
        arc4random_buf(fakekey, sizeof(fakekey));
 
-       /* XXX - peer max protocol version. */
-       fakekey[0] = s->client_version >> 8;
-       fakekey[1] = s->client_version & 0xff;
+       fakekey[0] = S3I(s)->hs.peer_legacy_version >> 8;
+       fakekey[1] = S3I(s)->hs.peer_legacy_version & 0xff;
 
        pkey = s->cert->pkeys[SSL_PKEY_RSA].privatekey;
        if ((pkey == NULL) || (pkey->type != EVP_PKEY_RSA) ||
@@ -1760,9 +1759,8 @@ ssl3_get_client_kex_rsa(SSL *s, CBS *cbs)
                /* SSLerror(s, SSL_R_BAD_RSA_DECRYPT); */
        }
 
-       /* XXX - peer max version. */
-       if ((al == -1) && !((pms[0] == (s->client_version >> 8)) &&
-           (pms[1] == (s->client_version & 0xff)))) {
+       if ((al == -1) && !((pms[0] == (S3I(s)->hs.peer_legacy_version >> 8)) &&
+           (pms[1] == (S3I(s)->hs.peer_legacy_version & 0xff)))) {
                /*
                 * The premaster secret must contain the same version number
                 * as the ClientHello to detect version rollback attacks
index c633b58..b5834db 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_versions.c,v 1.20 2021/07/01 17:53:39 jsing Exp $ */
+/* $OpenBSD: ssl_versions.c,v 1.21 2021/10/23 14:40:54 jsing Exp $ */
 /*
  * Copyright (c) 2016, 2017 Joel Sing <jsing@openbsd.org>
  *
@@ -250,6 +250,24 @@ ssl_max_supported_version(SSL *s, uint16_t *max_ver)
        return 1;
 }
 
+int
+ssl_max_legacy_version(SSL *s, uint16_t *max_ver)
+{
+       uint16_t max_version;
+
+       if ((max_version = S3I(s)->hs.our_max_tls_version) > TLS1_2_VERSION)
+               max_version = TLS1_2_VERSION;
+
+       if (SSL_is_dtls(s)) {
+               if ((max_version = ssl_tls_to_dtls_version(max_version)) == 0)
+                       return 0;
+       }
+
+       *max_ver = max_version;
+
+       return 1;
+}
+
 int
 ssl_max_shared_version(SSL *s, uint16_t peer_ver, uint16_t *max_ver)
 {
index 62c5174..00a1c6b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls13_client.c,v 1.86 2021/06/29 19:20:39 jsing Exp $ */
+/* $OpenBSD: tls13_client.c,v 1.87 2021/10/23 14:40:54 jsing Exp $ */
 /*
  * Copyright (c) 2018, 2019 Joel Sing <jsing@openbsd.org>
  *
@@ -36,7 +36,7 @@ tls13_client_init(struct tls13_ctx *ctx)
                SSLerror(s, SSL_R_NO_PROTOCOLS_AVAILABLE);
                return 0;
        }
-       s->client_version = s->version = ctx->hs->our_max_tls_version;
+       s->version = ctx->hs->our_max_tls_version;
 
        tls13_record_layer_set_retry_after_phh(ctx->rl,
            (s->internal->mode & SSL_MODE_AUTO_RETRY) != 0);
@@ -92,9 +92,8 @@ tls13_client_hello_build(struct tls13_ctx *ctx, CBB *cbb)
        SSL *s = ctx->ssl;
 
        /* Legacy client version is capped at TLS 1.2. */
-       client_version = ctx->hs->our_max_tls_version;
-       if (client_version > TLS1_2_VERSION)
-               client_version = TLS1_2_VERSION;
+       if (!ssl_max_legacy_version(s, &client_version))
+               goto err;
 
        if (!CBB_add_u16(cbb, client_version))
                goto err;
@@ -282,6 +281,7 @@ tls13_server_hello_process(struct tls13_ctx *ctx, CBS *cbs)
                goto err;
        }
        ctx->hs->negotiated_tls_version = ctx->hs->tls13.server_version;
+       ctx->hs->peer_legacy_version = legacy_version;
 
        /* The session_id must match. */
        if (!CBS_mem_equal(&session_id, ctx->hs->tls13.legacy_session_id,
index f668dd4..18e6fa3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: tls13_legacy.c,v 1.31 2021/09/16 19:25:30 jsing Exp $ */
+/*     $OpenBSD: tls13_legacy.c,v 1.32 2021/10/23 14:40:54 jsing Exp $ */
 /*
  * Copyright (c) 2018, 2019 Joel Sing <jsing@openbsd.org>
  *
@@ -383,7 +383,7 @@ tls13_use_legacy_client(struct tls13_ctx *ctx)
                return 0;
 
        s->internal->handshake_func = s->method->ssl_connect;
-       s->client_version = s->version = s->method->max_tls_version;
+       s->version = s->method->max_tls_version;
 
        return 1;
 }
@@ -397,7 +397,7 @@ tls13_use_legacy_server(struct tls13_ctx *ctx)
                return 0;
 
        s->internal->handshake_func = s->method->ssl_accept;
-       s->client_version = s->version = s->method->max_tls_version;
+       s->version = s->method->max_tls_version;
        s->server = 1;
 
        return 1;
index 9c0369f..a19b382 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls13_server.c,v 1.85 2021/10/23 13:12:55 tb Exp $ */
+/* $OpenBSD: tls13_server.c,v 1.86 2021/10/23 14:40:54 jsing Exp $ */
 /*
  * Copyright (c) 2019, 2020 Joel Sing <jsing@openbsd.org>
  * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
@@ -164,6 +164,7 @@ tls13_client_hello_process(struct tls13_ctx *ctx, CBS *cbs)
                return tls13_use_legacy_server(ctx);
        }
        ctx->hs->negotiated_tls_version = TLS1_3_VERSION;
+       ctx->hs->peer_legacy_version = legacy_version;
 
        /* Ensure we send subsequent alerts with the correct record version. */
        tls13_record_layer_set_legacy_version(ctx->rl, TLS1_2_VERSION);