Commit 590ed3d7 authored by Emilia Kasper's avatar Emilia Kasper
Browse files

SSL test framework: port resumption tests



Systematically test every server-side version downgrade or upgrade.

Client version upgrade or downgrade could be tested analogously but will
be done in a later change.

Reviewed-by: default avatarRich Salz <rsalz@openssl.org>
Reviewed-by: default avatarMatt Caswell <matt@openssl.org>
parent 23dd0c9f
Loading
Loading
Loading
Loading
+24 −4
Original line number Diff line number Diff line
@@ -82,7 +82,20 @@ The test section supports the following options:
  - Ignore - do not check for a session ticket (default)
  - Yes - a session ticket is expected
  - No - a session ticket is not expected
  - Broken - a special test case where the session ticket callback does not initialize crypto
  - Broken - a special test case where the session ticket callback does not
    initialize crypto

* HandshakeMode - which handshake flavour to test:
  - Simple - plain handshake (default)
  - Resume - test resumption
  - (Renegotiate - test renegotiation, not yet implemented)

* ResumptionExpected - whether or not resumption is expected (Resume mode only)
  - Yes - resumed handshake
  - No - full handshake (default)

When HandshakeMode is Resume or Renegotiate, the original handshake is expected
to succeed. All configured test expectations are verified against the second handshake.

* ServerNPNProtocols, Server2NPNProtocols, ClientNPNProtocols, ExpectedNPNProtocol,
  ServerALPNProtocols, Server2ALPNProtocols, ClientALPNProtocols, ExpectedALPNProtocol -
@@ -103,9 +116,16 @@ server => {
}
```

A server2 section may optionally be defined to configure a secondary
context that is selected via the ServerName test option. If the server2
section is not configured, then the configuration matches server.
The following sections may optionally be defined:

* server2 - this section configures a secondary context that is selected via the
  ServerName test option. This context is used whenever a ServerNameCallback is
  specified. If the server2 section is not present, then the configuration
  matches server.
* resume_server - this section configures the client to resume its session
  against a different server. This context is used whenever HandshakeMode is
  Resume. If the resume-server section is not present, then the configuration
  matches server.

### Default server and client configurations

+15 −2
Original line number Diff line number Diff line
@@ -43,12 +43,25 @@ sub print_templates {
    # Add the implicit base configuration.
    foreach my $test (@ssltests::tests) {
        $test->{"server"} = { (%ssltests::base_server, %{$test->{"server"}}) };
	# Do not emit an empty "server2" section.
        if (defined $test->{"server2"}) {
            $test->{"server2"} = { (%ssltests::base_server, %{$test->{"server2"}}) };
        } elsif (defined $test->{"test"}->{"ServerNameCallback"}) {
            # Default is the same as server.
            $test->{"server2"} = { (%ssltests::base_server, %{$test->{"server"}}) };
        } else {
            # Do not emit an empty "server2" section.
            $test->{"server2"} = { };
        }
        if (defined $test->{"resume_server"}) {
            $test->{"resume_server"} = { (%ssltests::base_server, %{$test->{"resume_server"}}) };
        } elsif (defined $test->{"test"}->{"HandshakeMode"} &&
                 $test->{"test"}->{"HandshakeMode"} eq "Resume") {
            # Default is the same as server.
            $test->{"resume_server"} = { (%ssltests::base_server, %{$test->{"server"}}) };
        } else {
            # Do not emit an empty "resume-server" section.
            $test->{"resume_server"} = { };
        }
        $test->{"client"} = { (%ssltests::base_client, %{$test->{"client"}}) };
    }

+87 −11
Original line number Diff line number Diff line
@@ -273,6 +273,9 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
                                    CTX_DATA *server2_ctx_data,
                                    CTX_DATA *client_ctx_data)
{
    unsigned char *ticket_keys;
    size_t ticket_key_len;

    switch (test_ctx->client_verify_callback) {
    case SSL_TEST_VERIFY_ACCEPT_ALL:
        SSL_CTX_set_cert_verify_callback(client_ctx, &verify_accept_cb,
@@ -312,7 +315,6 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
    if (test_ctx->session_ticket_expected == SSL_TEST_SESSION_TICKET_BROKEN) {
        SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, broken_session_ticket_cb);
    }

    if (test_ctx->server_npn_protocols != NULL) {
        parse_protos(test_ctx->server_npn_protocols,
                     &server_ctx_data->npn_protocols,
@@ -358,6 +360,16 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
                                               alpn_protos_len) == 0);
        OPENSSL_free(alpn_protos);
    }
    /*
     * Use fixed session ticket keys so that we can decrypt a ticket created with
     * one CTX in another CTX. Don't address server2 for the moment.
     */
    ticket_key_len = SSL_CTX_set_tlsext_ticket_keys(server_ctx, NULL, 0);
    ticket_keys = OPENSSL_zalloc(ticket_key_len);
    OPENSSL_assert(ticket_keys != NULL);
    OPENSSL_assert(SSL_CTX_set_tlsext_ticket_keys(server_ctx, ticket_keys,
                                                  ticket_key_len) == 1);
    OPENSSL_free(ticket_keys);
}

/* Configure per-SSL callbacks and other properties. */
@@ -376,16 +388,31 @@ typedef enum {
    PEER_ERROR
} peer_status_t;

static peer_status_t do_handshake_step(SSL *ssl)
/*
 * RFC 5246 says:
 *
 * Note that as of TLS 1.1,
 *     failure to properly close a connection no longer requires that a
 *     session not be resumed.  This is a change from TLS 1.0 to conform
 *     with widespread implementation practice.
 *
 * However,
 * (a) OpenSSL requires that a connection be shutdown for all protocol versions.
 * (b) We test lower versions, too.
 * So we just implement shutdown. We do a full bidirectional shutdown so that we
 * can compare sent and received close_notify alerts and get some test coverage
 * for SSL_shutdown as a bonus.
 */
static peer_status_t do_handshake_step(SSL *ssl, int shutdown)
{
    int ret;

    ret = SSL_do_handshake(ssl);
    ret = shutdown ? SSL_shutdown(ssl) : SSL_do_handshake(ssl);

    if (ret == 1) {
        return PEER_SUCCESS;
    } else if (ret == 0) {
        return PEER_ERROR;
        return shutdown ? PEER_RETRY : PEER_ERROR;
    } else {
        int error = SSL_get_error(ssl, ret);
        /* Memory bios should never block with SSL_ERROR_WANT_WRITE. */
@@ -484,15 +511,17 @@ static char *dup_str(const unsigned char *in, size_t len)
    return ret;
}

HANDSHAKE_RESULT *do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
                               SSL_CTX *client_ctx, const SSL_TEST_CTX *test_ctx)
static HANDSHAKE_RESULT *do_handshake_internal(
    SSL_CTX *server_ctx, SSL_CTX *server2_ctx, SSL_CTX *client_ctx,
    const SSL_TEST_CTX *test_ctx, SSL_SESSION *session_in,
    SSL_SESSION **session_out)
{
    SSL *server, *client;
    BIO *client_to_server, *server_to_client;
    HANDSHAKE_EX_DATA server_ex_data, client_ex_data;
    CTX_DATA client_ctx_data, server_ctx_data, server2_ctx_data;
    HANDSHAKE_RESULT *ret = HANDSHAKE_RESULT_new();
    int client_turn = 1;
    int client_turn = 1, shutdown = 0;
    peer_status_t client_status = PEER_RETRY, server_status = PEER_RETRY;
    handshake_status_t status = HANDSHAKE_RETRY;
    unsigned char* tick = NULL;
@@ -514,6 +543,11 @@ HANDSHAKE_RESULT *do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
    OPENSSL_assert(server != NULL && client != NULL);

    configure_handshake_ssl(server, client, test_ctx);
    if (session_in != NULL) {
        /* In case we're testing resumption without tickets. */
        OPENSSL_assert(SSL_CTX_add_session(server_ctx, session_in));
        OPENSSL_assert(SSL_set_session(client, session_in));
    }

    memset(&server_ex_data, 0, sizeof(server_ex_data));
    memset(&client_ex_data, 0, sizeof(client_ex_data));
@@ -559,19 +593,26 @@ HANDSHAKE_RESULT *do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
     */
    for(;;) {
        if (client_turn) {
            client_status = do_handshake_step(client);
            client_status = do_handshake_step(client, shutdown);
            status = handshake_status(client_status, server_status,
                                      1 /* client went last */);
        } else {
            server_status = do_handshake_step(server);
            server_status = do_handshake_step(server, shutdown);
            status = handshake_status(server_status, client_status,
                                      0 /* server went last */);
        }

        switch (status) {
        case HANDSHAKE_SUCCESS:
            if (shutdown) {
                ret->result = SSL_TEST_SUCCESS;
                goto err;
            } else {
                client_status = server_status = PEER_RETRY;
                shutdown = 1;
                client_turn = 1;
                break;
            }
        case CLIENT_ERROR:
            ret->result = SSL_TEST_CLIENT_FAIL;
            goto err;
@@ -615,10 +656,45 @@ HANDSHAKE_RESULT *do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
    SSL_get0_alpn_selected(server, &proto, &proto_len);
    ret->server_alpn_negotiated = dup_str(proto, proto_len);

    ret->client_resumed = SSL_session_reused(client);
    ret->server_resumed = SSL_session_reused(server);

    if (session_out != NULL)
        *session_out = SSL_get1_session(client);

    ctx_data_free_data(&server_ctx_data);
    ctx_data_free_data(&server2_ctx_data);
    ctx_data_free_data(&client_ctx_data);

    SSL_free(server);
    SSL_free(client);
    return ret;
}

HANDSHAKE_RESULT *do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
                               SSL_CTX *client_ctx, SSL_CTX *resume_server_ctx,
                               const SSL_TEST_CTX *test_ctx)
{
    HANDSHAKE_RESULT *result;
    SSL_SESSION *session = NULL;

    result = do_handshake_internal(server_ctx, server2_ctx, client_ctx,
                                   test_ctx, NULL, &session);
    if (test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_SIMPLE)
        goto end;

    OPENSSL_assert(test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_RESUME);

    if (result->result != SSL_TEST_SUCCESS) {
        result->result = SSL_TEST_FIRST_HANDSHAKE_FAILED;
        return result;
    }

    HANDSHAKE_RESULT_free(result);
    /* We don't support SNI on second handshake yet, so server2_ctx is NULL. */
    result = do_handshake_internal(resume_server_ctx, NULL, client_ctx, test_ctx,
                                   session, NULL);
 end:
    SSL_SESSION_free(session);
    return result;
}
+5 −1
Original line number Diff line number Diff line
@@ -36,6 +36,9 @@ typedef struct handshake_result {
    char *server_npn_negotiated;
    char *client_alpn_negotiated;
    char *server_alpn_negotiated;
    /* Was the handshake resumed? */
    int client_resumed;
    int server_resumed;
} HANDSHAKE_RESULT;

HANDSHAKE_RESULT *HANDSHAKE_RESULT_new(void);
@@ -43,6 +46,7 @@ void HANDSHAKE_RESULT_free(HANDSHAKE_RESULT *result);

/* Do a handshake and report some information about the result. */
HANDSHAKE_RESULT *do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
                               SSL_CTX *client_ctx, const SSL_TEST_CTX *test_ctx);
                               SSL_CTX *client_ctx, SSL_CTX *resume_server_ctx,
                               const SSL_TEST_CTX *test_ctx);

#endif  /* HEADER_HANDSHAKE_HELPER_H */
+4 −1
Original line number Diff line number Diff line
@@ -42,12 +42,15 @@ my %conf_dependent_tests = (
  "02-protocol-version.conf" => !$is_default_tls,
  "04-client_auth.conf" => !$is_default_tls,
  "05-dtls-protocol-version.conf" => !$is_default_dtls,
  "10-resumption.conf" => !$is_default_tls,
  "11-dtls_resumption.conf" => !$is_default_dtls,
);

# Default is $no_tls but some tests have different skip conditions.
my %skip = (
  "05-dtls-protocol-version.conf" => $no_dtls,
  "08-npn.conf" => $no_tls || $no_npn,
  "11-dtls_resumption.conf" => $no_dtls,
);

foreach my $conf (@conf_files) {
@@ -60,7 +63,7 @@ foreach my $conf (@conf_files) {

# We hard-code the number of tests to double-check that the globbing above
# finds all files as expected.
plan tests => 9;  # = scalar @conf_srcs
plan tests => 11;  # = scalar @conf_srcs

sub test_conf {
    plan tests => 3;
Loading