Adds CT validation to SSL connections
authorRob Percival <robpercival@google.com>
Thu, 3 Mar 2016 16:19:23 +0000 (16:19 +0000)
committerRich Salz <rsalz@openssl.org>
Fri, 4 Mar 2016 15:50:10 +0000 (10:50 -0500)
Disabled by default, but can be enabled by setting the
ct_validation_callback on a SSL or SSL_CTX.

Reviewed-by: Ben Laurie <ben@openssl.org>
Reviewed-by: Rich Salz <rsalz@openssl.org>
include/openssl/ssl.h
include/openssl/tls1.h
ssl/ssl_err.c
ssl/ssl_lib.c
ssl/ssl_locl.h
ssl/statem/statem_clnt.c
ssl/t1_ext.c
ssl/t1_lib.c
ssl/t1_trce.c
test/ssltest.c
util/ssleay.num

index 6e22396..e827214 100644 (file)
 
 # 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
index c2fe364..c3344e1 100644 (file)
@@ -240,6 +240,12 @@ extern "C" {
 /* ExtensionType value from RFC7301 */
 # 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
index 37ebbc8..c2d4bf3 100644 (file)
@@ -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"},
index 98489a1..40c4171 100644 (file)
 # 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
index 4fc079b..ca928e7 100644 (file)
 # 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
index 31b18ca..b8ca82f 100644 (file)
@@ -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))
index 09f1044..eb7d8a7 100644 (file)
 
 /* 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:
index f068a20..70c47c8 100644 (file)
 # 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,
@@ -1449,6 +1452,12 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *buf,
 #ifdef TLSEXT_TYPE_encrypt_then_mac
     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) {
index fdc61a5..cb22d49 100644 (file)
@@ -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"}
index b5d44a0..f65358a 100644 (file)
@@ -1586,8 +1586,10 @@ 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_verify_paths(c_ctx)) ||
+        (!SSL_CTX_set_default_ctlog_list_file(c_ctx))) {
         /* fprintf(stderr,"SSL_load_verify_locations\n"); */
         ERR_print_errors(bio_err);
         /* goto end; */
index b4b01fa..e3f6f05 100755 (executable)
@@ -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