Commit d2b23cd2 authored by Emilia Kasper's avatar Emilia Kasper
Browse files

SSL test framework: port SNI tests



Observe that the old tests were partly ill-defined:
setting sn_server1 but not sn_server2 in ssltest_old.c does not enable
the SNI callback.

Fix this, and also explicitly test both flavours of SNI mismatch (ignore
/ fatal alert). Tests still pass.

Reviewed-by: default avatarRich Salz <rsalz@openssl.org>
parent 2cdce3e3
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ The test section supports the following options:

* ClientVerifyCallback - the client's custom certificate verify callback.
  Used to test callback behaviour. One of
  - None - no custom callback (default)
  - AcceptAll - accepts all certificates.
  - RejectAll - rejects all certificates.

@@ -70,6 +71,12 @@ The test section supports the following options:
  - None - do not use SNI (default)
  - server1 - the initial context
  - server2 - the secondary context
  - invalid - an unknown context

* ServerNameCallback - the SNI switching callback to use
  - None - no callback (default)
  - IgnoreMismatch - continue the handshake on SNI mismatch
  - RejectMismatch - abort the handshake on SNI mismatch

* SessionTicketExpected - whether or not a session ticket is expected
  - Ignore - do not check for a session ticket (default)
+68 −9
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ typedef struct handshake_ex_data {
    int alert_sent;
    int alert_received;
    int session_ticket_do_not_call;
    ssl_servername_t servername;
} HANDSHAKE_EX_DATA;

static int ex_data_idx;
@@ -41,10 +42,25 @@ static void info_cb(const SSL *s, int where, int ret)
    }
}

static int servername_cb(SSL *s, int *ad, void *arg)
/*
 * Select the appropriate server CTX.
 * Returns SSL_TLSEXT_ERR_OK if a match was found.
 * If |ignore| is 1, returns SSL_TLSEXT_ERR_NOACK on mismatch.
 * Otherwise, returns SSL_TLSEXT_ERR_ALERT_FATAL on mismatch.
 * An empty SNI extension also returns SSL_TSLEXT_ERR_NOACK.
 */
static int select_server_ctx(SSL *s, void *arg, int ignore)
{
    const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
    if (servername != NULL && !strcmp(servername, "server2")) {
    HANDSHAKE_EX_DATA *ex_data =
        (HANDSHAKE_EX_DATA*)(SSL_get_ex_data(s, ex_data_idx));

    if (servername == NULL) {
        ex_data->servername = SSL_TEST_SERVERNAME_SERVER1;
        return SSL_TLSEXT_ERR_NOACK;
    }

    if (strcmp(servername, "server2") == 0) {
        SSL_CTX *new_ctx = (SSL_CTX*)arg;
        SSL_set_SSL_CTX(s, new_ctx);
        /*
@@ -54,8 +70,40 @@ static int servername_cb(SSL *s, int *ad, void *arg)
         */
        SSL_clear_options(s, 0xFFFFFFFFL);
        SSL_set_options(s, SSL_CTX_get_options(new_ctx));
    }

        ex_data->servername = SSL_TEST_SERVERNAME_SERVER2;
        return SSL_TLSEXT_ERR_OK;
    } else if (strcmp(servername, "server1") == 0) {
        ex_data->servername = SSL_TEST_SERVERNAME_SERVER1;
        return SSL_TLSEXT_ERR_OK;
    } else if (ignore) {
        ex_data->servername = SSL_TEST_SERVERNAME_SERVER1;
        return SSL_TLSEXT_ERR_NOACK;
    } else {
        /* Don't set an explicit alert, to test library defaults. */
        return SSL_TLSEXT_ERR_ALERT_FATAL;
    }
}

/*
 * (RFC 6066):
 *  If the server understood the ClientHello extension but
 *  does not recognize the server name, the server SHOULD take one of two
 *  actions: either abort the handshake by sending a fatal-level
 *  unrecognized_name(112) alert or continue the handshake.
 *
 * This behaviour is up to the application to configure; we test both
 * configurations to ensure the state machine propagates the result
 * correctly.
 */
static int servername_ignore_cb(SSL *s, int *ad, void *arg)
{
    return select_server_ctx(s, arg, 1);
}

static int servername_reject_cb(SSL *s, int *ad, void *arg)
{
    return select_server_ctx(s, arg, 0);
}

static int verify_reject_cb(X509_STORE_CTX *ctx, void *arg) {
@@ -106,14 +154,27 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
    }

    /* link the two contexts for SNI purposes */
    SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_cb);
    switch (test_ctx->servername_callback) {
    case SSL_TEST_SERVERNAME_IGNORE_MISMATCH:
        SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_ignore_cb);
        SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx);
        break;
    case SSL_TEST_SERVERNAME_REJECT_MISMATCH:
        SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_reject_cb);
        SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx);
        break;
    default:
        break;
    }

    /*
     * The initial_ctx/session_ctx always handles the encrypt/decrypt of the
     * session ticket. This ticket_key callback is assigned to the second
     * session (assigned via SNI), and should never be invoked
     */
    SSL_CTX_set_tlsext_ticket_key_cb(server2_ctx, do_not_call_session_ticket_cb);
    if (server2_ctx != NULL)
        SSL_CTX_set_tlsext_ticket_key_cb(server2_ctx,
                                         do_not_call_session_ticket_cb);

    if (test_ctx->session_ticket_expected == SSL_TEST_SESSION_TICKET_BROKEN) {
        SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, broken_session_ticket_cb);
@@ -333,9 +394,7 @@ HANDSHAKE_RESULT do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
    ret.client_alert_received = server_ex_data.alert_received;
    ret.server_protocol = SSL_version(server);
    ret.client_protocol = SSL_version(client);
    ret.servername = ((SSL_get_SSL_CTX(server) == server_ctx)
                      ? SSL_TEST_SERVERNAME_SERVER1
                      : SSL_TEST_SERVERNAME_SERVER2);
    ret.servername = server_ex_data.servername;
    if ((sess = SSL_get0_session(client)) != NULL)
        SSL_SESSION_get0_ticket(sess, &tick, &len);
    if (tick == NULL || len == 0)
+1 −20
Original line number Diff line number Diff line
@@ -79,7 +79,7 @@ my $client_sess="client.ss";
# new format in ssl_test.c and add recipes to 80-test_ssl_new.t instead.
plan tests =>
    1				# For testss
    + 13			# For the first testssl
    + 12			# For the first testssl
    ;

subtest 'test_ss' => sub {
@@ -579,25 +579,6 @@ sub testssl {
	}
    };

    subtest 'SNI tests' => sub {

	plan tests => 7;

      SKIP: {
	  skip "TLSv1.x is not supported by this OpenSSL build", 7
	      if $no_tls1 && $no_tls1_1 && $no_tls1_2;

	  ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foo"])));
	  ok(run(test([@ssltest, "-bio_pair", "-sn_server1", "foo"])));
	  ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foo", "-sn_server1", "foo", "-sn_expect1"])));
	  ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foo", "-sn_server1", "bar", "-sn_expect1"])));
	  ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foo", "-sn_server1", "foo", "-sn_server2", "bar", "-sn_expect1"])));
	  ok(run(test([@ssltest, "-bio_pair", "-sn_client", "bar", "-sn_server1", "foo", "-sn_server2", "bar", "-sn_expect2"])));
	  # Negative test - make sure it doesn't crash, and doesn't switch contexts
	  ok(run(test([@ssltest, "-bio_pair", "-sn_client", "foobar", "-sn_server1", "foo", "-sn_server2", "bar", "-sn_expect1"])));
	}
    };

    subtest 'ALPN tests' => sub {
	######################################################################

+169 −11
Original line number Diff line number Diff line
# Generated with generate_ssl_tests.pl

num_tests = 1
num_tests = 6

test-0 = 0-SNI-default
test-0 = 0-SNI-switch-context
test-1 = 1-SNI-keep-context
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
# ===========================================================

[0-SNI-default]
ssl_conf = 0-SNI-default-ssl
[0-SNI-switch-context]
ssl_conf = 0-SNI-switch-context-ssl

[0-SNI-default-ssl]
server = 0-SNI-default-server
server2 = 0-SNI-default-server2
client = 0-SNI-default-client
[0-SNI-switch-context-ssl]
server = 0-SNI-switch-context-server
server2 = 0-SNI-switch-context-server2
client = 0-SNI-switch-context-client

[0-SNI-default-server]
[0-SNI-switch-context-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[0-SNI-default-server2]
[0-SNI-switch-context-server2]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[0-SNI-default-client]
[0-SNI-switch-context-client]
CipherString = DEFAULT
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer

[test-0]
ExpectedResult = Success
ExpectedServerName = server2
ServerName = server2
ServerNameCallback = IgnoreMismatch


# ===========================================================

[1-SNI-keep-context]
ssl_conf = 1-SNI-keep-context-ssl

[1-SNI-keep-context-ssl]
server = 1-SNI-keep-context-server
server2 = 1-SNI-keep-context-server2
client = 1-SNI-keep-context-client

[1-SNI-keep-context-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[1-SNI-keep-context-server2]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[1-SNI-keep-context-client]
CipherString = DEFAULT
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer

[test-1]
ExpectedResult = Success
ExpectedServerName = server1
ServerName = server1
ServerNameCallback = IgnoreMismatch


# ===========================================================

[2-SNI-no-server-support]
ssl_conf = 2-SNI-no-server-support-ssl

[2-SNI-no-server-support-ssl]
server = 2-SNI-no-server-support-server
client = 2-SNI-no-server-support-client

[2-SNI-no-server-support-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[2-SNI-no-server-support-client]
CipherString = DEFAULT
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer

[test-2]
ExpectedResult = Success
ServerName = server1


# ===========================================================

[3-SNI-no-client-support]
ssl_conf = 3-SNI-no-client-support-ssl

[3-SNI-no-client-support-ssl]
server = 3-SNI-no-client-support-server
server2 = 3-SNI-no-client-support-server2
client = 3-SNI-no-client-support-client

[3-SNI-no-client-support-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[3-SNI-no-client-support-server2]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[3-SNI-no-client-support-client]
CipherString = DEFAULT
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer

[test-3]
ExpectedResult = Success
ExpectedServerName = server1
ServerNameCallback = IgnoreMismatch


# ===========================================================

[4-SNI-bad-sni-ignore-mismatch]
ssl_conf = 4-SNI-bad-sni-ignore-mismatch-ssl

[4-SNI-bad-sni-ignore-mismatch-ssl]
server = 4-SNI-bad-sni-ignore-mismatch-server
server2 = 4-SNI-bad-sni-ignore-mismatch-server2
client = 4-SNI-bad-sni-ignore-mismatch-client

[4-SNI-bad-sni-ignore-mismatch-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[4-SNI-bad-sni-ignore-mismatch-server2]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[4-SNI-bad-sni-ignore-mismatch-client]
CipherString = DEFAULT
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer

[test-4]
ExpectedResult = Success
ExpectedServerName = server1
ServerName = invalid
ServerNameCallback = IgnoreMismatch


# ===========================================================

[5-SNI-bad-sni-reject-mismatch]
ssl_conf = 5-SNI-bad-sni-reject-mismatch-ssl

[5-SNI-bad-sni-reject-mismatch-ssl]
server = 5-SNI-bad-sni-reject-mismatch-server
server2 = 5-SNI-bad-sni-reject-mismatch-server2
client = 5-SNI-bad-sni-reject-mismatch-client

[5-SNI-bad-sni-reject-mismatch-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[5-SNI-bad-sni-reject-mismatch-server2]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem

[5-SNI-bad-sni-reject-mismatch-client]
CipherString = DEFAULT
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer

[test-5]
ExpectedResult = ServerFail
ServerAlert = UnrecognizedName
ServerName = invalid
ServerNameCallback = RejectMismatch

+55 −2
Original line number Diff line number Diff line
@@ -16,11 +16,64 @@ package ssltests;

our @tests = (
    {
        name => "SNI-default",
        name => "SNI-switch-context",
        server => { },
        server2 => { },
        client => { },
        test   => { "ServerName" => "server2",
                    "ExpectedServerName" => "server2",
                    "ServerNameCallback" => "IgnoreMismatch",
                    "ExpectedResult" => "Success" },
    },
    {
        name => "SNI-keep-context",
        server => { },
        server2 => { },
        client => { },
        test   => { "ServerName" => "server1",
                    "ExpectedServerName" => "server1",
                    "ServerNameCallback" => "IgnoreMismatch",
                    "ExpectedResult" => "Success" },
    },
    {
        name => "SNI-no-server-support",
        server => { },
        client => { },
        test   => { "ServerName" => "server1",
                    "ExpectedResult" => "Success" },
    },
    {
        name => "SNI-no-client-support",
        server => { },
        server2 => { },
        client => { },
        test   => {
            # We expect that the callback is still called
            # to let the application decide whether they tolerate
            # missing SNI (as our test callback does).
            "ExpectedServerName" => "server1",
            "ServerNameCallback" => "IgnoreMismatch",
            "ExpectedResult" => "Success"
        },
    },
    {
        name => "SNI-bad-sni-ignore-mismatch",
        server => { },
        server2 => { },
        client => { },
        test   => { "ServerName" => "invalid",
                    "ExpectedServerName" => "server1",
                    "ServerNameCallback" => "IgnoreMismatch",
                    "ExpectedResult" => "Success" },
    },
    {
        name => "SNI-bad-sni-reject-mismatch",
        server => { },
        server2 => { },
        client => { },
        test   => { "ServerName" => "invalid",
                    "ServerNameCallback" => "RejectMismatch",
                    "ExpectedResult" => "ServerFail",
                    "ServerAlert" => "UnrecognizedName"},
    },
);
Loading