From 6e6aad333f26694ff39aba1e59b358e3f25a9a1d Mon Sep 17 00:00:00 2001
From: Daniel Fiala <daniel@openssl.org>
Date: Sun, 28 Aug 2022 11:53:32 +0200
Subject: [PATCH] Convert serverinfo in SSL_CTX_use_serverinfo() to v2.

Fixes #18183.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: Hugo Landau <hlandau@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19081)
---
 ssl/ssl_rsa.c     |  81 ++++++++++++++++------
 test/sslapitest.c | 171 +++++++++++++++++++++++++++++++---------------
 2 files changed, 177 insertions(+), 75 deletions(-)

diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index 6457c0c0ef..3535c95354 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -727,6 +727,34 @@ static int serverinfoex_srv_parse_cb(SSL *s, unsigned int ext_type,
     return 1;
 }
 
+static size_t extension_contextoff(unsigned int version)
+{
+    return version == SSL_SERVERINFOV1 ? 4 : 0;
+}
+
+static size_t extension_append_length(unsigned int version, size_t extension_length)
+{
+    return extension_length + extension_contextoff(version);
+}
+
+static void extension_append(unsigned int version,
+                             const unsigned char *extension,
+                             const size_t extension_length,
+                             unsigned char *serverinfo)
+{
+    const size_t contextoff = extension_contextoff(version);
+
+    if (contextoff > 0) {
+        /* We know this only uses the last 2 bytes */
+        serverinfo[0] = 0;
+        serverinfo[1] = 0;
+        serverinfo[2] = (SYNTHV1CONTEXT >> 8) & 0xff;
+        serverinfo[3] = SYNTHV1CONTEXT & 0xff;
+    }
+
+    memcpy(serverinfo + contextoff, extension, extension_length);
+}
+
 static int serverinfo_srv_parse_cb(SSL *s, unsigned int ext_type,
                                    const unsigned char *in,
                                    size_t inlen, int *al, void *arg)
@@ -842,12 +870,36 @@ int SSL_CTX_use_serverinfo_ex(SSL_CTX *ctx, unsigned int version,
                               const unsigned char *serverinfo,
                               size_t serverinfo_length)
 {
-    unsigned char *new_serverinfo;
+    unsigned char *new_serverinfo = NULL;
 
     if (ctx == NULL || serverinfo == NULL || serverinfo_length == 0) {
         SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_EX, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }
+    if (version == SSL_SERVERINFOV1) {
+        /*
+         * Convert serverinfo version v1 to v2 and call yourself recursively
+         * over the converted serverinfo.
+         */
+        const size_t sinfo_length = extension_append_length(SSL_SERVERINFOV1,
+                                                            serverinfo_length);
+        unsigned char *sinfo;
+        int ret;
+
+        sinfo = OPENSSL_malloc(sinfo_length);
+        if (sinfo == NULL) {
+            SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_EX, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+
+        extension_append(SSL_SERVERINFOV1, serverinfo, serverinfo_length, sinfo);
+
+        ret = SSL_CTX_use_serverinfo_ex(ctx, SSL_SERVERINFOV2, sinfo,
+                                        sinfo_length);
+
+        OPENSSL_free(sinfo);
+        return ret;
+    }
     if (!serverinfo_process_buffer(version, serverinfo, serverinfo_length,
                                    NULL)) {
         SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_EX, SSL_R_INVALID_SERVERINFO_DATA);
@@ -899,7 +951,7 @@ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file)
     char namePrefix2[] = "SERVERINFOV2 FOR ";
     int ret = 0;
     BIO *bin = NULL;
-    size_t num_extensions = 0, contextoff = 0;
+    size_t num_extensions = 0;
 
     if (ctx == NULL || file == NULL) {
         SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_PASSED_NULL_PARAMETER);
@@ -918,6 +970,7 @@ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file)
 
     for (num_extensions = 0;; num_extensions++) {
         unsigned int version;
+        size_t append_length;
 
         if (PEM_read_bio(bin, &name, &header, &extension, &extension_length)
             == 0) {
@@ -962,11 +1015,6 @@ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file)
                 SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, SSL_R_BAD_DATA);
                 goto end;
             }
-            /*
-             * File does not have a context value so we must take account of
-             * this later.
-             */
-            contextoff = 4;
         } else {
             /* 8 byte header: 4 bytes context, 2 bytes type, 2 bytes len */
             if (extension_length < 8
@@ -977,25 +1025,16 @@ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file)
             }
         }
         /* Append the decoded extension to the serverinfo buffer */
-        tmp = OPENSSL_realloc(serverinfo, serverinfo_length + extension_length
-                                          + contextoff);
+        append_length = extension_append_length(version, extension_length);
+        tmp = OPENSSL_realloc(serverinfo, serverinfo_length + append_length);
         if (tmp == NULL) {
             SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_MALLOC_FAILURE);
             goto end;
         }
         serverinfo = tmp;
-        if (contextoff > 0) {
-            unsigned char *sinfo = serverinfo + serverinfo_length;
-
-            /* We know this only uses the last 2 bytes */
-            sinfo[0] = 0;
-            sinfo[1] = 0;
-            sinfo[2] = (SYNTHV1CONTEXT >> 8) & 0xff;
-            sinfo[3] = SYNTHV1CONTEXT & 0xff;
-        }
-        memcpy(serverinfo + serverinfo_length + contextoff,
-               extension, extension_length);
-        serverinfo_length += extension_length + contextoff;
+        extension_append(version, extension, extension_length,
+                         serverinfo + serverinfo_length);
+        serverinfo_length += append_length;
 
         OPENSSL_free(name);
         name = NULL;
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 7197e15cac..685c28d934 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -85,20 +85,6 @@ struct sslapitest_log_counts {
 };
 
 
-static unsigned char serverinfov1[] = {
-    0xff, 0xff, /* Dummy extension type */
-    0x00, 0x01, /* Extension length is 1 byte */
-    0xff        /* Dummy extension data */
-};
-
-static unsigned char serverinfov2[] = {
-    0x00, 0x00, 0x00,
-    (unsigned char)(SSL_EXT_CLIENT_HELLO & 0xff), /* Dummy context - 4 bytes */
-    0xff, 0xff, /* Dummy extension type */
-    0x00, 0x01, /* Extension length is 1 byte */
-    0xff        /* Dummy extension data */
-};
-
 static int hostname_cb(SSL *s, int *al, void *arg)
 {
     const char *hostname = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
@@ -4349,62 +4335,137 @@ end:
     return testresult;
 }
 
-/*
- * Test loading of serverinfo data in various formats. test_sslmessages actually
- * tests to make sure the extensions appear in the handshake
- */
-static int test_serverinfo(int tst)
+#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_3)
+
+#define  SYNTHV1CONTEXT     (SSL_EXT_TLS1_2_AND_BELOW_ONLY \
+                             | SSL_EXT_CLIENT_HELLO \
+                             | SSL_EXT_TLS1_2_SERVER_HELLO \
+                             | SSL_EXT_IGNORE_ON_RESUMPTION)
+
+#define TLS13CONTEXT (SSL_EXT_TLS1_3_CERTIFICATE \
+                      | SSL_EXT_TLS1_2_SERVER_HELLO \
+                      | SSL_EXT_CLIENT_HELLO)
+
+#define SERVERINFO_CUSTOM                                 \
+    0x00, (char)TLSEXT_TYPE_signed_certificate_timestamp, \
+    0x00, 0x03,                                           \
+    0x04, 0x05, 0x06                                      \
+
+static const unsigned char serverinfo_custom_tls13[] = {
+    0x00, 0x00, (TLS13CONTEXT >> 8) & 0xff, TLS13CONTEXT & 0xff,
+    SERVERINFO_CUSTOM
+};
+static const unsigned char serverinfo_custom_v2[] = {
+    0x00, 0x00, (SYNTHV1CONTEXT >> 8) & 0xff,  SYNTHV1CONTEXT & 0xff,
+    SERVERINFO_CUSTOM
+};
+static const unsigned char serverinfo_custom_v1[] = {
+    SERVERINFO_CUSTOM
+};
+static const size_t serverinfo_custom_tls13_len = sizeof(serverinfo_custom_tls13);
+static const size_t serverinfo_custom_v2_len = sizeof(serverinfo_custom_v2);
+static const size_t serverinfo_custom_v1_len = sizeof(serverinfo_custom_v1);
+
+static int serverinfo_custom_parse_cb(SSL *s, unsigned int ext_type,
+                                      unsigned int context,
+                                      const unsigned char *in,
+                                      size_t inlen, X509 *x,
+                                      size_t chainidx, int *al,
+                                      void *parse_arg)
 {
-    unsigned int version;
-    unsigned char *sibuf;
-    size_t sibuflen;
-    int ret, expected, testresult = 0;
-    SSL_CTX *ctx;
+    const size_t len = serverinfo_custom_v1_len;
+    const unsigned char *si = &serverinfo_custom_v1[len - 3];
+    int *p_cb_result = (int*)parse_arg;
+    *p_cb_result = TEST_mem_eq(in, inlen, si, 3);
+    return 1;
+}
 
-    ctx = SSL_CTX_new(TLS_method());
-    if (!TEST_ptr(ctx))
-        goto end;
+static int test_serverinfo_custom(const int idx)
+{
+    SSL_CTX *sctx = NULL, *cctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0;
+    int cb_result = 0;
 
-    if ((tst & 0x01) == 0x01)
-        version = SSL_SERVERINFOV2;
-    else
-        version = SSL_SERVERINFOV1;
+    /*
+     * Following variables are set in the switch statement
+     *  according to the test iteration.
+     * Default values do not make much sense: test would fail with them.
+     */
+    int serverinfo_version = 0;
+    int protocol_version = 0;
+    unsigned int extension_context = 0;
+    const unsigned char *si = NULL;
+    size_t si_len = 0;
 
-    if ((tst & 0x02) == 0x02) {
-        sibuf = serverinfov2;
-        sibuflen = sizeof(serverinfov2);
-        expected = (version == SSL_SERVERINFOV2);
-    } else {
-        sibuf = serverinfov1;
-        sibuflen = sizeof(serverinfov1);
-        expected = (version == SSL_SERVERINFOV1);
+    const int call_use_serverinfo_ex = idx > 0;
+    switch (idx) {
+    case 0: /* FALLTHROUGH */
+    case 1:
+        serverinfo_version = SSL_SERVERINFOV1;
+        protocol_version = TLS1_2_VERSION;
+        extension_context = SYNTHV1CONTEXT;
+        si = serverinfo_custom_v1;
+        si_len = serverinfo_custom_v1_len;
+        break;
+    case 2:
+        serverinfo_version = SSL_SERVERINFOV2;
+        protocol_version = TLS1_2_VERSION;
+        extension_context = SYNTHV1CONTEXT;
+        si = serverinfo_custom_v2;
+        si_len = serverinfo_custom_v2_len;
+        break;
+    case 3:
+        serverinfo_version = SSL_SERVERINFOV2;
+        protocol_version = TLS1_3_VERSION;
+        extension_context = TLS13CONTEXT;
+        si = serverinfo_custom_tls13;
+        si_len = serverinfo_custom_tls13_len;
+        break;
     }
 
-    if ((tst & 0x04) == 0x04) {
-        ret = SSL_CTX_use_serverinfo_ex(ctx, version, sibuf, sibuflen);
-    } else {
-        ret = SSL_CTX_use_serverinfo(ctx, sibuf, sibuflen);
+    if (!TEST_true(create_ssl_ctx_pair(TLS_method(),
+                                       TLS_method(),
+                                       protocol_version,
+                                       protocol_version,
+                                       &sctx, &cctx, cert, privkey)))
+        goto end;
 
-        /*
-         * The version variable is irrelevant in this case - it's what is in the
-         * buffer that matters
-         */
-        if ((tst & 0x02) == 0x02)
-            expected = 0;
-        else
-            expected = 1;
+    if (call_use_serverinfo_ex) {
+        if (!TEST_true(SSL_CTX_use_serverinfo_ex(sctx, serverinfo_version,
+                                                 si, si_len)))
+            goto end;
+    } else {
+        if (!TEST_true(SSL_CTX_use_serverinfo(sctx, si, si_len)))
+            goto end;
     }
 
-    if (!TEST_true(ret == expected))
+    if (!TEST_true(SSL_CTX_add_custom_ext(cctx, TLSEXT_TYPE_signed_certificate_timestamp,
+                                          extension_context,
+                                          NULL, NULL, NULL,
+                                          serverinfo_custom_parse_cb,
+                                          &cb_result))
+        || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+                                         NULL, NULL))
+        || !TEST_true(create_ssl_connection(serverssl, clientssl,
+                                            SSL_ERROR_NONE))
+        || !TEST_int_eq(SSL_do_handshake(clientssl), 1))
+        goto end;
+
+    if (!TEST_true(cb_result))
         goto end;
 
     testresult = 1;
 
  end:
-    SSL_CTX_free(ctx);
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
 
     return testresult;
 }
+#endif
 
 /*
  * Test that SSL_export_keying_material() produces expected results. There are
@@ -7175,7 +7236,6 @@ int setup_tests(void)
 #else
     ADD_ALL_TESTS(test_custom_exts, 3);
 #endif
-    ADD_ALL_TESTS(test_serverinfo, 8);
     ADD_ALL_TESTS(test_export_key_mat, 6);
 #ifndef OPENSSL_NO_TLS1_3
     ADD_ALL_TESTS(test_export_key_mat_early, 3);
@@ -7207,6 +7267,9 @@ int setup_tests(void)
 #endif
     ADD_TEST(test_set_alpn);
     ADD_TEST(test_inherit_verify_param);
+#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_3)
+    ADD_ALL_TESTS(test_serverinfo_custom, 4);
+#endif
     return 1;
 }
 
-- 
2.17.1

