From: Rob Percival Date: Thu, 3 Mar 2016 16:19:23 +0000 (+0000) Subject: Adds CT validation to SSL connections X-Git-Tag: OpenSSL_1_1_0-pre4~367 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ed29e82adeea9d2ee89aeadf5646d4d1350a6855;p=openssl 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 Reviewed-by: Rich Salz --- diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 6e223960a9..e827214209 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -159,6 +159,9 @@ # include # include +# ifndef OPENSSL_NO_CT +# include +# 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 c2fe36430b..c3344e193f 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -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 diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 37ebbc84e3..c2d4bf3017 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 98489a17e7..40c4171dec 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -159,6 +159,9 @@ # include #endif #include +#ifndef OPENSSL_NO_CT +# include +#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 4fc079baed..ca928e7d5d 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -164,7 +164,9 @@ # include # include # include - +# ifndef OPENSSL_NO_CT +# include +# 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 31b18caa7b..b8ca82f073 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 09f1044039..eb7d8a74b2 100644 --- a/ssl/t1_ext.c +++ b/ssl/t1_ext.c @@ -54,11 +54,14 @@ /* Custom extension utility functions */ +#ifndef OPENSSL_NO_CT +# include +#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 f068a2008a..70c47c8e65 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -120,6 +120,9 @@ # include #endif #include "ssl_locl.h" +#ifndef OPENSSL_NO_CT +# include +#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) { diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c index fdc61a5f33..cb22d49c82 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/ssltest.c b/test/ssltest.c index b5d44a0c0f..f65358ac40 100644 --- a/test/ssltest.c +++ b/test/ssltest.c @@ -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; */ diff --git a/util/ssleay.num b/util/ssleay.num index b4b01faab2..e3f6f05a0d 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