From dd696a55a2554cc8c89dd64f7e1171ce211dfc5c Mon Sep 17 00:00:00 2001 From: Rob Percival Date: Wed, 2 Mar 2016 13:34:05 +0000 Subject: [PATCH] Extends s_client to allow a basic CT policy to be enabled Reviewed-by: Ben Laurie Reviewed-by: Rich Salz --- apps/Makefile.in | 21 ++++++++++---- apps/apps.c | 13 +++++++++ apps/apps.h | 2 ++ apps/ct_log_list.cnf | 34 +++++++++++++++++++++++ apps/s_cb.c | 1 + apps/s_client.c | 57 ++++++++++++++++++++++++++++++++++++++ test/recipes/80-test_ssl.t | 29 ++++++++++++++++--- test/ssltest.c | 54 ++++++++++++++++++++++++++++++------ 8 files changed, 192 insertions(+), 19 deletions(-) create mode 100644 apps/ct_log_list.cnf diff --git a/apps/Makefile.in b/apps/Makefile.in index 956d84b4d7..f9eba16d53 100644 --- a/apps/Makefile.in +++ b/apps/Makefile.in @@ -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 diff --git a/apps/apps.c b/apps/apps.c index 9f60e76dd8..19523d68fc 100644 --- a/apps/apps.c +++ b/apps/apps.c @@ -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; diff --git a/apps/apps.h b/apps/apps.h index 4540a63cfe..5450def13d 100644 --- a/apps/apps.h +++ b/apps/apps.h @@ -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 index 0000000000..243487453c --- /dev/null +++ b/apps/ct_log_list.cnf @@ -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 + diff --git a/apps/s_cb.c b/apps/s_cb.c index 8a25d173b0..3e9d0f63a9 100644 --- a/apps/s_cb.c +++ b/apps/s_cb.c @@ -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}, diff --git a/apps/s_client.c b/apps/s_client.c index 85fca1fc4e..cce8e244b5 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -165,6 +165,9 @@ typedef unsigned int u_int; #ifndef OPENSSL_NO_SRP # include #endif +#ifndef OPENSSL_NO_CT +# include +#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)), diff --git a/test/recipes/80-test_ssl.t b/test/recipes/80-test_ssl.t index bcab4b5f78..37237dc087 100644 --- a/test/recipes/80-test_ssl.t +++ b/test/recipes/80-test_ssl.t @@ -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 { diff --git a/test/ssltest.c b/test/ssltest.c index f65358ac40..da9391a0c8 100644 --- a/test/ssltest.c +++ b/test/ssltest.c @@ -187,6 +187,9 @@ # include #endif #include +#ifndef OPENSSL_NO_CT +# include +#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 - Minimum version the client should support\n"); fprintf(stderr, " -client_max_proto - Maximum version the client should support\n"); fprintf(stderr, " -should_negotiate - 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; } -- 2.25.1