Tests for SSL early callback
authorBenjamin Kaduk <bkaduk@akamai.com>
Tue, 31 Jan 2017 22:06:30 +0000 (16:06 -0600)
committerRichard Levitte <levitte@openssl.org>
Thu, 23 Feb 2017 18:40:26 +0000 (19:40 +0100)
Plumb things through in the same place as the SNI callback, since
we recommend that the early callback replace (and supplement) the
SNI callback, and add a few test cases.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2279)

test/handshake_helper.c
test/ssl-tests/05-sni.conf
test/ssl-tests/05-sni.conf.in
test/ssl_test_ctx.c
test/ssl_test_ctx.h

index c82581c..88f6aec 100644 (file)
@@ -123,6 +123,67 @@ static int select_server_ctx(SSL *s, void *arg, int ignore)
     }
 }
 
+static int early_select_server_ctx(SSL *s, void *arg, int ignore)
+{
+    const char *servername;
+    const unsigned char *p;
+    size_t len, remaining;
+    HANDSHAKE_EX_DATA *ex_data =
+        (HANDSHAKE_EX_DATA*)(SSL_get_ex_data(s, ex_data_idx));
+
+    /*
+     * The server_name extension was given too much extensibility when it
+     * was written, so parsing the normal case is a bit complex.
+     */
+    if (!SSL_early_get0_ext(s, TLSEXT_TYPE_server_name, &p, &remaining) ||
+        remaining <= 2)
+        return 0;
+    /* Extract the length of the supplied list of names. */
+    len = (*(p++) << 1);
+    len += *(p++);
+    if (len + 2 != remaining)
+        return 0;
+    remaining = len;
+    /*
+     * The list in practice only has a single element, so we only consider
+     * the first one.
+     */
+    if (remaining == 0 || *p++ != TLSEXT_NAMETYPE_host_name)
+        return 0;
+    remaining--;
+    /* Now we can finally pull out the byte array with the actual hostname. */
+    if (remaining <= 2)
+        return 0;
+    len = (*(p++) << 1);
+    len += *(p++);
+    if (len + 2 > remaining)
+        return 0;
+    remaining = len;
+    servername = (const char *)p;
+
+    if (len == strlen("server2") && strncmp(servername, "server2", len) == 0) {
+        SSL_CTX *new_ctx = arg;
+        SSL_set_SSL_CTX(s, new_ctx);
+        /*
+         * Copy over all the SSL_CTX options - reasonable behavior
+         * allows testing of cases where the options between two
+         * contexts differ/conflict
+         */
+        SSL_clear_options(s, 0xFFFFFFFFL);
+        SSL_set_options(s, SSL_CTX_get_options(new_ctx));
+
+        ex_data->servername = SSL_TEST_SERVERNAME_SERVER2;
+        return 1;
+    } else if (len == strlen("server1") &&
+               strncmp(servername, "server1", len) == 0) {
+        ex_data->servername = SSL_TEST_SERVERNAME_SERVER1;
+        return 1;
+    } else if (ignore) {
+        ex_data->servername = SSL_TEST_SERVERNAME_SERVER1;
+        return 1;
+    }
+    return 0;
+}
 /*
  * (RFC 6066):
  *  If the server understood the ClientHello extension but
@@ -144,6 +205,50 @@ static int servername_reject_cb(SSL *s, int *ad, void *arg)
     return select_server_ctx(s, arg, 0);
 }
 
+static int early_ignore_cb(SSL *s, int *al, void *arg)
+{
+    if (!early_select_server_ctx(s, arg, 1)) {
+        *al = SSL_AD_UNRECOGNIZED_NAME;
+        return 0;
+    }
+    return 1;
+}
+
+static int early_reject_cb(SSL *s, int *al, void *arg)
+{
+    if (!early_select_server_ctx(s, arg, 0)) {
+        *al = SSL_AD_UNRECOGNIZED_NAME;
+        return 0;
+    }
+    return 1;
+}
+
+static int early_nov12_cb(SSL *s, int *al, void *arg)
+{
+    int ret;
+    unsigned int v;
+    const unsigned char *p;
+
+    v = SSL_early_get0_legacy_version(s);
+    if (v > TLS1_2_VERSION || v < SSL3_VERSION) {
+        *al = SSL_AD_PROTOCOL_VERSION;
+        return 0;
+    }
+    (void)SSL_early_get0_session_id(s, &p);
+    if (p == NULL ||
+        SSL_early_get0_random(s, &p) == 0 ||
+        SSL_early_get0_ciphers(s, &p) == 0 ||
+        SSL_early_get0_compression_methods(s, &p) == 0) {
+        *al = SSL_AD_INTERNAL_ERROR;
+        return 0;
+    }
+    ret = early_select_server_ctx(s, arg, 0);
+    SSL_set_max_proto_version(s, TLS1_1_VERSION);
+    if (!ret)
+        *al = SSL_AD_UNRECOGNIZED_NAME;
+    return ret;
+}
+
 static unsigned char dummy_ocsp_resp_good_val = 0xff;
 static unsigned char dummy_ocsp_resp_bad_val = 0xfe;
 
@@ -337,7 +442,10 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
         break;
     }
 
-    /* link the two contexts for SNI purposes */
+    /*
+     * Link the two contexts for SNI purposes.
+     * Also do early callbacks here, as setting both early and SNI is bad.
+     */
     switch (extra->server.servername_callback) {
     case SSL_TEST_SERVERNAME_IGNORE_MISMATCH:
         SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_ignore_cb);
@@ -349,6 +457,14 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
         break;
     case SSL_TEST_SERVERNAME_CB_NONE:
         break;
+    case SSL_TEST_SERVERNAME_EARLY_IGNORE_MISMATCH:
+        SSL_CTX_set_early_cb(server_ctx, early_ignore_cb, server2_ctx);
+        break;
+    case SSL_TEST_SERVERNAME_EARLY_REJECT_MISMATCH:
+        SSL_CTX_set_early_cb(server_ctx, early_reject_cb, server2_ctx);
+        break;
+    case SSL_TEST_SERVERNAME_EARLY_NO_V12:
+        SSL_CTX_set_early_cb(server_ctx, early_nov12_cb, server2_ctx);
     }
 
     if (extra->server.cert_status != SSL_TEST_CERT_STATUS_NONE) {
index e1fb3d9..d5d350e 100644 (file)
@@ -1,6 +1,6 @@
 # Generated with generate_ssl_tests.pl
 
-num_tests = 6
+num_tests = 9
 
 test-0 = 0-SNI-switch-context
 test-1 = 1-SNI-keep-context
@@ -8,6 +8,9 @@ test-2 = 2-SNI-no-server-support
 test-3 = 3-SNI-no-client-support
 test-4 = 4-SNI-bad-sni-ignore-mismatch
 test-5 = 5-SNI-bad-sni-reject-mismatch
+test-6 = 6-SNI-bad-early-sni-ignore-mismatch
+test-7 = 7-SNI-bad-early-sni-reject-mismatch
+test-8 = 8-SNI-early-disable-v12
 # ===========================================================
 
 [0-SNI-switch-context]
@@ -201,3 +204,105 @@ ServerNameCallback = RejectMismatch
 ServerName = invalid
 
 
+# ===========================================================
+
+[6-SNI-bad-early-sni-ignore-mismatch]
+ssl_conf = 6-SNI-bad-early-sni-ignore-mismatch-ssl
+
+[6-SNI-bad-early-sni-ignore-mismatch-ssl]
+server = 6-SNI-bad-early-sni-ignore-mismatch-server
+client = 6-SNI-bad-early-sni-ignore-mismatch-client
+server2 = 6-SNI-bad-early-sni-ignore-mismatch-server
+
+[6-SNI-bad-early-sni-ignore-mismatch-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[6-SNI-bad-early-sni-ignore-mismatch-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-6]
+ExpectedResult = Success
+ExpectedServerName = server1
+server = 6-SNI-bad-early-sni-ignore-mismatch-server-extra
+server2 = 6-SNI-bad-early-sni-ignore-mismatch-server-extra
+client = 6-SNI-bad-early-sni-ignore-mismatch-client-extra
+
+[6-SNI-bad-early-sni-ignore-mismatch-server-extra]
+ServerNameCallback = EarlyIgnoreMismatch
+
+[6-SNI-bad-early-sni-ignore-mismatch-client-extra]
+ServerName = invalid
+
+
+# ===========================================================
+
+[7-SNI-bad-early-sni-reject-mismatch]
+ssl_conf = 7-SNI-bad-early-sni-reject-mismatch-ssl
+
+[7-SNI-bad-early-sni-reject-mismatch-ssl]
+server = 7-SNI-bad-early-sni-reject-mismatch-server
+client = 7-SNI-bad-early-sni-reject-mismatch-client
+server2 = 7-SNI-bad-early-sni-reject-mismatch-server
+
+[7-SNI-bad-early-sni-reject-mismatch-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[7-SNI-bad-early-sni-reject-mismatch-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-7]
+ExpectedResult = ServerFail
+ExpectedServerAlert = UnrecognizedName
+server = 7-SNI-bad-early-sni-reject-mismatch-server-extra
+server2 = 7-SNI-bad-early-sni-reject-mismatch-server-extra
+client = 7-SNI-bad-early-sni-reject-mismatch-client-extra
+
+[7-SNI-bad-early-sni-reject-mismatch-server-extra]
+ServerNameCallback = EarlyRejectMismatch
+
+[7-SNI-bad-early-sni-reject-mismatch-client-extra]
+ServerName = invalid
+
+
+# ===========================================================
+
+[8-SNI-early-disable-v12]
+ssl_conf = 8-SNI-early-disable-v12-ssl
+
+[8-SNI-early-disable-v12-ssl]
+server = 8-SNI-early-disable-v12-server
+client = 8-SNI-early-disable-v12-client
+server2 = 8-SNI-early-disable-v12-server
+
+[8-SNI-early-disable-v12-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[8-SNI-early-disable-v12-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-8]
+ExpectedProtocol = TLSv1.1
+ExpectedServerName = server2
+server = 8-SNI-early-disable-v12-server-extra
+server2 = 8-SNI-early-disable-v12-server-extra
+client = 8-SNI-early-disable-v12-client-extra
+
+[8-SNI-early-disable-v12-server-extra]
+ServerNameCallback = EarlyNoV12
+
+[8-SNI-early-disable-v12-client-extra]
+ServerName = server2
+
+
index 76003e7..63b295d 100644 (file)
@@ -13,6 +13,7 @@ use strict;
 use warnings;
 
 package ssltests;
+use OpenSSL::Test::Utils;
 
 our @tests = (
     {
@@ -109,4 +110,60 @@ our @tests = (
             "ExpectedServerAlert" => "UnrecognizedName"
         },
     },
+    {
+        name => "SNI-bad-early-sni-ignore-mismatch",
+        server => {
+            extra => {
+                "ServerNameCallback" => "EarlyIgnoreMismatch",
+            },
+        },
+        client => {
+            extra => {
+                "ServerName" => "invalid",
+            },
+        },
+        test   => {
+            "ExpectedServerName" => "server1",
+            "ExpectedResult" => "Success"
+        },
+    },
+    {
+        name => "SNI-bad-early-sni-reject-mismatch",
+        server => {
+            extra => {
+                "ServerNameCallback" => "EarlyRejectMismatch",
+            },
+        },
+        client => {
+            extra => {
+                "ServerName" => "invalid",
+            },
+        },
+        test   => {
+            "ExpectedResult" => "ServerFail",
+            "ExpectedServerAlert" => "UnrecognizedName"
+        },
+    },
 );
+
+our @tests_tls_1_1 = (
+    {
+        name => "SNI-early-disable-v12",
+        server => {
+            extra => {
+                "ServerNameCallback" => "EarlyNoV12",
+            },
+        },
+        client => {
+            extra => {
+                "ServerName" => "server2",
+            },
+        },
+        test   => {
+            "ExpectedProtocol" => "TLSv1.1",
+            "ExpectedServerName" => "server2",
+        },
+    },
+);
+
+push @tests, @tests_tls_1_1 unless disabled("tls1_1");
index 66fb31c..f9e74d1 100644 (file)
@@ -237,6 +237,9 @@ static const test_enum ssl_servername_callbacks[] = {
     {"None", SSL_TEST_SERVERNAME_CB_NONE},
     {"IgnoreMismatch", SSL_TEST_SERVERNAME_IGNORE_MISMATCH},
     {"RejectMismatch", SSL_TEST_SERVERNAME_REJECT_MISMATCH},
+    {"EarlyIgnoreMismatch", SSL_TEST_SERVERNAME_EARLY_IGNORE_MISMATCH},
+    {"EarlyRejectMismatch", SSL_TEST_SERVERNAME_EARLY_REJECT_MISMATCH},
+    {"EarlyNoV12", SSL_TEST_SERVERNAME_EARLY_NO_V12},
 };
 
 __owur static int parse_servername_callback(SSL_TEST_SERVER_CONF *server_conf,
index 1c66740..499e314 100644 (file)
@@ -38,7 +38,10 @@ typedef enum {
 typedef enum {
     SSL_TEST_SERVERNAME_CB_NONE = 0,  /* Default */
     SSL_TEST_SERVERNAME_IGNORE_MISMATCH,
-    SSL_TEST_SERVERNAME_REJECT_MISMATCH
+    SSL_TEST_SERVERNAME_REJECT_MISMATCH,
+    SSL_TEST_SERVERNAME_EARLY_IGNORE_MISMATCH,
+    SSL_TEST_SERVERNAME_EARLY_REJECT_MISMATCH,
+    SSL_TEST_SERVERNAME_EARLY_NO_V12
 } ssl_servername_callback_t;
 
 typedef enum {