diff --git a/test/README.ssltest.md b/test/README.ssltest.md index 53ee0b433c..d6b020d01a 100644 --- a/test/README.ssltest.md +++ b/test/README.ssltest.md @@ -143,6 +143,11 @@ client => { - server2 - the secondary context - invalid - an unknown context +* CTValidation - Certificate Transparency validation strategy. One of + - None - no validation (default) + - Permissive - SSL_CT_VALIDATION_PERMISSIVE + - Strict - SSL_CT_VALIDATION_STRICT + #### Supported server-side options * ServerNameCallback - the SNI switching callback to use @@ -212,6 +217,10 @@ $ TEST_CERTS_DIR=test/certs util/shlib_wrap.sh test/ssl_test \ test/ssl-tests/01-simple.conf ``` +Some tests also need additional environment variables; for example, Certificate +Transparency tests need a `CTLOG_FILE`. See `test/recipes/80-test_ssl_new.t` for +details. + Note that the test expectations sometimes depend on the Configure settings. For example, the negotiated protocol depends on the set of available (enabled) protocols: a build with `enable-ssl3` has different test expectations than a diff --git a/test/handshake_helper.c b/test/handshake_helper.c index be689dc240..9fa6019824 100644 --- a/test/handshake_helper.c +++ b/test/handshake_helper.c @@ -374,6 +374,22 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx, OPENSSL_assert(SSL_CTX_set_tlsext_ticket_keys(server_ctx, ticket_keys, ticket_key_len) == 1); OPENSSL_free(ticket_keys); + +#ifndef OPENSSL_NO_CT + OPENSSL_assert(SSL_CTX_set_default_ctlog_list_file(client_ctx)); + switch (extra->client.ct_validation) { + case SSL_TEST_CT_VALIDATION_PERMISSIVE: + OPENSSL_assert(SSL_CTX_enable_ct(client_ctx, + SSL_CT_VALIDATION_PERMISSIVE)); + break; + case SSL_TEST_CT_VALIDATION_STRICT: + OPENSSL_assert(SSL_CTX_enable_ct(client_ctx, + SSL_CT_VALIDATION_STRICT)); + break; + case SSL_TEST_CT_VALIDATION_NONE: + break; + } +#endif } /* Configure per-SSL callbacks and other properties. */ diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t index 877a087e8a..1530bc2f3d 100644 --- a/test/recipes/80-test_ssl_new.t +++ b/test/recipes/80-test_ssl_new.t @@ -20,6 +20,7 @@ use OpenSSL::Test::Utils qw/disabled alldisabled available_protocols/; setup("test_ssl_new"); $ENV{TEST_CERTS_DIR} = srctop_dir("test", "certs"); +$ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf"); my @conf_srcs = glob(srctop_file("test", "ssl-tests", "*.conf.in")); map { s/;.*// } @conf_srcs if $^O eq "VMS"; @@ -28,7 +29,7 @@ map { s/\.in// } @conf_files; # We hard-code the number of tests to double-check that the globbing above # finds all files as expected. -plan tests => 11; # = scalar @conf_srcs +plan tests => 12; # = scalar @conf_srcs # Some test results depend on the configuration of enabled protocols. We only # verify generated sources in the default configuration. @@ -40,6 +41,7 @@ my $is_default_dtls = (!disabled("dtls1") && !disabled("dtls1_2")); my $no_tls = alldisabled(available_protocols("tls")); my $no_dtls = alldisabled(available_protocols("dtls")); my $no_npn = disabled("nextprotoneg"); +my $no_ct = disabled("ct"); my %conf_dependent_tests = ( "02-protocol-version.conf" => !$is_default_tls, @@ -55,6 +57,7 @@ my %skip = ( "08-npn.conf" => $no_tls || $no_npn, "10-resumption.conf" => disabled("tls1_1") || disabled("tls1_2"), "11-dtls_resumption.conf" => disabled("dtls1") || disabled("dtls1_2"), + "12-ct.conf" => $no_tls || $no_ct, ); foreach my $conf (@conf_files) { diff --git a/test/recipes/80-test_ssl_old.t b/test/recipes/80-test_ssl_old.t index 631adbf7eb..22bb226470 100644 --- a/test/recipes/80-test_ssl_old.t +++ b/test/recipes/80-test_ssl_old.t @@ -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 - +8 # For the first testssl + +7 # For the first testssl ; subtest 'test_ss' => sub { @@ -601,28 +601,6 @@ sub testssl { ok(run(test([@ssltest, "-cipher", "AES128-SHA256", "-bytes", "8m"]))); } }; - - subtest 'Certificate Transparency tests' => sub { - ###################################################################### - - plan tests => 3; - - SKIP: { - skip "Certificate Transparency is not supported by this OpenSSL build", 3 - if $no_ct; - skip "TLSv1.0 is not supported by this OpenSSL build", 3 - if $no_tls1; - - $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf"); - my @ca = qw(-CAfile certCA.ss); - ok(run(test([@ssltest, @ca, "-bio_pair", "-tls1", "-noct"]))); - # No SCTs provided, so this should fail. - ok(run(test([@ssltest, @ca, "-bio_pair", "-tls1", "-ct", - "-should_negotiate", "fail-client"]))); - # No SCTs provided, unverified chains still succeed. - ok(run(test([@ssltest, "-bio_pair", "-tls1", "-ct"]))); - } - }; } unlink $CAkey; diff --git a/test/ssl-tests/12-ct.conf b/test/ssl-tests/12-ct.conf new file mode 100644 index 0000000000..22fa18dd45 --- /dev/null +++ b/test/ssl-tests/12-ct.conf @@ -0,0 +1,135 @@ +# Generated with generate_ssl_tests.pl + +num_tests = 4 + +test-0 = 0-ct-permissive +test-1 = 1-ct-strict +test-2 = 2-ct-permissive-resumption +test-3 = 3-ct-strict-resumption +# =========================================================== + +[0-ct-permissive] +ssl_conf = 0-ct-permissive-ssl + +[0-ct-permissive-ssl] +server = 0-ct-permissive-server +client = 0-ct-permissive-client + +[0-ct-permissive-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[0-ct-permissive-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-0] +ExpectedResult = Success +client = 0-ct-permissive-client-extra + +[0-ct-permissive-client-extra] +CTValidation = Permissive + + +# =========================================================== + +[1-ct-strict] +ssl_conf = 1-ct-strict-ssl + +[1-ct-strict-ssl] +server = 1-ct-strict-server +client = 1-ct-strict-client + +[1-ct-strict-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[1-ct-strict-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-1] +ExpectedClientAlert = HandshakeFailure +ExpectedResult = ClientFail +client = 1-ct-strict-client-extra + +[1-ct-strict-client-extra] +CTValidation = Strict + + +# =========================================================== + +[2-ct-permissive-resumption] +ssl_conf = 2-ct-permissive-resumption-ssl + +[2-ct-permissive-resumption-ssl] +server = 2-ct-permissive-resumption-server +client = 2-ct-permissive-resumption-client +resume-server = 2-ct-permissive-resumption-server +resume-client = 2-ct-permissive-resumption-client + +[2-ct-permissive-resumption-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[2-ct-permissive-resumption-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-2] +ExpectedResult = Success +HandshakeMode = Resume +ResumptionExpected = Yes +client = 2-ct-permissive-resumption-client-extra +resume-client = 2-ct-permissive-resumption-client-extra + +[2-ct-permissive-resumption-client-extra] +CTValidation = Permissive + + +# =========================================================== + +[3-ct-strict-resumption] +ssl_conf = 3-ct-strict-resumption-ssl + +[3-ct-strict-resumption-ssl] +server = 3-ct-strict-resumption-server +client = 3-ct-strict-resumption-client +resume-server = 3-ct-strict-resumption-server +resume-client = 3-ct-strict-resumption-resume-client + +[3-ct-strict-resumption-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[3-ct-strict-resumption-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[3-ct-strict-resumption-resume-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-3] +ExpectedResult = Success +HandshakeMode = Resume +ResumptionExpected = Yes +client = 3-ct-strict-resumption-client-extra +resume-client = 3-ct-strict-resumption-resume-client-extra + +[3-ct-strict-resumption-client-extra] +CTValidation = Permissive + +[3-ct-strict-resumption-resume-client-extra] +CTValidation = Strict + + diff --git a/test/ssl-tests/12-ct.conf.in b/test/ssl-tests/12-ct.conf.in new file mode 100644 index 0000000000..9964d013c2 --- /dev/null +++ b/test/ssl-tests/12-ct.conf.in @@ -0,0 +1,80 @@ +# -*- mode: perl; -*- +# Copyright 2016-2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +## Test version negotiation + +use strict; +use warnings; + +package ssltests; + + +our @tests = ( + # Currently only have tests for certs without SCTs. + { + name => "ct-permissive", + server => { }, + client => { + extra => { + "CTValidation" => "Permissive", + }, + }, + test => { + "ExpectedResult" => "Success", + }, + }, + { + name => "ct-strict", + server => { }, + client => { + extra => { + "CTValidation" => "Strict", + }, + }, + test => { + "ExpectedResult" => "ClientFail", + "ExpectedClientAlert" => "HandshakeFailure", + }, + }, + { + name => "ct-permissive-resumption", + server => { }, + client => { + extra => { + "CTValidation" => "Permissive", + }, + }, + test => { + "HandshakeMode" => "Resume", + "ResumptionExpected" => "Yes", + "ExpectedResult" => "Success", + }, + }, + { + name => "ct-strict-resumption", + server => { }, + client => { + extra => { + "CTValidation" => "Permissive", + }, + }, + # SCTs are not present during resumption, so the resumption + # should succeed. + resume_client => { + extra => { + "CTValidation" => "Strict", + }, + }, + test => { + "HandshakeMode" => "Resume", + "ResumptionExpected" => "Yes", + "ExpectedResult" => "Success", + }, + }, +); diff --git a/test/ssl_test_ctx.c b/test/ssl_test_ctx.c index d4a7c8ba59..e95c1f2f66 100644 --- a/test/ssl_test_ctx.c +++ b/test/ssl_test_ctx.c @@ -142,7 +142,7 @@ static const test_enum ssl_verify_callbacks[] = { }; __owur static int parse_client_verify_callback(SSL_TEST_CLIENT_CONF *client_conf, - const char *value) + const char *value) { int ret_value; if (!parse_enum(ssl_verify_callbacks, OSSL_NELEM(ssl_verify_callbacks), @@ -328,6 +328,34 @@ const char *ssl_handshake_mode_name(ssl_handshake_mode_t mode) mode); } +/***********************/ +/* CT Validation */ +/***********************/ + +static const test_enum ssl_ct_validation_modes[] = { + {"None", SSL_TEST_CT_VALIDATION_NONE}, + {"Permissive", SSL_TEST_CT_VALIDATION_PERMISSIVE}, + {"Strict", SSL_TEST_CT_VALIDATION_STRICT}, +}; + +__owur static int parse_ct_validation(SSL_TEST_CLIENT_CONF *client_conf, + const char *value) +{ + int ret_value; + if (!parse_enum(ssl_ct_validation_modes, OSSL_NELEM(ssl_ct_validation_modes), + &ret_value, value)) { + return 0; + } + client_conf->ct_validation = ret_value; + return 1; +} + +const char *ssl_ct_validation_name(ssl_ct_validation_t mode) +{ + return enum_name(ssl_ct_validation_modes, OSSL_NELEM(ssl_ct_validation_modes), + mode); +} + static int parse_boolean(const char *value, int *result) { if (strcasecmp(value, "Yes") == 0) { @@ -385,6 +413,7 @@ static const ssl_test_client_option ssl_test_client_options[] = { { "ServerName", &parse_servername }, { "NPNProtocols", &parse_client_npn_protocols }, { "ALPNProtocols", &parse_client_alpn_protocols }, + { "CTValidation", &parse_ct_validation }, }; /* Nested server options. */ diff --git a/test/ssl_test_ctx.h b/test/ssl_test_ctx.h index a939f3d560..916b31aa89 100644 --- a/test/ssl_test_ctx.h +++ b/test/ssl_test_ctx.h @@ -60,6 +60,11 @@ typedef enum { SSL_TEST_HANDSHAKE_RENEGOTIATE } ssl_handshake_mode_t; +typedef enum { + SSL_TEST_CT_VALIDATION_NONE = 0, /* Default */ + SSL_TEST_CT_VALIDATION_PERMISSIVE, + SSL_TEST_CT_VALIDATION_STRICT +} ssl_ct_validation_t; /* * Server/client settings that aren't supported by the SSL CONF library, * such as callbacks. @@ -72,6 +77,7 @@ typedef struct { /* Supported NPN and ALPN protocols. A comma-separated list. */ char *npn_protocols; char *alpn_protocols; + ssl_ct_validation_t ct_validation; } SSL_TEST_CLIENT_CONF; typedef struct { @@ -150,6 +156,7 @@ const char *ssl_servername_callback_name(ssl_servername_callback_t const char *ssl_session_ticket_name(ssl_session_ticket_t server); const char *ssl_test_method_name(ssl_test_method_t method); const char *ssl_handshake_mode_name(ssl_handshake_mode_t mode); +const char *ssl_ct_validation_name(ssl_ct_validation_t mode); /* * Load the test case context from |conf|. diff --git a/test/ssl_test_ctx_test.c b/test/ssl_test_ctx_test.c index b7f888a418..479bda4d58 100644 --- a/test/ssl_test_ctx_test.c +++ b/test/ssl_test_ctx_test.c @@ -54,6 +54,12 @@ static int SSL_TEST_CLIENT_CONF_equal(SSL_TEST_CLIENT_CONF *client, if (!strings_equal("Client ALPNProtocols", client->alpn_protocols, client2->alpn_protocols)) return 0; + if (client->ct_validation != client2->ct_validation) { + fprintf(stderr, "CTValidation mismatch: %s vs %s.\n", + ssl_ct_validation_name(client->ct_validation), + ssl_ct_validation_name(client2->ct_validation)); + return 0; + } return 1; } @@ -259,6 +265,9 @@ static int test_good_configuration() OPENSSL_assert( fixture.expected_ctx->resume_extra.server2.alpn_protocols != NULL); + fixture.expected_ctx->resume_extra.client.ct_validation = + SSL_TEST_CT_VALIDATION_STRICT; + EXECUTE_SSL_TEST_CTX_TEST(); } @@ -275,6 +284,7 @@ static const char *bad_configurations[] = { "ssltest_unknown_method", "ssltest_unknown_handshake_mode", "ssltest_unknown_resumption_expected", + "ssltest_unknown_ct_validation", }; static int test_bad_configuration(int idx) diff --git a/test/ssl_test_ctx_test.conf b/test/ssl_test_ctx_test.conf index 20ae5d3068..3c46d96738 100644 --- a/test/ssl_test_ctx_test.conf +++ b/test/ssl_test_ctx_test.conf @@ -4,6 +4,7 @@ client = ssltest_good_client_extra server = ssltest_good_server_extra resume-server2 = ssltest_good_resume_server2_extra +resume-client = ssltest_good_resume_client_extra Method = DTLS HandshakeMode = Resume @@ -20,6 +21,9 @@ VerifyCallback = RejectAll ServerName = server2 NPNProtocols = foo,bar +[ssltest_good_resume_client_extra] +CTValidation = Strict + [ssltest_good_server_extra] ServerNameCallback = IgnoreMismatch BrokenSessionTicket = Yes @@ -74,3 +78,9 @@ HandshakeMode = Foo [ssltest_unknown_resumption_expected] ResumptionExpected = Foo + +[ssltest_unknown_ct_validation] +client = ssltest_unknown_ct_validation_client + +[ssltest_unknown_ct_validation_client] +CTCallback = Foo