Add some CertStatus tests
authorMatt Caswell <matt@openssl.org>
Tue, 30 Aug 2016 13:20:18 +0000 (14:20 +0100)
committerMatt Caswell <matt@openssl.org>
Tue, 30 Aug 2016 13:49:10 +0000 (14:49 +0100)
The previous commit revealed a long standing problem where CertStatus
processing was broken in DTLS. This would have been revealed by better
testing - so add some!

Reviewed-by: Rich Salz <rsalz@openssl.org>
test/handshake_helper.c
test/recipes/80-test_ssl_new.t
test/ssl-tests/15-certstatus.conf [new file with mode: 0644]
test/ssl-tests/15-certstatus.conf.in [new file with mode: 0644]
test/ssl-tests/16-certstatus.conf [new file with mode: 0644]
test/ssl-tests/16-dtls-certstatus.conf [new file with mode: 0644]
test/ssl-tests/16-dtls-certstatus.conf.in [new file with mode: 0644]
test/ssl_test_ctx.c
test/ssl_test_ctx.h
test/ssl_test_ctx_test.c

index 409f16c..90e18fc 100644 (file)
@@ -144,6 +144,38 @@ static int servername_reject_cb(SSL *s, int *ad, void *arg)
     return select_server_ctx(s, arg, 0);
 }
 
+static unsigned char dummy_ocsp_resp_good_val = 0xff;
+static unsigned char dummy_ocsp_resp_bad_val = 0xfe;
+
+static int server_ocsp_cb(SSL *s, void *arg)
+{
+    unsigned char *resp;
+
+    resp = OPENSSL_malloc(1);
+    if (resp == NULL)
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+    /*
+     * For the purposes of testing we just send back a dummy OCSP response
+     */
+    *resp = *(unsigned char *)arg;
+    if (!SSL_set_tlsext_status_ocsp_resp(s, resp, 1))
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+    return SSL_TLSEXT_ERR_OK;
+}
+
+static int client_ocsp_cb(SSL *s, void *arg)
+{
+    const unsigned char *resp;
+    int len;
+
+    len = SSL_get_tlsext_status_ocsp_resp(s, &resp);
+    if (len != 1 || *resp != dummy_ocsp_resp_good_val)
+        return 0;
+
+    return 1;
+}
+
 static int verify_reject_cb(X509_STORE_CTX *ctx, void *arg) {
     X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
     return 0;
@@ -319,6 +351,16 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
         break;
     }
 
+    if (extra->server.cert_status != SSL_TEST_CERT_STATUS_NONE) {
+        SSL_CTX_set_tlsext_status_type(client_ctx, TLSEXT_STATUSTYPE_ocsp);
+        SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb);
+        SSL_CTX_set_tlsext_status_arg(client_ctx, NULL);
+        SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb);
+        SSL_CTX_set_tlsext_status_arg(server_ctx,
+            ((extra->server.cert_status == SSL_TEST_CERT_STATUS_GOOD_RESPONSE)
+            ? &dummy_ocsp_resp_good_val : &dummy_ocsp_resp_bad_val));
+    }
+
     /*
      * The initial_ctx/session_ctx always handles the encrypt/decrypt of the
      * session ticket. This ticket_key callback is assigned to the second
index 175b3b2..46c2f42 100644 (file)
@@ -29,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 => 14;  # = scalar @conf_srcs
+plan tests => 16;  # = scalar @conf_srcs
 
 # Some test results depend on the configuration of enabled protocols. We only
 # verify generated sources in the default configuration.
@@ -69,7 +69,8 @@ my %skip = (
   # special-casing for.
   # We should review this once we have TLS 1.3.
   "13-fragmentation.conf" => disabled("tls1_2"),
-  "14-curves.conf" => disabled("tls1_2") || $no_ec || $no_ec2m
+  "14-curves.conf" => disabled("tls1_2") || $no_ec || $no_ec2m,
+  "16-dtls-certstatus.conf" => $no_dtls
 );
 
 foreach my $conf (@conf_files) {
diff --git a/test/ssl-tests/15-certstatus.conf b/test/ssl-tests/15-certstatus.conf
new file mode 100644 (file)
index 0000000..bf6c41c
--- /dev/null
@@ -0,0 +1,62 @@
+# Generated with generate_ssl_tests.pl
+
+num_tests = 2
+
+test-0 = 0-certstatus-good
+test-1 = 1-certstatus-bad
+# ===========================================================
+
+[0-certstatus-good]
+ssl_conf = 0-certstatus-good-ssl
+
+[0-certstatus-good-ssl]
+server = 0-certstatus-good-server
+client = 0-certstatus-good-client
+
+[0-certstatus-good-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[0-certstatus-good-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-0]
+ExpectedResult = Success
+Method = TLS
+server = 0-certstatus-good-server-extra
+
+[0-certstatus-good-server-extra]
+CertStatus = GoodResponse
+
+
+# ===========================================================
+
+[1-certstatus-bad]
+ssl_conf = 1-certstatus-bad-ssl
+
+[1-certstatus-bad-ssl]
+server = 1-certstatus-bad-server
+client = 1-certstatus-bad-client
+
+[1-certstatus-bad-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[1-certstatus-bad-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-1]
+ExpectedResult = ClientFail
+Method = TLS
+server = 1-certstatus-bad-server-extra
+
+[1-certstatus-bad-server-extra]
+CertStatus = BadResponse
+
+
diff --git a/test/ssl-tests/15-certstatus.conf.in b/test/ssl-tests/15-certstatus.conf.in
new file mode 100644 (file)
index 0000000..074602d
--- /dev/null
@@ -0,0 +1,45 @@
+# -*- 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 CertStatus messages
+
+use strict;
+use warnings;
+
+package ssltests;
+
+
+our @tests = (
+    {
+        name => "certstatus-good",
+        server => {
+            extra => {
+                "CertStatus" => "GoodResponse",
+            },
+        },
+        client => {},
+        test => {
+            "Method" => "TLS",
+            "ExpectedResult" => "Success"
+        }
+    },
+    {
+        name => "certstatus-bad",
+        server => {
+            extra => {
+                "CertStatus" => "BadResponse",
+            },
+        },
+        client => {},
+        test => {
+            "Method" => "TLS",
+            "ExpectedResult" => "ClientFail"
+        }
+    },
+);
diff --git a/test/ssl-tests/16-certstatus.conf b/test/ssl-tests/16-certstatus.conf
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/ssl-tests/16-dtls-certstatus.conf b/test/ssl-tests/16-dtls-certstatus.conf
new file mode 100644 (file)
index 0000000..a561803
--- /dev/null
@@ -0,0 +1,62 @@
+# Generated with generate_ssl_tests.pl
+
+num_tests = 2
+
+test-0 = 0-certstatus-good
+test-1 = 1-certstatus-bad
+# ===========================================================
+
+[0-certstatus-good]
+ssl_conf = 0-certstatus-good-ssl
+
+[0-certstatus-good-ssl]
+server = 0-certstatus-good-server
+client = 0-certstatus-good-client
+
+[0-certstatus-good-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[0-certstatus-good-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-0]
+ExpectedResult = Success
+Method = DTLS
+server = 0-certstatus-good-server-extra
+
+[0-certstatus-good-server-extra]
+CertStatus = GoodResponse
+
+
+# ===========================================================
+
+[1-certstatus-bad]
+ssl_conf = 1-certstatus-bad-ssl
+
+[1-certstatus-bad-ssl]
+server = 1-certstatus-bad-server
+client = 1-certstatus-bad-client
+
+[1-certstatus-bad-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[1-certstatus-bad-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-1]
+ExpectedResult = ClientFail
+Method = DTLS
+server = 1-certstatus-bad-server-extra
+
+[1-certstatus-bad-server-extra]
+CertStatus = BadResponse
+
+
diff --git a/test/ssl-tests/16-dtls-certstatus.conf.in b/test/ssl-tests/16-dtls-certstatus.conf.in
new file mode 100644 (file)
index 0000000..7280029
--- /dev/null
@@ -0,0 +1,45 @@
+# -*- 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 DTLS CertStatus messages
+
+use strict;
+use warnings;
+
+package ssltests;
+
+
+our @tests = (
+    {
+        name => "certstatus-good",
+        server => {
+            extra => {
+                "CertStatus" => "GoodResponse",
+            },
+        },
+        client => {},
+        test => {
+            "Method" => "DTLS",
+            "ExpectedResult" => "Success"
+        }
+    },
+    {
+        name => "certstatus-bad",
+        server => {
+            extra => {
+                "CertStatus" => "BadResponse",
+            },
+        },
+        client => {},
+        test => {
+            "Method" => "DTLS",
+            "ExpectedResult" => "ClientFail"
+        }
+    },
+);
index ac90f19..ee2e8a1 100644 (file)
@@ -390,6 +390,34 @@ const char *ssl_ct_validation_name(ssl_ct_validation_t mode)
 IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_CTX, test, resumption_expected)
 IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_SERVER_CONF, server, broken_session_ticket)
 
+/**************/
+/* CertStatus */
+/**************/
+
+static const test_enum ssl_certstatus[] = {
+    {"None", SSL_TEST_CERT_STATUS_NONE},
+    {"GoodResponse", SSL_TEST_CERT_STATUS_GOOD_RESPONSE},
+    {"BadResponse", SSL_TEST_CERT_STATUS_BAD_RESPONSE}
+};
+
+__owur static int parse_certstatus(SSL_TEST_SERVER_CONF *server_conf,
+                                            const char *value)
+{
+    int ret_value;
+    if (!parse_enum(ssl_certstatus, OSSL_NELEM(ssl_certstatus), &ret_value,
+                    value)) {
+        return 0;
+    }
+    server_conf->cert_status = ret_value;
+    return 1;
+}
+
+const char *ssl_certstatus_name(ssl_cert_status_t cert_status)
+{
+    return enum_name(ssl_certstatus,
+                     OSSL_NELEM(ssl_certstatus), cert_status);
+}
+
 /***********************/
 /* ApplicationData     */
 /***********************/
@@ -453,6 +481,7 @@ static const ssl_test_server_option ssl_test_server_options[] = {
     { "NPNProtocols", &parse_server_npn_protocols },
     { "ALPNProtocols", &parse_server_alpn_protocols },
     { "BrokenSessionTicket", &parse_server_broken_session_ticket },
+    { "CertStatus", &parse_certstatus },
 };
 
 /*
index 11b6c33..c8c66d6 100644 (file)
@@ -65,6 +65,12 @@ typedef enum {
     SSL_TEST_CT_VALIDATION_PERMISSIVE,
     SSL_TEST_CT_VALIDATION_STRICT
 } ssl_ct_validation_t;
+
+typedef enum {
+    SSL_TEST_CERT_STATUS_NONE = 0, /* Default */
+    SSL_TEST_CERT_STATUS_GOOD_RESPONSE,
+    SSL_TEST_CERT_STATUS_BAD_RESPONSE
+} ssl_cert_status_t;
 /*
  * Server/client settings that aren't supported by the SSL CONF library,
  * such as callbacks.
@@ -88,6 +94,8 @@ typedef struct {
     char *alpn_protocols;
     /* Whether to set a broken session ticket callback. */
     int broken_session_ticket;
+    /* Should we send a CertStatus message? */
+    ssl_cert_status_t cert_status;
 } SSL_TEST_SERVER_CONF;
 
 typedef struct {
@@ -164,6 +172,7 @@ 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);
+const char *ssl_certstatus_name(ssl_cert_status_t cert_status);
 
 /*
  * Load the test case context from |conf|.
index b54d7ea..0f321c6 100644 (file)
@@ -83,6 +83,12 @@ static int SSL_TEST_SERVER_CONF_equal(SSL_TEST_SERVER_CONF *server,
                 server->broken_session_ticket, server2->broken_session_ticket);
         return 0;
     }
+    if (server->cert_status != server2->cert_status) {
+        fprintf(stderr, "CertStatus mismatch: %s vs %s.\n",
+                ssl_certstatus_name(server->cert_status),
+                ssl_certstatus_name(server2->cert_status));
+        return 0;
+    }
     return 1;
 }