Extends s_client to allow a basic CT policy to be enabled
authorRob Percival <robpercival@google.com>
Wed, 2 Mar 2016 13:34:05 +0000 (13:34 +0000)
committerRich Salz <rsalz@openssl.org>
Fri, 4 Mar 2016 15:50:10 +0000 (10:50 -0500)
Reviewed-by: Ben Laurie <ben@openssl.org>
Reviewed-by: Rich Salz <rsalz@openssl.org>
apps/Makefile.in
apps/apps.c
apps/apps.h
apps/ct_log_list.cnf [new file with mode: 0644]
apps/s_cb.c
apps/s_client.c
test/recipes/80-test_ssl.t
test/ssltest.c

index 956d84b..f9eba16 100644 (file)
@@ -30,6 +30,7 @@ LIBSSL=-L.. -lssl
 
 SCRIPTS=CA.pl tsget
 EXE= openssl$(EXE_EXT)
+CONFS=openssl.cnf ct_log_list.cnf
 
 COMMANDS= \
        asn1pars.o ca.o ciphers.o cms.o crl.o crl2p7.o dgst.o dhparam.o \
@@ -92,10 +93,14 @@ install:
         cp $$i $(DESTDIR)$(OPENSSLDIR)/misc/$$i.new; \
         chmod 755 $(DESTDIR)$(OPENSSLDIR)/misc/$$i.new; \
         mv -f $(DESTDIR)$(OPENSSLDIR)/misc/$$i.new $(DESTDIR)$(OPENSSLDIR)/misc/$$i ); \
-        done
-       @cp openssl.cnf $(DESTDIR)$(OPENSSLDIR)/openssl.cnf.new; \
-       chmod 644 $(DESTDIR)$(OPENSSLDIR)/openssl.cnf.new; \
-       mv -f  $(DESTDIR)$(OPENSSLDIR)/openssl.cnf.new $(DESTDIR)$(OPENSSLDIR)/openssl.cnf
+        done;
+       @set -e; for i in $(CONFS); \
+       do \
+       (echo installing $$i; \
+        cp $$i $(DESTDIR)$(OPENSSLDIR)/$$i.new; \
+        chmod 644 $(DESTDIR)$(OPENSSLDIR)/$$i.new; \
+        mv -f $(DESTDIR)$(OPENSSLDIR)/$$i.new $(DESTDIR)$(OPENSSLDIR)/$$i ); \
+        done;
 
 uninstall:
        @set -e; for i in $(EXE); \
@@ -107,8 +112,12 @@ uninstall:
        do  \
                echo $(RM) $(DESTDIR)$(OPENSSLDIR)/misc/$$i; \
                $(RM) $(DESTDIR)$(OPENSSLDIR)/misc/$$i; \
-       done
-       $(RM) $(DESTDIR)$(OPENSSLDIR)/openssl.cnf
+       done;
+       @set -e; for i in $(CONFS); \
+       do  \
+               echo $(RM) $(DESTDIR)$(OPENSSLDIR)/$$i; \
+               $(RM) $(DESTDIR)$(OPENSSLDIR)/$$i; \
+       done;
 
 generate: openssl-vms.cnf progs.h
 
index 9f60e76..19523d6 100644 (file)
@@ -235,6 +235,19 @@ int ctx_set_verify_locations(SSL_CTX *ctx, const char *CAfile,
     return SSL_CTX_load_verify_locations(ctx, CAfile, CApath);
 }
 
+int ctx_set_ctlog_list_file(SSL_CTX *ctx, const char *path)
+{
+    if (path == NULL) {
+        if (SSL_CTX_set_default_ctlog_list_file(ctx) <= 0) {
+            BIO_puts(bio_err, "Failed to load default Certificate Transparency "
+                     "log list\n");
+        }
+        return 1; /* Do not treat failure to load the default as an error */
+    }
+
+    return SSL_CTX_set_ctlog_list_file(ctx, path);
+}
+
 int dump_cert_text(BIO *out, X509 *x)
 {
     char *p;
index 4540a63..5450def 100644 (file)
@@ -489,6 +489,8 @@ X509_STORE *setup_verify(char *CAfile, char *CApath,
                          int noCAfile, int noCApath);
 int ctx_set_verify_locations(SSL_CTX *ctx, const char *CAfile,
                              const char *CApath, int noCAfile, int noCApath);
+int ctx_set_ctlog_list_file(SSL_CTX *ctx, const char *path);
+
 # ifdef OPENSSL_NO_ENGINE
 #  define setup_engine(engine, debug) NULL
 # else
diff --git a/apps/ct_log_list.cnf b/apps/ct_log_list.cnf
new file mode 100644 (file)
index 0000000..2434874
--- /dev/null
@@ -0,0 +1,34 @@
+enabled_logs=pilot,aviator,rocketeer,digicert,certly,izempe,symantec,venafi
+
+[pilot]
+description = Google Pilot Log
+key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfahLEimAoz2t01p3uMziiLOl/fHTDM0YDOhBRuiBARsV4UvxG2LdNgoIGLrtCzWE0J5APC2em4JlvR8EEEFMoA==
+
+[aviator]
+description = Google Aviator log
+key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q==
+
+[rocketeer]
+description = Google Rocketeer log
+key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg==
+
+[digicert]
+description = DigiCert Log Server
+key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAkbFvhu7gkAW6MHSrBlpE1n4+HCFRkC5OLAjgqhkTH+/uzSfSl8ois8ZxAD2NgaTZe1M9akhYlrYkes4JECs6A==
+
+[certly]
+description = Certly.IO log
+key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2MNvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA==
+
+[izempe]
+description = Izempe log
+key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ2Q5DC3cUBj4IQCiDu0s6j51up+TZAkAEcQRF6tczw90rLWXkJMAW7jr9yc92bIKgV8vDXU4lDeZHvYHduDuvg==
+
+[symantec]
+description = Symantec log
+key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEluqsHEYMG1XcDfy1lCdGV0JwOmkY4r87xNuroPS2bMBTP01CEDPwWJePa75y9CrsHEKqAy8afig1dpkIPSEUhg==
+
+[venafi]
+description = Venafi log
+key = MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAolpIHxdSlTXLo1s6H1OCdpSj/4DyHDc8wLG9wVmLqy1lk9fz4ATVmm+/1iN2Nk8jmctUKK2MFUtlWXZBSpym97M7frGlSaQXUWyA3CqQUEuIJOmlEjKTBEiQAvpfDjCHjlV2Be4qTM6jamkJbiWtgnYPhJL6ONaGTiSPm7Byy57iaz/hbckldSOIoRhYBiMzeNoA0DiRZ9KmfSeXZ1rB8y8X5urSW+iBzf2SaOfzBvDpcoTuAaWx2DPazoOl28fP1hZ+kHUYvxbcMjttjauCFx+JII0dmuZNIwjfeG/GBb9frpSX219k1O4Wi6OEbHEr8at/XQ0y7gTikOxBn/s5wQIDAQAB
+
index 8a25d17..3e9d0f6 100644 (file)
@@ -711,6 +711,7 @@ static STRINT_PAIR tlsext_types[] = {
     {"heartbeat", TLSEXT_TYPE_heartbeat},
     {"session ticket", TLSEXT_TYPE_session_ticket},
     {"renegotiation info", TLSEXT_TYPE_renegotiate},
+    {"signed certificate timestamps", TLSEXT_TYPE_signed_certificate_timestamp},
     {"TLS padding", TLSEXT_TYPE_padding},
 #ifdef TLSEXT_TYPE_next_proto_neg
     {"next protocol", TLSEXT_TYPE_next_proto_neg},
index 85fca1f..cce8e24 100644 (file)
@@ -165,6 +165,9 @@ typedef unsigned int u_int;
 #ifndef OPENSSL_NO_SRP
 # include <openssl/srp.h>
 #endif
+#ifndef OPENSSL_NO_CT
+# include <openssl/ct.h>
+#endif
 #include "s_apps.h"
 #include "timeouts.h"
 
@@ -656,6 +659,9 @@ typedef enum OPTION_choice {
     OPT_X_ENUM,
     OPT_S_ENUM,
     OPT_FALLBACKSCSV, OPT_NOCMDS, OPT_PROXY, OPT_DANE_TLSA_DOMAIN,
+#ifndef OPENSSL_NO_CT
+    OPT_NOCT, OPT_REQUESTCT, OPT_REQUIRECT, OPT_CTLOG_FILE,
+#endif
     OPT_DANE_TLSA_RRDATA
 } OPTION_CHOICE;
 
@@ -809,6 +815,12 @@ OPTIONS s_client_options[] = {
     {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
     {"ssl_client_engine", OPT_SSL_CLIENT_ENGINE, 's',
      "Specify engine to be used for client certificate operations"},
+#endif
+#ifndef OPENSSL_NO_CT
+    {"noct", OPT_NOCT, '-', "Do not request or parse SCTs (default)"},
+    {"requestct", OPT_REQUESTCT, '-', "Request SCTs (enables OCSP stapling)"},
+    {"requirect", OPT_REQUIRECT, '-', "Require at least 1 SCT (enables OCSP stapling)"},
+    {"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"},
 #endif
     {NULL}
 };
@@ -903,6 +915,10 @@ int s_client_main(int argc, char **argv)
     int srp_lateuser = 0;
     SRP_ARG srp_arg = { NULL, NULL, 0, 0, 0, 1024 };
 #endif
+#ifndef OPENSSL_NO_CT
+    char *ctlog_file = NULL;
+    ct_validation_cb ct_validation = NULL;
+#endif
 
     FD_ZERO(&readfds);
     FD_ZERO(&writefds);
@@ -1293,6 +1309,20 @@ int s_client_main(int argc, char **argv)
         case OPT_NOCAFILE:
             noCAfile = 1;
             break;
+#ifndef OPENSSL_NO_CT
+        case OPT_NOCT:
+            ct_validation = NULL;
+            break;
+        case OPT_REQUESTCT:
+            ct_validation = CT_verify_no_bad_scts;
+            break;
+        case OPT_REQUIRECT:
+            ct_validation = CT_verify_at_least_one_good_sct;
+            break;
+        case OPT_CTLOG_FILE:
+            ctlog_file = opt_arg();
+            break;
+#endif
         case OPT_CHAINCAFILE:
             chCAfile = opt_arg();
             break;
@@ -1588,6 +1618,18 @@ int s_client_main(int argc, char **argv)
     if (state)
         SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback);
 
+#ifndef OPENSSL_NO_CT
+    if (!SSL_CTX_set_ct_validation_callback(ctx, ct_validation, NULL)) {
+        ERR_print_errors(bio_err);
+        goto end;
+    }
+
+    if (ctx_set_ctlog_list_file(ctx, ctlog_file) <= 0) {
+        ERR_print_errors(bio_err);
+        goto end;
+    }
+#endif
+
     SSL_CTX_set_verify(ctx, verify, verify_callback);
 
     if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) {
@@ -2459,6 +2501,9 @@ static void print_stuff(BIO *bio, SSL *s, int full)
     const COMP_METHOD *comp, *expansion;
 #endif
     unsigned char *exportedkeymat;
+#ifndef OPENSSL_NO_CT
+    const STACK_OF(SCT) *scts;
+#endif
 
     if (full) {
         int got_a_chain = 0;
@@ -2511,6 +2556,18 @@ static void print_stuff(BIO *bio, SSL *s, int full)
         ssl_print_sigalgs(bio, s);
         ssl_print_tmp_key(bio, s);
 
+#ifndef OPENSSL_NO_CT
+        scts = SSL_get0_peer_scts(s);
+        BIO_printf(bio, "---\nSCTs present (%i)\n---\n",
+                   scts ? sk_SCT_num(scts) : 0);
+        SCT_LIST_print(scts, bio, 0, "\n---\n");
+        BIO_printf(bio, "\n");
+        if (SSL_get_ct_validation_callback(s) == NULL) {
+          BIO_printf(bio, "---\nWarning: CT validation is disabled, so not all "
+                     "SCTs may be displayed. Re-run with \"-requestct\".\n");
+        }
+#endif
+
         BIO_printf(bio,
                    "---\nSSL handshake has read %"PRIu64" bytes and written %"PRIu64" bytes\n",
                    BIO_number_read(SSL_get_rbio(s)),
index bcab4b5..37237dc 100644 (file)
@@ -13,10 +13,10 @@ setup("test_ssl");
 
 my ($no_rsa, $no_dsa, $no_dh, $no_ec, $no_srp, $no_psk,
     $no_ssl3, $no_tls1, $no_tls1_1, $no_tls1_2,
-    $no_dtls, $no_dtls1, $no_dtls1_2) =
+    $no_dtls, $no_dtls1, $no_dtls1_2, $no_ct) =
     anydisabled qw/rsa dsa dh ec srp psk
                    ssl3 tls1 tls1_1 tls1_2
-                   dtls dtls1 dtls1_2/;
+                   dtls dtls1 dtls1_2 ct/;
 my $no_anytls = alldisabled(available_protocols("tls"));
 my $no_anydtls = alldisabled(available_protocols("dtls"));
 
@@ -64,7 +64,7 @@ my $P2intermediate="tmp_intP2.ss";
 plan tests =>
     1                          # For testss
     + 1                                # For ssltest -test_cipherlist
-    + 10                       # For the first testssl
+    + 11                       # For the first testssl
     + 16                       # For the first testsslproxy
     + 16                       # For the second testsslproxy
     ;
@@ -325,7 +325,7 @@ sub testssl {
     }
 
 
-    # plan tests => 10;
+    # plan tests => 11;
 
     subtest 'standard SSL tests' => sub {
        ######################################################################
@@ -762,6 +762,27 @@ sub testssl {
             ok($ok);
         }}}}}
     };
+
+    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");
+         ok(run(test([@ssltest, "-bio_pair", "-tls1", "-noct"])));
+         ok(run(test([@ssltest, "-bio_pair", "-tls1", "-requestct"])));
+         # No SCTs provided, so this should fail.
+         ok(run(test([@ssltest, "-bio_pair", "-tls1", "-requirect",
+                      "-should_negotiate", "fail-client"])));
+       }
+    };
+
 }
 
 sub testsslproxy {
index f65358a..da9391a 100644 (file)
 # include <openssl/srp.h>
 #endif
 #include <openssl/bn.h>
+#ifndef OPENSSL_NO_CT
+# include <openssl/ct.h>
+#endif
 
 #include "../ssl/ssl_locl.h"
 
@@ -493,8 +496,6 @@ static int verify_alpn(SSL *client, SSL *server)
     return -1;
 }
 
-#define SCT_EXT_TYPE 18
-
 /*
  * WARNING : below extension types are *NOT* IETF assigned, and could
  * conflict if these types are reassigned and handled specially by OpenSSL
@@ -529,7 +530,7 @@ static int serverinfo_cli_parse_cb(SSL *s, unsigned int ext_type,
                                    const unsigned char *in, size_t inlen,
                                    int *al, void *arg)
 {
-    if (ext_type == SCT_EXT_TYPE)
+    if (ext_type == TLSEXT_TYPE_signed_certificate_timestamp)
         serverinfo_sct_seen++;
     else if (ext_type == TACK_EXT_TYPE)
         serverinfo_tack_seen++;
@@ -838,6 +839,11 @@ static void sv_usage(void)
     fprintf(stderr, " -client_min_proto <string> - Minimum version the client should support\n");
     fprintf(stderr, " -client_max_proto <string> - Maximum version the client should support\n");
     fprintf(stderr, " -should_negotiate <string> - The version that should be negotiated, fail-client or fail-server\n");
+#ifndef OPENSSL_NO_CT
+    fprintf(stderr, " -noct         - no certificate transparency\n");
+    fprintf(stderr, " -requestct    - request certificate transparency\n");
+    fprintf(stderr, " -requirect    - require certificate transparency\n");
+#endif
 }
 
 static void print_key_details(BIO *out, EVP_PKEY *key)
@@ -1057,6 +1063,14 @@ int main(int argc, char *argv[])
 #endif
     int no_protocol;
 
+#ifndef OPENSSL_NO_CT
+    /*
+     * Disable CT validation by default, because it will interfere with
+     * anything using custom extension handlers to deal with SCT extensions.
+     */
+    ct_validation_cb ct_validation = NULL;
+#endif
+
     SSL_CONF_CTX *s_cctx = NULL, *c_cctx = NULL;
     STACK_OF(OPENSSL_STRING) *conf_args = NULL;
     char *arg = NULL, *argn = NULL;
@@ -1229,6 +1243,17 @@ int main(int argc, char *argv[])
         } else if (strcmp(*argv, "-time") == 0) {
             print_time = 1;
         }
+#ifndef OPENSSL_NO_CT
+        else if (strcmp(*argv, "-noct") == 0) {
+            ct_validation = NULL;
+        }
+        else if (strcmp(*argv, "-requestct") == 0) {
+            ct_validation = CT_verify_no_bad_scts;
+        }
+        else if (strcmp(*argv, "-requirect") == 0) {
+            ct_validation = CT_verify_at_least_one_good_sct;
+        }
+#endif
 #ifndef OPENSSL_NO_COMP
         else if (strcmp(*argv, "-zlib") == 0) {
             comp = COMP_ZLIB;
@@ -1512,6 +1537,13 @@ int main(int argc, char *argv[])
         }
     }
 
+#ifndef OPENSSL_NO_CT
+    if (!SSL_CTX_set_ct_validation_callback(c_ctx, ct_validation, NULL)) {
+        ERR_print_errors(bio_err);
+        goto end;
+    }
+#endif
+
     /* Process SSL_CONF arguments */
     SSL_CONF_CTX_set_ssl_ctx(c_cctx, c_ctx);
     SSL_CONF_CTX_set_ssl_ctx(s_cctx, s_ctx);
@@ -1586,15 +1618,18 @@ int main(int argc, char *argv[])
 
     if ((!SSL_CTX_load_verify_locations(s_ctx, CAfile, CApath)) ||
         (!SSL_CTX_set_default_verify_paths(s_ctx)) ||
-        (!SSL_CTX_set_default_ctlog_list_file(s_ctx)) ||
         (!SSL_CTX_load_verify_locations(c_ctx, CAfile, CApath)) ||
-        (!SSL_CTX_set_default_verify_paths(c_ctx)) ||
-        (!SSL_CTX_set_default_ctlog_list_file(c_ctx))) {
+        (!SSL_CTX_set_default_verify_paths(c_ctx))) {
         /* fprintf(stderr,"SSL_load_verify_locations\n"); */
         ERR_print_errors(bio_err);
         /* goto end; */
     }
 
+    if (!SSL_CTX_set_default_ctlog_list_file(s_ctx) ||
+        !SSL_CTX_set_default_ctlog_list_file(c_ctx)) {
+        ERR_print_errors(bio_err);
+    }
+
     if (client_auth) {
         printf("client authentication\n");
         SSL_CTX_set_verify(s_ctx,
@@ -1684,9 +1719,10 @@ int main(int argc, char *argv[])
 #endif
 
     if (serverinfo_sct) {
-        if (!SSL_CTX_add_client_custom_ext(c_ctx, SCT_EXT_TYPE,
-                                      NULL, NULL, NULL,
-                                      serverinfo_cli_parse_cb, NULL)) {
+        if (!SSL_CTX_add_client_custom_ext(c_ctx,
+                TLSEXT_TYPE_signed_certificate_timestamp,
+                NULL, NULL, NULL,
+                serverinfo_cli_parse_cb, NULL)) {
             BIO_printf(bio_err, "Error adding SCT extension\n");
             goto end;
         }