[openssl-commits] [openssl] master update

Rich Salz rsalz at openssl.org
Fri Mar 4 15:52:30 UTC 2016


The branch master has been updated
       via  69d86ef1908b9f153fb32ed39eaee52011a96797 (commit)
       via  a203c096b62b9f91e007f29c76476afeaadf6c93 (commit)
       via  eb64a6c6762652a5a293819f6934046e8a148c5e (commit)
       via  238d692c6a9b07ce04d896481783478086fedc6d (commit)
       via  2508c047eb116df36c3189f767313cb65fd5283c (commit)
       via  0c6ea56568f8dbc5d83cd737d81cd7362295acf0 (commit)
       via  a930afb6986fb8e22a59b2bfc5f1a19a8dc3388d (commit)
       via  dd696a55a2554cc8c89dd64f7e1171ce211dfc5c (commit)
       via  98d8ddd25474b954d52f01086fcc2536965d015b (commit)
       via  ed29e82adeea9d2ee89aeadf5646d4d1350a6855 (commit)
      from  ddb4c0477af623fcad3e6709640729e82693a4c9 (commit)


- Log -----------------------------------------------------------------
commit 69d86ef1908b9f153fb32ed39eaee52011a96797
Author: Rob Percival <robpercival at google.com>
Date:   Fri Mar 4 15:30:33 2016 +0000

    Make formatting consistent in apps/Makefile.in
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit a203c096b62b9f91e007f29c76476afeaadf6c93
Author: Rob Percival <robpercival at google.com>
Date:   Fri Mar 4 13:18:24 2016 +0000

    Remove redundant semi-colons from apps/Makefile.in
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit eb64a6c6762652a5a293819f6934046e8a148c5e
Author: Rob Percival <robpercival at google.com>
Date:   Thu Mar 3 14:07:28 2016 +0000

    Documentation for new CT s_client flags
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 238d692c6a9b07ce04d896481783478086fedc6d
Author: Rob Percival <robpercival at google.com>
Date:   Thu Mar 3 18:39:30 2016 +0000

    Documentation for new SSL functions
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 2508c047eb116df36c3189f767313cb65fd5283c
Author: Rob Percival <robpercival at google.com>
Date:   Thu Mar 3 16:08:55 2016 +0000

    Handle empty log name in "enable_logs" line of the CT log file
    
    e.g. "enabled_logs = foo,,bar"
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 0c6ea56568f8dbc5d83cd737d81cd7362295acf0
Author: Rob Percival <robpercival at google.com>
Date:   Thu Mar 3 16:08:01 2016 +0000

    Handle missing "enabled_logs" line in CT log file
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit a930afb6986fb8e22a59b2bfc5f1a19a8dc3388d
Author: Rob Percival <robpercival at google.com>
Date:   Thu Mar 3 16:06:59 2016 +0000

    If a CT log entry in CTLOG_FILE is invalid, skip it and continue loading
    
    Previously, the remaining CT log entries would not be loaded.
    Also, CTLOG_STORE_load_file would return 1 even if a log entry was
    invalid, resulting in no errors being shown.
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit dd696a55a2554cc8c89dd64f7e1171ce211dfc5c
Author: Rob Percival <robpercival at google.com>
Date:   Wed Mar 2 13:34:05 2016 +0000

    Extends s_client to allow a basic CT policy to be enabled
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 98d8ddd25474b954d52f01086fcc2536965d015b
Author: Rob Percival <robpercival at google.com>
Date:   Fri Mar 4 13:18:54 2016 +0000

    Change default CT log list filename to "ct_log_list.cnf"
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit ed29e82adeea9d2ee89aeadf5646d4d1350a6855
Author: Rob Percival <robpercival at google.com>
Date:   Thu Mar 3 16:19:23 2016 +0000

    Adds CT validation to SSL connections
    
    Disabled by default, but can be enabled by setting the
    ct_validation_callback on a SSL or SSL_CTX.
    
    Reviewed-by: Ben Laurie <ben at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

-----------------------------------------------------------------------

Summary of changes:
 CHANGES                                        |   5 +
 NEWS                                           |   1 +
 apps/Makefile.in                               |  47 ++--
 apps/apps.c                                    |  13 ++
 apps/apps.h                                    |   2 +
 test/ct/log_list.conf => apps/ct_log_list.cnf  |   6 +-
 apps/s_cb.c                                    |   1 +
 apps/s_client.c                                |  57 +++++
 crypto/ct/ct_log.c                             |  38 +++-
 crypto/include/internal/cryptlib.h             |   4 +-
 doc/apps/s_client.pod                          |  19 ++
 doc/ssl/SSL_CTX_has_client_custom_ext.pod      |  28 +++
 doc/ssl/SSL_CTX_set_ct_validation_callback.pod |  59 +++++
 doc/ssl/SSL_CTX_set_ctlog_list_file.pod        |  57 +++++
 doc/ssl/SSL_get0_peer_scts.pod                 |  36 +++
 include/openssl/ct.h                           |   4 +-
 include/openssl/ssl.h                          |  53 +++++
 include/openssl/tls1.h                         |   6 +
 ssl/ssl_err.c                                  |  15 ++
 ssl/ssl_lib.c                                  | 295 ++++++++++++++++++++++++-
 ssl/ssl_locl.h                                 |  38 +++-
 ssl/statem/statem_clnt.c                       |   9 +
 ssl/t1_ext.c                                   |  41 +++-
 ssl/t1_lib.c                                   |  33 +++
 ssl/t1_trce.c                                  |   1 +
 test/recipes/80-test_ssl.t                     |  29 ++-
 test/ssltest.c                                 |  50 ++++-
 util/ssleay.num                                |   8 +
 28 files changed, 904 insertions(+), 51 deletions(-)
 copy test/ct/log_list.conf => apps/ct_log_list.cnf (82%)
 create mode 100644 doc/ssl/SSL_CTX_has_client_custom_ext.pod
 create mode 100644 doc/ssl/SSL_CTX_set_ct_validation_callback.pod
 create mode 100644 doc/ssl/SSL_CTX_set_ctlog_list_file.pod
 create mode 100644 doc/ssl/SSL_get0_peer_scts.pod

diff --git a/CHANGES b/CHANGES
index 8869e0b..8c4d9a5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -873,6 +873,11 @@
      whose return value is often ignored. 
      [Steve Henson]
 
+  *) New -noct, -requestct, -requirect and -ctlogfile options for s_client.
+     These allow SCTs (signed certificate timestamps) to be requested and
+     validated when establishing a connection.
+     [Rob Percival <robpercival at google.com>]
+
  Changes between 1.0.2f and 1.0.2g [1 Mar 2016]
 
   * Disable weak ciphers in SSLv3 and up in default builds of OpenSSL.
diff --git a/NEWS b/NEWS
index cfcca0e..240bd0a 100644
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,7 @@
       o Support for X25519
       o Extended SSL_CONF support using configuration files
       o KDF algorithm support. Implement TLS PRF as a KDF.
+      o Support for Certificate Transparency
 
   Major changes between OpenSSL 1.0.2f and OpenSSL 1.0.2g [1 Mar 2016]
 
diff --git a/apps/Makefile.in b/apps/Makefile.in
index 956d84b..6e8bdf5 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 \
@@ -80,35 +81,43 @@ files:
 install:
 	@[ -n "$(INSTALLTOP)" ] # should be set by top Makefile...
 	@set -e; for i in $(EXE); \
-	do  \
-	(echo installing $$i; \
-	 cp $$i $(DESTDIR)$(INSTALLTOP)/bin/$$i.new; \
-	 chmod 755 $(DESTDIR)$(INSTALLTOP)/bin/$$i.new; \
-	 mv -f $(DESTDIR)$(INSTALLTOP)/bin/$$i.new $(DESTDIR)$(INSTALLTOP)/bin/$$i ); \
-	 done;
+	do \
+		echo installing $$i; \
+		cp $$i $(DESTDIR)$(INSTALLTOP)/bin/$$i.new; \
+		chmod 755 $(DESTDIR)$(INSTALLTOP)/bin/$$i.new; \
+		mv -f $(DESTDIR)$(INSTALLTOP)/bin/$$i.new $(DESTDIR)$(INSTALLTOP)/bin/$$i; \
+	done
 	@set -e; for i in $(SCRIPTS); \
-	do  \
-	(echo installing $$i; \
-	 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
+	do \
+		echo installing $$i; \
+		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
+	@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); \
-	do  \
+	do \
 		echo $(RM) $(DESTDIR)$(INSTALLTOP)/bin/$$i; \
 		$(RM) $(DESTDIR)$(INSTALLTOP)/bin/$$i; \
-	done;
+	done
 	@set -e; for i in $(SCRIPTS); \
-	do  \
+	do \
 		echo $(RM) $(DESTDIR)$(OPENSSLDIR)/misc/$$i; \
 		$(RM) $(DESTDIR)$(OPENSSLDIR)/misc/$$i; \
 	done
-	$(RM) $(DESTDIR)$(OPENSSLDIR)/openssl.cnf
+	@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 9f60e76..19523d6 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 4540a63..5450def 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/test/ct/log_list.conf b/apps/ct_log_list.cnf
similarity index 82%
copy from test/ct/log_list.conf
copy to apps/ct_log_list.cnf
index 4b68e53..2434874 100644
--- a/test/ct/log_list.conf
+++ b/apps/ct_log_list.cnf
@@ -1,8 +1,4 @@
-enabled_logs=test,pilot,aviator,rocketeer,digicert,certly,izempe,symantec,venafi
-
-[test]
-description = https://github.com/google/certificate-transparency/tree/99218b6445906a81f219d84e9c6d2683e13e4e58/test/testdata
-key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmXg8sUUzwBYaWrRb+V0IopzQ6o3UyEJ04r5ZrRXGdpYM8K+hB0pXrGRLI0eeWz+3skXrS0IO83AhA3GpRL6s6w==
+enabled_logs=pilot,aviator,rocketeer,digicert,certly,izempe,symantec,venafi
 
 [pilot]
 description = Google Pilot Log
diff --git a/apps/s_cb.c b/apps/s_cb.c
index 8a25d17..3e9d0f6 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 85fca1f..cce8e24 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 <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;
 
@@ -810,6 +816,12 @@ OPTIONS s_client_options[] = {
     {"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/crypto/ct/ct_log.c b/crypto/ct/ct_log.c
index 14f3bcc..03cb51f 100644
--- a/crypto/ct/ct_log.c
+++ b/crypto/ct/ct_log.c
@@ -85,6 +85,7 @@ struct ctlog_store_st {
 typedef struct ctlog_store_load_ctx_st {
     CTLOG_STORE *log_store;
     CONF *conf;
+    size_t invalid_log_entries;
 } CTLOG_STORE_LOAD_CTX;
 
 /*
@@ -201,17 +202,31 @@ int CTLOG_STORE_load_default_file(CTLOG_STORE *store)
     return CTLOG_STORE_load_file(store, fpath);
 }
 
-static int ctlog_store_load_log(const char *log_name, int log_name_len, void *arg)
+/*
+ * Called by CONF_parse_list, which stops if this returns <= 0, so don't unless
+ * something very bad happens. Otherwise, one bad log entry would stop loading
+ * of any of the following log entries.
+ */
+static int ctlog_store_load_log(const char *log_name, int log_name_len,
+                                void *arg)
 {
     CTLOG_STORE_LOAD_CTX *load_ctx = arg;
     CTLOG *ct_log;
     /* log_name may not be null-terminated, so fix that before using it */
-    char *tmp = OPENSSL_strndup(log_name, log_name_len);
+    char *tmp;
+
+    /* log_name will be NULL for empty list entries */
+    if (log_name == NULL)
+        return 1;
 
+    tmp = OPENSSL_strndup(log_name, log_name_len);
     ct_log = ctlog_new_from_conf(load_ctx->conf, tmp);
     OPENSSL_free(tmp);
-    if (ct_log == NULL)
-        return 0;
+    if (ct_log == NULL) {
+        /* If we can't load this log, record that fact and skip it */
+        ++load_ctx->invalid_log_entries;
+        return 1;
+    }
 
     sk_CTLOG_push(load_ctx->log_store->logs, ct_log);
     return 1;
@@ -219,7 +234,7 @@ static int ctlog_store_load_log(const char *log_name, int log_name_len, void *ar
 
 int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file)
 {
-    int ret = -1;
+    int ret = 0;
     char *enabled_logs;
     CTLOG_STORE_LOAD_CTX* load_ctx = ctlog_store_load_ctx_new();
 
@@ -235,7 +250,18 @@ int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file)
     }
 
     enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs");
-    CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx);
+    if (enabled_logs == NULL) {
+        ret = 0;
+        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
+        goto end;
+    }
+
+    ret = CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx);
+    if (ret == 1 && load_ctx->invalid_log_entries > 0) {
+        ret = 0;
+        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
+        goto end;
+    }
 
 end:
     NCONF_free(load_ctx->conf);
diff --git a/crypto/include/internal/cryptlib.h b/crypto/include/internal/cryptlib.h
index 22caf93..a97e20b 100644
--- a/crypto/include/internal/cryptlib.h
+++ b/crypto/include/internal/cryptlib.h
@@ -94,13 +94,13 @@ DEFINE_LHASH_OF(MEM);
 #  define X509_CERT_DIR           OPENSSLDIR "/certs"
 #  define X509_CERT_FILE          OPENSSLDIR "/cert.pem"
 #  define X509_PRIVATE_DIR        OPENSSLDIR "/private"
-#  define CTLOG_FILE              OPENSSLDIR "/log_list.conf"
+#  define CTLOG_FILE              OPENSSLDIR "/ct_log_list.cnf"
 # else
 #  define X509_CERT_AREA          "SSLROOT:[000000]"
 #  define X509_CERT_DIR           "SSLCERTS:"
 #  define X509_CERT_FILE          "SSLCERTS:cert.pem"
 #  define X509_PRIVATE_DIR        "SSLPRIVATE:"
-#  define CTLOG_FILE              "SSLCERTS:log_list.conf"
+#  define CTLOG_FILE              "SSLROOT:ct_log_list.cnf"
 # endif
 
 # define X509_CERT_DIR_EVP        "SSL_CERT_DIR"
diff --git a/doc/apps/s_client.pod b/doc/apps/s_client.pod
index d794b34..607ece5 100644
--- a/doc/apps/s_client.pod
+++ b/doc/apps/s_client.pod
@@ -91,6 +91,8 @@ B<openssl> B<s_client>
 [B<-serverinfo types>]
 [B<-status>]
 [B<-nextprotoneg protocols>]
+[B<-noct|requestct|requirect>]
+[B<-ctlogfile>]
 
 =head1 DESCRIPTION
 
@@ -435,6 +437,23 @@ Empty list of protocols is treated specially and will cause the client to
 advertise support for the TLS extension but disconnect just after
 receiving ServerHello with a list of server supported protocols.
 
+=item B<-noct|requestct|requirect>
+
+Use one of these three options to control whether Certificate Transparency (CT)
+is disabled (-noct), enabled but not enforced (-requestct), or enabled and
+enforced (-requirect). If CT is enabled, signed certificate timestamps (SCTs)
+will be requested from the server and invalid SCTs will cause the connection to
+be aborted. If CT is enforced, at least one valid SCT from a recognised CT log
+(see B<-ctlogfile>) will be required or the connection will be aborted.
+
+Enabling CT also enables OCSP stapling, as this is one possible delivery method
+for SCTs.
+
+=item B<-ctlogfile>
+
+A file containing a list of known Certificate Transparency logs. See
+L<SSL_CTX_set_ctlog_list_file(3)> for the expected file format.
+
 =back
 
 =head1 CONNECTED COMMANDS
diff --git a/doc/ssl/SSL_CTX_has_client_custom_ext.pod b/doc/ssl/SSL_CTX_has_client_custom_ext.pod
new file mode 100644
index 0000000..3a1079d
--- /dev/null
+++ b/doc/ssl/SSL_CTX_has_client_custom_ext.pod
@@ -0,0 +1,28 @@
+=pod
+
+=head1 NAME
+
+SSL_CTX_has_client_custom_ext - check whether a handler exists for a particular
+client extension type
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ int SSL_CTX_has_client_custom_ext(const SSL_CTX *ctx, unsigned int ext_type);
+
+=head1 DESCRIPTION
+
+SSL_CTX_has_client_custom_ext() checks whether a handler has been set for a
+client extension of type B<ext_type> using SSL_CTX_add_client_custom_ext().
+
+=head1 RETURN VALUES
+
+Returns 1 if a handler has been set, 0 otherwise.
+
+=head1 SEE ALSO
+
+L<ssl(3)>,
+L<SSL_CTX_add_client_custom_ext(3)>
+
+=cut
diff --git a/doc/ssl/SSL_CTX_set_ct_validation_callback.pod b/doc/ssl/SSL_CTX_set_ct_validation_callback.pod
new file mode 100644
index 0000000..59ab293
--- /dev/null
+++ b/doc/ssl/SSL_CTX_set_ct_validation_callback.pod
@@ -0,0 +1,59 @@
+=pod
+
+=head1 NAME
+
+SSL_set_ct_validation_callback, SSL_CTX_set_ct_validation_callback,
+SSL_get_ct_validation_callback, SSL_CTX_get_ct_validation_callback -
+control Certificate Transparency policy
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ int SSL_set_ct_validation_callback(SSL *s, ct_validation_cb callback, void *arg);
+ int SSL_CTX_set_ct_validation_callback(SSL_CTX *ctx, ct_validation_cb callback, void *arg);
+ ct_validation_cb SSL_get_ct_validation_callback(const SSL *s);
+ ct_validation_cb SSL_CTX_get_ct_validation_callback(const SSL_CTX *ctx);
+
+=head1 DESCRIPTION
+
+SSL_set_ct_validation_callback() and SSL_CTX_set_ct_validation_callback() set
+the function that is called when Certificate Transparency validation needs to
+occur. It is the responsibility of this function to examine the signed
+certificate timestamps (SCTs) that are passed to it and determine whether they
+are sufficient to allow the connection to continue. If they are, the function
+must return 1, otherwise it must return 0.
+
+An arbitrary piece of user data, B<arg>, can be passed in when setting the
+callback. This will be passed to the callback whenever it is invoked. Ownership
+of this userdata remains with the caller.
+
+If no callback is set, SCTs will not be requested and Certificate Transparency
+validation will not occur.
+
+=head1 NOTES
+
+If a callback is set, OCSP stapling will be enabled. This is because one
+possible source of SCTs is the OCSP response from a server.
+
+=head1 RESTRICTIONS
+
+Certificate Transparency validation cannot be enabled and so a callback cannot
+be set if a custom client extension handler has been registered to handle SCT
+extensions (B<TLSEXT_TYPE_signed_certificate_timestamp>).
+
+=head1 RETURN VALUES
+
+SSL_CTX_set_ct_validation_callback() and SSL_set_ct_validation_callback()
+return 1 if the B<callback> is successfully set. They return 0 if an error
+occurs, e.g. a custom client extension handler has been setup to handle SCTs.
+
+SSL_CTX_get_ct_validation_callback() and SSL_get_ct_validation_callback()
+return the current callback, or NULL if no callback is set.
+
+=head1 SEE ALSO
+
+L<ssl(3)>,
+L<ct_validation_cb(3)>
+
+=cut
diff --git a/doc/ssl/SSL_CTX_set_ctlog_list_file.pod b/doc/ssl/SSL_CTX_set_ctlog_list_file.pod
new file mode 100644
index 0000000..ddad842
--- /dev/null
+++ b/doc/ssl/SSL_CTX_set_ctlog_list_file.pod
@@ -0,0 +1,57 @@
+=pod
+
+=head1 NAME
+
+SSL_CTX_set_default_ctlog_list_file, SSL_CTX_set_ctlog_list_file -
+load a Certificate Transparency log list from a file
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ int SSL_CTX_set_default_ctlog_list_file(SSL_CTX *ctx);
+ int SSL_CTX_set_ctlog_list_file(SSL_CTX *ctx, const char *path);
+
+=head1 DESCRIPTION
+
+SSL_CTX_set_default_ctlog_list_file() loads a list of Certificate Transparency
+(CT) logs from the default file location, "ct_log_list.cnf", found in the
+directory where OpenSSL is installed.
+
+SSL_CTX_set_ctlog_list_file() loads a list of CT logs from a given path.
+
+The expected format of the log list file is:
+
+ enabled_logs=foo,bar
+
+ [foo]
+ description = Log 1
+ key = <base64-encoded public key here>
+
+ [bar]
+ description = Log 2
+ key = <base64-encoded public key here>
+
+=head1 NOTES
+
+These functions will not clear the existing CT log list - it will be appended
+to.
+
+SSL_CTX_set_default_ctlog_list_file() will not report errors if it fails for
+any reason. Use SSL_CTX_set_ctlog_list_file() if you want errors to be reported.
+
+If an error occurs whilst parsing a particular log entry in the file, that log
+entry will be skipped.
+
+=head1 RETURN VALUES
+
+SSL_CTX_set_default_ctlog_list_file() and SSL_CTX_set_ctlog_list_file()
+return 1 if the log list is successfully loaded, and 0 if an error occurs. In
+the case of an error, the log list may have been partially loaded.
+
+=head1 SEE ALSO
+
+L<ssl(3)>,
+L<ct_validation_cb(3)>
+
+=cut
diff --git a/doc/ssl/SSL_get0_peer_scts.pod b/doc/ssl/SSL_get0_peer_scts.pod
new file mode 100644
index 0000000..a2a1a29
--- /dev/null
+++ b/doc/ssl/SSL_get0_peer_scts.pod
@@ -0,0 +1,36 @@
+=pod
+
+=head1 NAME
+
+SSL_get0_peer_scts - get SCTs received
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ const STACK_OF(SCT) *SSL_get0_peer_scts(SSL *s);
+
+=head1 DESCRIPTION
+
+SSL_get0_peer_scts() returns the signed certificate timestamps (SCTs) that have
+been received. If this is the first time that this function has been called for
+a given B<SSL> instance, it will examine the TLS extensions, OCSP response and
+the peer's certificate for SCTs. Future calls will return the same SCTs.
+
+=head1 RESTRICTIONS
+
+If no Certificate Transparency validation callback has been set (using
+B<SSL_CTX_set_ct_validation_callback> or B<SSL_set_ct_validation_callback>),
+this function is not guarantee to return all of the SCTs that the peer is
+capable of sending.
+
+=head1 RETURN VALUES
+
+SSL_get0_peer_scts() returns a list of SCTs found, or NULL if an error occurs.
+
+=head1 SEE ALSO
+
+L<ssl(3)>,
+L<SSL_CTX_set_ct_validation_callback(3)>
+
+=cut
diff --git a/include/openssl/ct.h b/include/openssl/ct.h
index 7ea7ff2..6d2182f 100644
--- a/include/openssl/ct.h
+++ b/include/openssl/ct.h
@@ -542,7 +542,7 @@ CTLOG *CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store,
 
 /*
  * Loads a CT log list into a |store| from a |file|.
- * Returns 1 if loading is successful, or a non-positive integer otherwise.
+ * Returns 1 if loading is successful, or 0 otherwise.
  */
 int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file);
 
@@ -550,7 +550,7 @@ int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file);
  * Loads the default CT log list into a |store|.
  * See internal/cryptlib.h for the environment variable and file path that are
  * consulted to find the default file.
- * Returns 1 if loading is successful, or a non-positive integer otherwise.
+ * Returns 1 if loading is successful, or 0 otherwise.
  */
 int CTLOG_STORE_load_default_file(CTLOG_STORE *store);
 
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 6e22396..e827214 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -159,6 +159,9 @@
 
 # include <openssl/safestack.h>
 # include <openssl/symhacks.h>
+# ifndef OPENSSL_NO_CT
+#  include <openssl/ct.h>
+# endif
 
 #ifdef  __cplusplus
 extern "C" {
@@ -862,6 +865,9 @@ const char *SSL_get_psk_identity(const SSL *s);
 
 /* Register callbacks to handle custom TLS Extensions for client or server. */
 
+__owur int SSL_CTX_has_client_custom_ext(const SSL_CTX *ctx,
+                                         unsigned int ext_type);
+
 __owur int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
                                   custom_ext_add_cb add_cb,
                                   custom_ext_free_cb free_cb,
@@ -1865,6 +1871,43 @@ __owur const char *SSL_CIPHER_standard_name(const SSL_CIPHER *c);
 
 int DTLSv1_listen(SSL *s, BIO_ADDR *client);
 
+# ifndef OPENSSL_NO_CT
+
+/*
+ * Sets a |callback| that is invoked upon receipt of ServerHelloDone to validate
+ * the received SCTs.
+ * If the callback returns a non-positive result, the connection is terminated.
+ * Call this function before beginning a handshake.
+ * If a NULL |callback| is provided, SCT validation is disabled.
+ * |arg| is arbitrary userdata that will be passed to the callback whenever it
+ * is invoked. Ownership of |arg| remains with the caller.
+ *
+ * NOTE: A side-effect of setting a CT callback is that an OCSP stapled response
+ *       will be requested.
+ */
+__owur int SSL_set_ct_validation_callback(SSL *s,
+                                          ct_validation_cb callback,
+                                          void *arg);
+__owur int SSL_CTX_set_ct_validation_callback(SSL_CTX *ctx,
+                                              ct_validation_cb callback,
+                                              void *arg);
+/*
+ * Gets the callback being used to validate SCTs.
+ * This will return NULL if SCTs are neither being requested nor validated.
+ */
+__owur ct_validation_cb SSL_get_ct_validation_callback(const SSL *s);
+__owur ct_validation_cb SSL_CTX_get_ct_validation_callback(const SSL_CTX *ctx);
+
+/* Gets the SCTs received from a connection */
+const STACK_OF(SCT) *SSL_get0_peer_scts(SSL *s);
+
+/* Load the CT log list from the default location */
+int SSL_CTX_set_default_ctlog_list_file(SSL_CTX *ctx);
+/* Load the CT log list from the specified file path */
+int SSL_CTX_set_ctlog_list_file(SSL_CTX *ctx, const char *path);
+
+# endif /* OPENSSL_NO_CT */
+
 /* What the "other" parameter contains in security callback */
 /* Mask for type */
 # define SSL_SECOP_OTHER_TYPE    0xffff0000
@@ -1976,6 +2019,7 @@ void ERR_load_SSL_strings(void);
 
 /* Function codes. */
 # define SSL_F_CHECK_SUITEB_CIPHER_LIST                   331
+# define SSL_F_CT_MOVE_SCTS                               345
 # define SSL_F_D2I_SSL_SESSION                            103
 # define SSL_F_DANE_CTX_ENABLE                            347
 # define SSL_F_DANE_MTYPE_SET                             393
@@ -2058,11 +2102,13 @@ void ERR_load_SSL_strings(void);
 # define SSL_F_SSL_CREATE_CIPHER_LIST                     166
 # define SSL_F_SSL_CTRL                                   232
 # define SSL_F_SSL_CTX_CHECK_PRIVATE_KEY                  168
+# define SSL_F_SSL_CTX_GET_CT_VALIDATION_CALLBACK         349
 # define SSL_F_SSL_CTX_MAKE_PROFILES                      309
 # define SSL_F_SSL_CTX_NEW                                169
 # define SSL_F_SSL_CTX_SET_ALPN_PROTOS                    343
 # define SSL_F_SSL_CTX_SET_CIPHER_LIST                    269
 # define SSL_F_SSL_CTX_SET_CLIENT_CERT_ENGINE             290
+# define SSL_F_SSL_CTX_SET_CT_VALIDATION_CALLBACK         396
 # define SSL_F_SSL_CTX_SET_PURPOSE                        226
 # define SSL_F_SSL_CTX_SET_SESSION_ID_CONTEXT             219
 # define SSL_F_SSL_CTX_SET_SSL_VERSION                    170
@@ -2082,6 +2128,8 @@ void ERR_load_SSL_strings(void);
 # define SSL_F_SSL_DANE_ENABLE                            395
 # define SSL_F_SSL_DO_CONFIG                              391
 # define SSL_F_SSL_DO_HANDSHAKE                           180
+# define SSL_F_SSL_GET0_PEER_SCTS                         397
+# define SSL_F_SSL_GET_CT_VALIDATION_CALLBACK             398
 # define SSL_F_SSL_GET_NEW_SESSION                        181
 # define SSL_F_SSL_GET_PREV_SESSION                       217
 # define SSL_F_SSL_GET_SERVER_CERT_INDEX                  322
@@ -2111,6 +2159,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_F_SSL_SET_ALPN_PROTOS                        344
 # define SSL_F_SSL_SET_CERT                               191
 # define SSL_F_SSL_SET_CIPHER_LIST                        271
+# define SSL_F_SSL_SET_CT_VALIDATION_CALLBACK             399
 # define SSL_F_SSL_SET_FD                                 192
 # define SSL_F_SSL_SET_PKEY                               193
 # define SSL_F_SSL_SET_PURPOSE                            227
@@ -2136,6 +2185,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_F_SSL_USE_RSAPRIVATEKEY                      204
 # define SSL_F_SSL_USE_RSAPRIVATEKEY_ASN1                 205
 # define SSL_F_SSL_USE_RSAPRIVATEKEY_FILE                 206
+# define SSL_F_SSL_VALIDATE_CT                            400
 # define SSL_F_SSL_VERIFY_CERT_CHAIN                      207
 # define SSL_F_SSL_WRITE                                  208
 # define SSL_F_STATE_MACHINE                              353
@@ -2253,6 +2303,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_CONTEXT_NOT_DANE_ENABLED                   167
 # define SSL_R_COOKIE_GEN_CALLBACK_FAILURE                400
 # define SSL_R_COOKIE_MISMATCH                            308
+# define SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED       206
 # define SSL_R_DANE_ALREADY_ENABLED                       172
 # define SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL            173
 # define SSL_R_DANE_NOT_ENABLED                           175
@@ -2377,8 +2428,10 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_REQUIRED_CIPHER_MISSING                    215
 # define SSL_R_REQUIRED_COMPRESSSION_ALGORITHM_MISSING    342
 # define SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING           345
+# define SSL_R_SCT_VERIFICATION_FAILED                    208
 # define SSL_R_SERVERHELLO_TLSEXT                         275
 # define SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED           277
+# define SSL_R_SET_FAILED                                 209
 # define SSL_R_SHUTDOWN_WHILE_IN_INIT                     407
 # define SSL_R_SIGNATURE_ALGORITHMS_ERROR                 360
 # define SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE      220
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index c2fe364..c3344e1 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -241,6 +241,12 @@ extern "C" {
 # define TLSEXT_TYPE_application_layer_protocol_negotiation 16
 
 /*
+ * Extension type for Certificate Transparency
+ * https://tools.ietf.org/html/rfc6962#section-3.3.1
+ */
+# define TLSEXT_TYPE_signed_certificate_timestamp    18
+
+/*
  * ExtensionType value for TLS padding extension.
  * http://tools.ietf.org/html/draft-agl-tls-padding
  */
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 37ebbc8..c2d4bf3 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -70,6 +70,7 @@
 
 static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_CHECK_SUITEB_CIPHER_LIST), "check_suiteb_cipher_list"},
+    {ERR_FUNC(SSL_F_CT_MOVE_SCTS), "CT_move_scts"},
     {ERR_FUNC(SSL_F_D2I_SSL_SESSION), "d2i_SSL_SESSION"},
     {ERR_FUNC(SSL_F_DANE_CTX_ENABLE), "dane_ctx_enable"},
     {ERR_FUNC(SSL_F_DANE_MTYPE_SET), "dane_mtype_set"},
@@ -169,12 +170,16 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_SSL_CREATE_CIPHER_LIST), "ssl_create_cipher_list"},
     {ERR_FUNC(SSL_F_SSL_CTRL), "SSL_ctrl"},
     {ERR_FUNC(SSL_F_SSL_CTX_CHECK_PRIVATE_KEY), "SSL_CTX_check_private_key"},
+    {ERR_FUNC(SSL_F_SSL_CTX_GET_CT_VALIDATION_CALLBACK),
+     "SSL_CTX_get_ct_validation_callback"},
     {ERR_FUNC(SSL_F_SSL_CTX_MAKE_PROFILES), "ssl_ctx_make_profiles"},
     {ERR_FUNC(SSL_F_SSL_CTX_NEW), "SSL_CTX_new"},
     {ERR_FUNC(SSL_F_SSL_CTX_SET_ALPN_PROTOS), "SSL_CTX_set_alpn_protos"},
     {ERR_FUNC(SSL_F_SSL_CTX_SET_CIPHER_LIST), "SSL_CTX_set_cipher_list"},
     {ERR_FUNC(SSL_F_SSL_CTX_SET_CLIENT_CERT_ENGINE),
      "SSL_CTX_set_client_cert_engine"},
+    {ERR_FUNC(SSL_F_SSL_CTX_SET_CT_VALIDATION_CALLBACK),
+     "SSL_CTX_set_ct_validation_callback"},
     {ERR_FUNC(SSL_F_SSL_CTX_SET_PURPOSE), "SSL_CTX_set_purpose"},
     {ERR_FUNC(SSL_F_SSL_CTX_SET_SESSION_ID_CONTEXT),
      "SSL_CTX_set_session_id_context"},
@@ -203,6 +208,9 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_SSL_DANE_ENABLE), "SSL_dane_enable"},
     {ERR_FUNC(SSL_F_SSL_DO_CONFIG), "ssl_do_config"},
     {ERR_FUNC(SSL_F_SSL_DO_HANDSHAKE), "SSL_do_handshake"},
+    {ERR_FUNC(SSL_F_SSL_GET0_PEER_SCTS), "SSL_get0_peer_scts"},
+    {ERR_FUNC(SSL_F_SSL_GET_CT_VALIDATION_CALLBACK),
+     "SSL_get_ct_validation_callback"},
     {ERR_FUNC(SSL_F_SSL_GET_NEW_SESSION), "ssl_get_new_session"},
     {ERR_FUNC(SSL_F_SSL_GET_PREV_SESSION), "ssl_get_prev_session"},
     {ERR_FUNC(SSL_F_SSL_GET_SERVER_CERT_INDEX), "ssl_get_server_cert_index"},
@@ -243,6 +251,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_SSL_SET_ALPN_PROTOS), "SSL_set_alpn_protos"},
     {ERR_FUNC(SSL_F_SSL_SET_CERT), "ssl_set_cert"},
     {ERR_FUNC(SSL_F_SSL_SET_CIPHER_LIST), "SSL_set_cipher_list"},
+    {ERR_FUNC(SSL_F_SSL_SET_CT_VALIDATION_CALLBACK),
+     "SSL_set_ct_validation_callback"},
     {ERR_FUNC(SSL_F_SSL_SET_FD), "SSL_set_fd"},
     {ERR_FUNC(SSL_F_SSL_SET_PKEY), "ssl_set_pkey"},
     {ERR_FUNC(SSL_F_SSL_SET_PURPOSE), "SSL_set_purpose"},
@@ -270,6 +280,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_SSL_USE_RSAPRIVATEKEY), "SSL_use_RSAPrivateKey"},
     {ERR_FUNC(SSL_F_SSL_USE_RSAPRIVATEKEY_ASN1), "SSL_use_RSAPrivateKey_ASN1"},
     {ERR_FUNC(SSL_F_SSL_USE_RSAPRIVATEKEY_FILE), "SSL_use_RSAPrivateKey_file"},
+    {ERR_FUNC(SSL_F_SSL_VALIDATE_CT), "SSL_validate_ct"},
     {ERR_FUNC(SSL_F_SSL_VERIFY_CERT_CHAIN), "ssl_verify_cert_chain"},
     {ERR_FUNC(SSL_F_SSL_WRITE), "SSL_write"},
     {ERR_FUNC(SSL_F_STATE_MACHINE), "state_machine"},
@@ -422,6 +433,8 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
     {ERR_REASON(SSL_R_COOKIE_GEN_CALLBACK_FAILURE),
      "cookie gen callback failure"},
     {ERR_REASON(SSL_R_COOKIE_MISMATCH), "cookie mismatch"},
+    {ERR_REASON(SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED),
+     "custom ext handler already installed"},
     {ERR_REASON(SSL_R_DANE_ALREADY_ENABLED), "dane already enabled"},
     {ERR_REASON(SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL),
      "dane cannot override mtype full"},
@@ -576,9 +589,11 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "required compresssion algorithm missing"},
     {ERR_REASON(SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING),
      "scsv received when renegotiating"},
+    {ERR_REASON(SSL_R_SCT_VERIFICATION_FAILED), "sct verification failed"},
     {ERR_REASON(SSL_R_SERVERHELLO_TLSEXT), "serverhello tlsext"},
     {ERR_REASON(SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED),
      "session id context uninitialized"},
+    {ERR_REASON(SSL_R_SET_FAILED), "set failed"},
     {ERR_REASON(SSL_R_SHUTDOWN_WHILE_IN_INIT), "shutdown while in init"},
     {ERR_REASON(SSL_R_SIGNATURE_ALGORITHMS_ERROR),
      "signature algorithms error"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 98489a1..40c4171 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -159,6 +159,9 @@
 # include <openssl/engine.h>
 #endif
 #include <openssl/async.h>
+#ifndef OPENSSL_NO_CT
+# include <openssl/ct.h>
+#endif
 
 const char SSL_version_str[] = OPENSSL_VERSION_TEXT;
 
@@ -740,6 +743,12 @@ SSL *SSL_new(SSL_CTX *ctx)
 
     s->job = NULL;
 
+#ifndef OPENSSL_NO_CT
+    if (!SSL_set_ct_validation_callback(s, ctx->ct_validation_callback,
+            ctx->ct_validation_callback_arg))
+        goto err;
+#endif
+
     return (s);
  err:
     SSL_free(s);
@@ -1041,6 +1050,10 @@ void SSL_free(SSL *s)
 #endif                         /* OPENSSL_NO_EC */
     sk_X509_EXTENSION_pop_free(s->tlsext_ocsp_exts, X509_EXTENSION_free);
     sk_OCSP_RESPID_pop_free(s->tlsext_ocsp_ids, OCSP_RESPID_free);
+#ifndef OPENSSL_NO_CT
+    SCT_LIST_free(s->scts);
+    OPENSSL_free(s->tlsext_scts);
+#endif
     OPENSSL_free(s->tlsext_ocsp_resp);
     OPENSSL_free(s->alpn_client_proto_list);
 
@@ -2321,7 +2334,11 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
     ret->cert_store = X509_STORE_new();
     if (ret->cert_store == NULL)
         goto err;
-
+#ifndef OPENSSL_NO_CT
+    ret->ctlog_store = CTLOG_STORE_new();
+    if (ret->ctlog_store == NULL)
+        goto err;
+#endif
     if (!ssl_create_cipher_list(ret->method,
                            &ret->cipher_list, &ret->cipher_list_by_id,
                            SSL_DEFAULT_CIPHER_LIST, ret->cert)
@@ -2439,6 +2456,9 @@ void SSL_CTX_free(SSL_CTX *a)
     CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_CTX, a, &a->ex_data);
     lh_SSL_SESSION_free(a->sessions);
     X509_STORE_free(a->cert_store);
+#ifndef OPENSSL_NO_CT
+    CTLOG_STORE_free(a->ctlog_store);
+#endif
     sk_SSL_CIPHER_free(a->cipher_list);
     sk_SSL_CIPHER_free(a->cipher_list_by_id);
     ssl_cert_free(a->cert);
@@ -3796,3 +3816,276 @@ STACK_OF(X509) *SSL_get0_verified_chain(const SSL *s)
 }
 
 IMPLEMENT_OBJ_BSEARCH_GLOBAL_CMP_FN(SSL_CIPHER, SSL_CIPHER, ssl_cipher_id);
+
+#ifndef OPENSSL_NO_CT
+
+/*
+ * Moves SCTs from the |src| stack to the |dst| stack.
+ * The source of each SCT will be set to |origin|.
+ * If |dst| points to a NULL pointer, a new stack will be created and owned by
+ * the caller.
+ * Returns the number of SCTs moved, or a negative integer if an error occurs.
+ */
+static int ct_move_scts(STACK_OF(SCT) **dst, STACK_OF(SCT) *src, sct_source_t origin)
+{
+    int scts_moved = 0;
+    SCT *sct = NULL;
+
+    if (*dst == NULL) {
+        *dst = sk_SCT_new_null();
+        if (*dst == NULL) {
+            SSLerr(SSL_F_CT_MOVE_SCTS, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+
+    while ((sct = sk_SCT_pop(src)) != NULL) {
+        if (SCT_set_source(sct, origin) != 1)
+            goto err;
+
+        if (sk_SCT_push(*dst, sct) <= 0)
+            goto err;
+        scts_moved += 1;
+    }
+
+    return scts_moved;
+err:
+    if (sct != NULL)
+        sk_SCT_push(src, sct); /* Put the SCT back */
+    return scts_moved;
+}
+
+/*
+* Look for data collected during ServerHello and parse if found.
+* Return 1 on success, 0 on failure.
+*/
+static int ct_extract_tls_extension_scts(SSL *s)
+{
+    int scts_extracted = 0;
+
+    if (s->tlsext_scts != NULL) {
+        const unsigned char *p = s->tlsext_scts;
+        STACK_OF(SCT) *scts = o2i_SCT_LIST(NULL, &p, s->tlsext_scts_len);
+
+        scts_extracted = ct_move_scts(&s->scts, scts, SCT_SOURCE_TLS_EXTENSION);
+
+        SCT_LIST_free(scts);
+    }
+
+    return scts_extracted;
+}
+
+/*
+ * Checks for an OCSP response and then attempts to extract any SCTs found if it
+ * contains an SCT X509 extension. They will be stored in |s->scts|.
+ * Returns:
+ * - The number of SCTs extracted, assuming an OCSP response exists.
+ * - 0 if no OCSP response exists or it contains no SCTs.
+ * - A negative integer if an error occurs.
+ */
+static int ct_extract_ocsp_response_scts(SSL *s)
+{
+    int scts_extracted = 0;
+    const unsigned char *p;
+    OCSP_BASICRESP *br = NULL;
+    OCSP_RESPONSE *rsp = NULL;
+    STACK_OF(SCT) *scts = NULL;
+    int i;
+
+    if (s->tlsext_ocsp_resp == NULL || s->tlsext_ocsp_resplen == 0)
+        goto err;
+
+    p = s->tlsext_ocsp_resp;
+    rsp = d2i_OCSP_RESPONSE(NULL, &p, s->tlsext_ocsp_resplen);
+    if (rsp == NULL)
+        goto err;
+
+    br = OCSP_response_get1_basic(rsp);
+    if (br == NULL)
+        goto err;
+
+    for (i = 0; i < OCSP_resp_count(br); ++i) {
+        OCSP_SINGLERESP *single = OCSP_resp_get0(br, i);
+
+        if (single == NULL)
+            continue;
+
+        scts = OCSP_SINGLERESP_get1_ext_d2i(single, NID_ct_cert_scts, NULL, NULL);
+        scts_extracted = ct_move_scts(&s->scts, scts,
+                                      SCT_SOURCE_OCSP_STAPLED_RESPONSE);
+        if (scts_extracted < 0)
+            goto err;
+    }
+err:
+    SCT_LIST_free(scts);
+    OCSP_BASICRESP_free(br);
+    OCSP_RESPONSE_free(rsp);
+    return scts_extracted;
+}
+
+/*
+ * Attempts to extract SCTs from the peer certificate.
+ * Return the number of SCTs extracted, or a negative integer if an error
+ * occurs.
+ */
+static int ct_extract_x509v3_extension_scts(SSL *s)
+{
+    int scts_extracted = 0;
+    X509 *cert = SSL_get_peer_certificate(s);
+
+    if (cert != NULL) {
+        STACK_OF(SCT) *scts =
+            X509_get_ext_d2i(cert, NID_ct_precert_scts, NULL, NULL);
+
+        scts_extracted =
+            ct_move_scts(&s->scts, scts, SCT_SOURCE_X509V3_EXTENSION);
+
+        SCT_LIST_free(scts);
+    }
+
+    return scts_extracted;
+}
+
+/*
+ * Attempts to find all received SCTs by checking TLS extensions, the OCSP
+ * response (if it exists) and X509v3 extensions in the certificate.
+ * Returns NULL if an error occurs.
+ */
+const STACK_OF(SCT) *SSL_get0_peer_scts(SSL *s)
+{
+    if (!s->scts_parsed) {
+        if (ct_extract_tls_extension_scts(s) < 0 ||
+            ct_extract_ocsp_response_scts(s) < 0 ||
+            ct_extract_x509v3_extension_scts(s) < 0)
+            goto err;
+
+        s->scts_parsed = 1;
+    }
+    return s->scts;
+err:
+    return NULL;
+}
+
+int SSL_set_ct_validation_callback(SSL *s, ct_validation_cb callback, void *arg)
+{
+    int ret = 0;
+
+    /*
+     * Since code exists that uses the custom extension handler for CT, look
+     * for this and throw an error if they have already registered to use CT.
+     */
+    if (callback != NULL && SSL_CTX_has_client_custom_ext(s->ctx,
+            TLSEXT_TYPE_signed_certificate_timestamp)) {
+        SSLerr(SSL_F_SSL_SET_CT_VALIDATION_CALLBACK,
+               SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED);
+        goto err;
+    }
+
+    s->ct_validation_callback = callback;
+    s->ct_validation_callback_arg = arg;
+
+    if (callback != NULL) {
+        /* If we are validating CT, then we MUST accept SCTs served via OCSP */
+        if (!SSL_set_tlsext_status_type(s, TLSEXT_STATUSTYPE_ocsp))
+            goto err;
+    }
+
+    ret = 1;
+err:
+    return ret;
+}
+
+int SSL_CTX_set_ct_validation_callback(SSL_CTX *ctx, ct_validation_cb callback,
+                                       void *arg)
+{
+    int ret = 0;
+
+    /*
+     * Since code exists that uses the custom extension handler for CT, look for
+     * this and throw an error if they have already registered to use CT.
+     */
+    if (callback != NULL && SSL_CTX_has_client_custom_ext(ctx,
+            TLSEXT_TYPE_signed_certificate_timestamp)) {
+        SSLerr(SSL_F_SSL_CTX_SET_CT_VALIDATION_CALLBACK,
+               SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED);
+        goto err;
+    }
+
+    ctx->ct_validation_callback = callback;
+    ctx->ct_validation_callback_arg = arg;
+    ret = 1;
+err:
+    return ret;
+}
+
+ct_validation_cb SSL_get_ct_validation_callback(const SSL *s)
+{
+    return s->ct_validation_callback;
+}
+
+ct_validation_cb SSL_CTX_get_ct_validation_callback(const SSL_CTX *ctx)
+{
+    return ctx->ct_validation_callback;
+}
+
+int SSL_validate_ct(SSL *s)
+{
+    int ret = 0;
+    X509 *cert = SSL_get_peer_certificate(s);
+    X509 *issuer = NULL;
+    CT_POLICY_EVAL_CTX *ctx = NULL;
+    const STACK_OF(SCT) *scts;
+
+    /* If no callback is set, attempt no validation - just return success */
+    if (s->ct_validation_callback == NULL)
+        return 1;
+
+    if (cert == NULL) {
+        SSLerr(SSL_F_SSL_VALIDATE_CT, SSL_R_NO_CERTIFICATE_ASSIGNED);
+        goto end;
+    }
+
+    if (s->verified_chain != NULL && sk_X509_num(s->verified_chain) > 1)
+        issuer = sk_X509_value(s->verified_chain, 1);
+
+    ctx = CT_POLICY_EVAL_CTX_new();
+    if (ctx == NULL) {
+        SSLerr(SSL_F_SSL_VALIDATE_CT, ERR_R_MALLOC_FAILURE);
+        goto end;
+    }
+
+    CT_POLICY_EVAL_CTX_set0_cert(ctx, cert);
+    CT_POLICY_EVAL_CTX_set0_issuer(ctx, issuer);
+    CT_POLICY_EVAL_CTX_set0_log_store(ctx, s->ctx->ctlog_store);
+
+    scts = SSL_get0_peer_scts(s);
+
+    if (SCT_LIST_validate(scts, ctx) != 1) {
+        SSLerr(SSL_F_SSL_VALIDATE_CT, SSL_R_SCT_VERIFICATION_FAILED);
+        goto end;
+    }
+
+    ret = s->ct_validation_callback(ctx, scts, s->ct_validation_callback_arg);
+    if (ret < 0)
+        ret = 0; /* This function returns 0 on failure */
+
+end:
+    CT_POLICY_EVAL_CTX_free(ctx);
+    return ret;
+}
+
+int SSL_CTX_set_default_ctlog_list_file(SSL_CTX *ctx)
+{
+    int ret = CTLOG_STORE_load_default_file(ctx->ctlog_store);
+
+    /* Clear any errors if the default file does not exist */
+    ERR_clear_error();
+    return ret;
+}
+
+int SSL_CTX_set_ctlog_list_file(SSL_CTX *ctx, const char *path)
+{
+    return CTLOG_STORE_load_file(ctx->ctlog_store, path);
+}
+
+#endif
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 4fc079b..ca928e7 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -164,7 +164,9 @@
 # include <openssl/ssl.h>
 # include <openssl/async.h>
 # include <openssl/symhacks.h>
-
+# ifndef OPENSSL_NO_CT
+#  include <openssl/ct.h>
+# endif
 #include "record/record.h"
 #include "statem/statem.h"
 #include "packet_locl.h"
@@ -815,6 +817,16 @@ struct ssl_ctx_st {
 
     int quiet_shutdown;
 
+#  ifndef OPENSSL_NO_CT
+    CTLOG_STORE *ctlog_store; /* CT Log Store */
+    /*
+    * Validates that the SCTs (Signed Certificate Timestamps) are sufficient.
+    * If they are not, the connection should be aborted.
+    */
+    ct_validation_cb ct_validation_callback;
+    void *ct_validation_callback_arg;
+#  endif
+
     /*
      * Maximum amount of data to send in one fragment. actual record size can
      * be more than this due to padding and MAC overheads.
@@ -1088,6 +1100,26 @@ struct ssl_st {
     /* certificate status request info */
     /* Status type or -1 if no status type */
     int tlsext_status_type;
+#  ifndef OPENSSL_NO_CT
+    /*
+    * Validates that the SCTs (Signed Certificate Timestamps) are sufficient.
+    * If they are not, the connection should be aborted.
+    */
+    ct_validation_cb ct_validation_callback;
+    /* User-supplied argument tha tis passed to the ct_validation_callback */
+    void *ct_validation_callback_arg;
+    /*
+     * Consolidated stack of SCTs from all sources.
+     * Lazily populated by CT_get_peer_scts(SSL*)
+     */
+    STACK_OF(SCT) *scts;
+    /* Raw extension data, if seen */
+    unsigned char *tlsext_scts;
+    /* Length of raw extension data, if seen */
+    uint16_t tlsext_scts_len;
+    /* Have we attempted to find/parse SCTs yet? */
+    int scts_parsed;
+#  endif
     /* Expect OCSP CertificateStatus message */
     int tlsext_status_expected;
     /* OCSP status request only */
@@ -2037,6 +2069,10 @@ int tls1_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain,
                      int idx);
 void tls1_set_cert_validity(SSL *s);
 
+#ifndef OPENSSL_NO_CT
+__owur int SSL_validate_ct(SSL *s);
+#endif
+
 #  ifndef OPENSSL_NO_DH
 __owur DH *ssl_get_auto_dh(SSL *s);
 #  endif
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index 31b18ca..b8ca82f 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -2058,6 +2058,15 @@ MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt)
         }
     }
 
+#ifndef OPENSSL_NO_CT
+    if (s->ct_validation_callback != NULL) {
+        if (!SSL_validate_ct(s)) {
+            ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+            return MSG_PROCESS_ERROR;
+        }
+    }
+#endif
+
 #ifndef OPENSSL_NO_SCTP
     /* Only applies to renegotiation */
     if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s))
diff --git a/ssl/t1_ext.c b/ssl/t1_ext.c
index 09f1044..eb7d8a7 100644
--- a/ssl/t1_ext.c
+++ b/ssl/t1_ext.c
@@ -54,11 +54,14 @@
 
 /* Custom extension utility functions */
 
+#ifndef OPENSSL_NO_CT
+# include <openssl/ct.h>
+#endif
 #include "ssl_locl.h"
 
 
 /* Find a custom extension from the list. */
-static custom_ext_method *custom_ext_find(custom_ext_methods *exts,
+static custom_ext_method *custom_ext_find(const custom_ext_methods *exts,
                                           unsigned int ext_type)
 {
     size_t i;
@@ -211,8 +214,12 @@ static int custom_ext_meth_add(custom_ext_methods *exts,
      */
     if (!add_cb && free_cb)
         return 0;
-    /* Don't add if extension supported internally. */
-    if (SSL_extension_supported(ext_type))
+    /*
+     * Don't add if extension supported internally, but make exception
+     * for extension types that previously were not supported, but now are.
+     */
+    if (SSL_extension_supported(ext_type) &&
+        ext_type != TLSEXT_TYPE_signed_certificate_timestamp)
         return 0;
     /* Extension type must fit in 16 bits */
     if (ext_type > 0xffff)
@@ -241,6 +248,12 @@ static int custom_ext_meth_add(custom_ext_methods *exts,
     return 1;
 }
 
+/* Return true if a client custom extension exists, false otherwise */
+int SSL_CTX_has_client_custom_ext(const SSL_CTX *ctx, unsigned int ext_type)
+{
+    return custom_ext_find(&ctx->cert->cli_ext, ext_type) != NULL;
+}
+
 /* Application level functions to add custom extension callbacks */
 int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
                                   custom_ext_add_cb add_cb,
@@ -249,8 +262,25 @@ int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
                                   custom_ext_parse_cb parse_cb,
                                   void *parse_arg)
 {
-    return custom_ext_meth_add(&ctx->cert->cli_ext, ext_type,
-                               add_cb, free_cb, add_arg, parse_cb, parse_arg);
+    int ret = custom_ext_meth_add(&ctx->cert->cli_ext, ext_type, add_cb,
+                                  free_cb, add_arg, parse_cb, parse_arg);
+
+    if (ret != 1)
+        goto end;
+
+#ifndef OPENSSL_NO_CT
+    /*
+     * We don't want applications registering callbacks for SCT extensions
+     * whilst simultaneously using the built-in SCT validation features, as
+     * these two things may not play well together.
+     */
+    if (ext_type == TLSEXT_TYPE_signed_certificate_timestamp &&
+        SSL_CTX_get_ct_validation_callback(ctx) != NULL) {
+        ret = 0;
+    }
+#endif
+end:
+    return ret;
 }
 
 int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
@@ -280,6 +310,7 @@ int SSL_extension_supported(unsigned int ext_type)
     case TLSEXT_TYPE_signature_algorithms:
     case TLSEXT_TYPE_srp:
     case TLSEXT_TYPE_status_request:
+    case TLSEXT_TYPE_signed_certificate_timestamp:
     case TLSEXT_TYPE_use_srtp:
 #ifdef TLSEXT_TYPE_encrypt_then_mac
     case TLSEXT_TYPE_encrypt_then_mac:
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index f068a20..70c47c8 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -120,6 +120,9 @@
 # include <openssl/bn.h>
 #endif
 #include "ssl_locl.h"
+#ifndef OPENSSL_NO_CT
+# include <openssl/ct.h>
+#endif
 
 static int tls_decrypt_ticket(SSL *s, const unsigned char *tick, int ticklen,
                               const unsigned char *sess_id, int sesslen,
@@ -1450,6 +1453,12 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *buf,
     s2n(TLSEXT_TYPE_encrypt_then_mac, ret);
     s2n(0, ret);
 #endif
+#ifndef OPENSSL_NO_CT
+    if (s->ct_validation_callback != NULL) {
+        s2n(TLSEXT_TYPE_signed_certificate_timestamp, ret);
+        s2n(0, ret);
+    }
+#endif
     s2n(TLSEXT_TYPE_extended_master_secret, ret);
     s2n(0, ret);
 
@@ -2414,6 +2423,30 @@ static int ssl_scan_serverhello_tlsext(SSL *s, PACKET *pkt, int *al)
             /* Set flag to expect CertificateStatus message */
             s->tlsext_status_expected = 1;
         }
+#ifndef OPENSSL_NO_CT
+        /*
+         * Only take it if we asked for it - i.e if there is no CT validation
+         * callback set, then a custom extension MAY be processing it, so we
+         * need to let control continue to flow to that.
+         */
+        else if (type == TLSEXT_TYPE_signed_certificate_timestamp &&
+                 s->ct_validation_callback != NULL) {
+            /* Simply copy it off for later processing */
+            if (s->tlsext_scts != NULL) {
+                OPENSSL_free(s->tlsext_scts);
+                s->tlsext_scts = NULL;
+            }
+            s->tlsext_scts_len = size;
+            if (size > 0) {
+                s->tlsext_scts = OPENSSL_malloc(size);
+                if (s->tlsext_scts == NULL) {
+                    *al = TLS1_AD_INTERNAL_ERROR;
+                    return 0;
+                }
+                memcpy(s->tlsext_scts, data, size);
+            }
+        }
+#endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
         else if (type == TLSEXT_TYPE_next_proto_neg &&
                  s->s3->tmp.finish_md_len == 0) {
diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c
index fdc61a5..cb22d49 100644
--- a/ssl/t1_trce.c
+++ b/ssl/t1_trce.c
@@ -498,6 +498,7 @@ static ssl_trace_tbl ssl_exts_tbl[] = {
     {TLSEXT_TYPE_session_ticket, "session_ticket"},
     {TLSEXT_TYPE_renegotiate, "renegotiate"},
     {TLSEXT_TYPE_next_proto_neg, "next_proto_neg"},
+    {TLSEXT_TYPE_signed_certificate_timestamp, "signed_certificate_timestamps"},
     {TLSEXT_TYPE_padding, "padding"},
     {TLSEXT_TYPE_encrypt_then_mac, "encrypt_then_mac"},
     {TLSEXT_TYPE_extended_master_secret, "extended_master_secret"}
diff --git a/test/recipes/80-test_ssl.t b/test/recipes/80-test_ssl.t
index bcab4b5..37237dc 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 b5d44a0..da9391a 100644
--- a/test/ssltest.c
+++ b/test/ssltest.c
@@ -187,6 +187,9 @@
 # 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);
@@ -1593,6 +1625,11 @@ int main(int argc, char *argv[])
         /* 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,
@@ -1682,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;
         }
diff --git a/util/ssleay.num b/util/ssleay.num
index b4b01fa..e3f6f05 100755
--- a/util/ssleay.num
+++ b/util/ssleay.num
@@ -422,3 +422,11 @@ SSL_get_default_passwd_cb_userdata      477	1_1_0	EXIST::FUNCTION:
 SSL_get_default_passwd_cb               478	1_1_0	EXIST::FUNCTION:
 SSL_CTX_get_default_passwd_cb_userdata  479	1_1_0	EXIST::FUNCTION:
 SSL_CTX_get_default_passwd_cb           480	1_1_0	EXIST::FUNCTION:
+SSL_CTX_get_ct_validation_callback      481	1_1_0	EXIST::FUNCTION:CT
+SSL_get_ct_validation_callback          482	1_1_0	EXIST::FUNCTION:CT
+SSL_CTX_has_client_custom_ext           483	1_1_0	EXIST::FUNCTION:
+SSL_CTX_set_ctlog_list_file             484	1_1_0	EXIST::FUNCTION:CT
+SSL_CTX_set_default_ctlog_list_file     485	1_1_0	EXIST::FUNCTION:CT
+SSL_set_ct_validation_callback          486	1_1_0	EXIST::FUNCTION:CT
+SSL_get0_peer_scts                      487	1_1_0	EXIST::FUNCTION:CT
+SSL_CTX_set_ct_validation_callback      488	1_1_0	EXIST::FUNCTION:CT


More information about the openssl-commits mailing list