diff options
author | Brad King <brad.king@kitware.com> | 2018-05-18 14:16:50 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2018-05-18 14:16:50 (GMT) |
commit | 3e913b819d8d8118d5e8dc3b7289f622e9ca92e5 (patch) | |
tree | 82c19f5ec814c84b986e54e3fc6fa0a83622fd81 /Utilities/cmcurl/lib/vtls | |
parent | f3c73b878c594d40119e480ca1074e733d7ba1ce (diff) | |
parent | d431136e029c652f5913bcebeaab3b9236b114c4 (diff) | |
download | CMake-3e913b819d8d8118d5e8dc3b7289f622e9ca92e5.zip CMake-3e913b819d8d8118d5e8dc3b7289f622e9ca92e5.tar.gz CMake-3e913b819d8d8118d5e8dc3b7289f622e9ca92e5.tar.bz2 |
Merge branch 'upstream-curl' into update-curl
* upstream-curl:
curl 2018-05-15 (cb013830)
Diffstat (limited to 'Utilities/cmcurl/lib/vtls')
-rw-r--r-- | Utilities/cmcurl/lib/vtls/axtls.c | 10 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/cyassl.c | 31 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/darwinssl.c | 90 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/gskit.c | 10 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/gtls.c | 31 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/mbedtls.c | 21 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/nss.c | 27 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/openssl.c | 227 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/polarssl.c | 16 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/schannel.c | 483 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/schannel.h | 40 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/schannel_verify.c | 551 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/vtls.c | 25 | ||||
-rw-r--r-- | Utilities/cmcurl/lib/vtls/vtls.h | 21 |
14 files changed, 1228 insertions, 355 deletions
diff --git a/Utilities/cmcurl/lib/vtls/axtls.c b/Utilities/cmcurl/lib/vtls/axtls.c index 9294f49..5ed898b 100644 --- a/Utilities/cmcurl/lib/vtls/axtls.c +++ b/Utilities/cmcurl/lib/vtls/axtls.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2010, DirecTV, Contact: Eric Hu, <ehu@directv.com>. - * Copyright (C) 2010 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -703,13 +703,7 @@ static void *Curl_axtls_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_axtls = { { CURLSSLBACKEND_AXTLS, "axtls" }, /* info */ - - 0, /* have_ca_path */ - 0, /* have_certinfo */ - 0, /* have_pinnedpubkey */ - 0, /* have_ssl_ctx */ - 0, /* support_https_proxy */ - + 0, /* no fancy stuff */ sizeof(struct ssl_backend_data), /* diff --git a/Utilities/cmcurl/lib/vtls/cyassl.c b/Utilities/cmcurl/lib/vtls/cyassl.c index 46b71bf..20ce460 100644 --- a/Utilities/cmcurl/lib/vtls/cyassl.c +++ b/Utilities/cmcurl/lib/vtls/cyassl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -187,8 +187,13 @@ cyassl_connect_step1(struct connectdata *conn, use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_0: +#ifdef WOLFSSL_ALLOW_TLSV10 req_method = TLSv1_client_method(); use_sni(TRUE); +#else + failf(data, "CyaSSL does not support TLS 1.0"); + return CURLE_NOT_BUILT_IN; +#endif break; case CURL_SSLVERSION_TLSv1_1: req_method = TLSv1_1_client_method(); @@ -199,8 +204,14 @@ cyassl_connect_step1(struct connectdata *conn, use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_3: +#ifdef WOLFSSL_TLS13 + req_method = wolfTLSv1_3_client_method(); + use_sni(TRUE); + break; +#else failf(data, "CyaSSL: TLS 1.3 is not yet supported"); return CURLE_SSL_CONNECT_ERROR; +#endif case CURL_SSLVERSION_SSLv3: #ifdef WOLFSSL_ALLOW_SSLV3 req_method = SSLv3_client_method(); @@ -245,7 +256,11 @@ cyassl_connect_step1(struct connectdata *conn, */ if((wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1) != 1) && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_1) != 1) && - (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_2) != 1)) { + (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_2) != 1) +#ifdef WOLFSSL_TLS13 + && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_3) != 1) +#endif + ) { failf(data, "SSL: couldn't set the minimum protocol version"); return CURLE_SSL_CONNECT_ERROR; } @@ -956,7 +971,7 @@ static CURLcode Curl_cyassl_random(struct Curl_easy *data, return CURLE_OK; } -static void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */ +static CURLcode Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */ size_t tmplen, unsigned char *sha256sum /* output */, size_t unused) @@ -966,6 +981,7 @@ static void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */ InitSha256(&SHA256pw); Sha256Update(&SHA256pw, tmp, (word32)tmplen); Sha256Final(&SHA256pw, sha256sum); + return CURLE_OK; } static void *Curl_cyassl_get_internals(struct ssl_connect_data *connssl, @@ -978,15 +994,10 @@ static void *Curl_cyassl_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_cyassl = { { CURLSSLBACKEND_WOLFSSL, "WolfSSL" }, /* info */ - 0, /* have_ca_path */ - 0, /* have_certinfo */ #ifdef KEEP_PEER_CERT - 1, /* have_pinnedpubkey */ -#else - 0, /* have_pinnedpubkey */ + SSLSUPP_PINNEDPUBKEY | #endif - 1, /* have_ssl_ctx */ - 0, /* support_https_proxy */ + SSLSUPP_SSL_CTX, sizeof(struct ssl_backend_data), diff --git a/Utilities/cmcurl/lib/vtls/darwinssl.c b/Utilities/cmcurl/lib/vtls/darwinssl.c index 53a7ec3..45fe49d 100644 --- a/Utilities/cmcurl/lib/vtls/darwinssl.c +++ b/Utilities/cmcurl/lib/vtls/darwinssl.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>. - * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1135,28 +1135,79 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, raise linker errors when used on that cat for some reason. */ #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS if(CFURLCreateDataAndPropertiesFromResource(NULL, pkcs_url, &pkcs_data, - NULL, NULL, &status)) { + NULL, NULL, &status)) { + CFArrayRef items = NULL; + + /* On iOS SecPKCS12Import will never add the client certificate to the + * Keychain. + * + * It gives us back a SecIdentityRef that we can use directly. */ +#if CURL_BUILD_IOS const void *cKeys[] = {kSecImportExportPassphrase}; const void *cValues[] = {password}; CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, password ? 1L : 0L, NULL, NULL); - CFArrayRef items = NULL; - /* Here we go: */ - status = SecPKCS12Import(pkcs_data, options, &items); - if(status == errSecSuccess && items && CFArrayGetCount(items)) { - CFDictionaryRef identity_and_trust = CFArrayGetValueAtIndex(items, 0L); - const void *temp_identity = CFDictionaryGetValue(identity_and_trust, - kSecImportItemIdentity); + if(options != NULL) { + status = SecPKCS12Import(pkcs_data, options, &items); + CFRelease(options); + } + + + /* On macOS SecPKCS12Import will always add the client certificate to + * the Keychain. + * + * As this doesn't match iOS, and apps may not want to see their client + * certificate saved in the the user's keychain, we use SecItemImport + * with a NULL keychain to avoid importing it. + * + * This returns a SecCertificateRef from which we can construct a + * SecIdentityRef. + */ +#elif CURL_BUILD_MAC_10_7 + SecItemImportExportKeyParameters keyParams; + SecExternalFormat inputFormat = kSecFormatPKCS12; + SecExternalItemType inputType = kSecItemTypeCertificate; - /* Retain the identity; we don't care about any other data... */ - CFRetain(temp_identity); - *out_cert_and_key = (SecIdentityRef)temp_identity; + memset(&keyParams, 0x00, sizeof(keyParams)); + keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; + keyParams.passphrase = password; + + status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType, + 0, &keyParams, NULL, &items); +#endif + + + /* Extract the SecIdentityRef */ + if(status == errSecSuccess && items && CFArrayGetCount(items)) { + CFIndex i, count; + count = CFArrayGetCount(items); + + for(i = 0; i < count; i++) { + CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i); + CFTypeID itemID = CFGetTypeID(item); + + if(itemID == CFDictionaryGetTypeID()) { + CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue( + (CFDictionaryRef) item, + kSecImportItemIdentity); + CFRetain(identity); + *out_cert_and_key = (SecIdentityRef) identity; + break; + } +#if CURL_BUILD_MAC_10_7 + else if(itemID == SecCertificateGetTypeID()) { + status = SecIdentityCreateWithCertificate(NULL, + (SecCertificateRef) item, + out_cert_and_key); + break; + } +#endif + } } if(items) CFRelease(items); - CFRelease(options); CFRelease(pkcs_data); } #endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ @@ -1340,7 +1391,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, #endif /* CURL_BUILD_MAC */ #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLCreateContext != NULL) { /* use the newer API if avaialble */ + if(SSLCreateContext != NULL) { /* use the newer API if available */ if(BACKEND->ssl_ctx) CFRelease(BACKEND->ssl_ctx); BACKEND->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); @@ -2843,13 +2894,14 @@ static CURLcode Curl_darwinssl_md5sum(unsigned char *tmp, /* input */ return CURLE_OK; } -static void Curl_darwinssl_sha256sum(const unsigned char *tmp, /* input */ +static CURLcode Curl_darwinssl_sha256sum(const unsigned char *tmp, /* input */ size_t tmplen, unsigned char *sha256sum, /* output */ size_t sha256len) { assert(sha256len >= CURL_SHA256_DIGEST_LENGTH); (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum); + return CURLE_OK; } static bool Curl_darwinssl_false_start(void) @@ -2977,15 +3029,11 @@ static void *Curl_darwinssl_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_darwinssl = { { CURLSSLBACKEND_DARWINSSL, "darwinssl" }, /* info */ - 0, /* have_ca_path */ - 0, /* have_certinfo */ #ifdef DARWIN_SSL_PINNEDPUBKEY - 1, /* have_pinnedpubkey */ + SSLSUPP_PINNEDPUBKEY, #else - 0, /* have_pinnedpubkey */ + 0, #endif /* DARWIN_SSL_PINNEDPUBKEY */ - 0, /* have_ssl_ctx */ - 0, /* support_https_proxy */ sizeof(struct ssl_backend_data), diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c index 8f0cc0b..b0856cd 100644 --- a/Utilities/cmcurl/lib/vtls/gskit.c +++ b/Utilities/cmcurl/lib/vtls/gskit.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1353,12 +1353,8 @@ static void *Curl_gskit_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_gskit = { { CURLSSLBACKEND_GSKIT, "gskit" }, /* info */ - 0, /* have_ca_path */ - 1, /* have_certinfo */ - 0, /* have_pinnedpubkey */ - 0, /* have_ssl_ctx */ - /* TODO: convert to 1 and fix test #1014 (if need) */ - 0, /* support_https_proxy */ + SSLSUPP_CERTINFO | + SSLSUPP_PINNEDPUBKEY, sizeof(struct ssl_backend_data), diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index 30b255b..207b0fd 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -60,15 +60,6 @@ /* The last #include file should be: */ #include "memdebug.h" -#ifndef GNUTLS_POINTER_TO_SOCKET_CAST -#define GNUTLS_POINTER_TO_SOCKET_CAST(p) \ - ((curl_socket_t) ((char *)(p) - (char *)NULL)) -#endif -#ifndef GNUTLS_SOCKET_TO_POINTER_CAST -#define GNUTLS_SOCKET_TO_POINTER_CAST(s) \ - ((void *) ((char *)NULL + (s))) -#endif - /* Enable GnuTLS debugging by defining GTLSDEBUG */ /*#define GTLSDEBUG */ @@ -161,7 +152,8 @@ static int gtls_mapped_sockerrno(void) static ssize_t Curl_gtls_push(void *s, const void *buf, size_t len) { - ssize_t ret = swrite(GNUTLS_POINTER_TO_SOCKET_CAST(s), buf, len); + curl_socket_t sock = *(curl_socket_t *)s; + ssize_t ret = swrite(sock, buf, len); #if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS) if(ret < 0) gnutls_transport_set_global_errno(gtls_mapped_sockerrno()); @@ -171,7 +163,8 @@ static ssize_t Curl_gtls_push(void *s, const void *buf, size_t len) static ssize_t Curl_gtls_pull(void *s, void *buf, size_t len) { - ssize_t ret = sread(GNUTLS_POINTER_TO_SOCKET_CAST(s), buf, len); + curl_socket_t sock = *(curl_socket_t *)s; + ssize_t ret = sread(sock, buf, len); #if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS) if(ret < 0) gnutls_transport_set_global_errno(gtls_mapped_sockerrno()); @@ -857,7 +850,7 @@ gtls_connect_step1(struct connectdata *conn, } else { /* file descriptor for the socket */ - transport_ptr = GNUTLS_SOCKET_TO_POINTER_CAST(conn->sock[sockindex]); + transport_ptr = &conn->sock[sockindex]; gnutls_transport_push = Curl_gtls_push; gnutls_transport_pull = Curl_gtls_pull; } @@ -1770,7 +1763,7 @@ static CURLcode Curl_gtls_md5sum(unsigned char *tmp, /* input */ return CURLE_OK; } -static void Curl_gtls_sha256sum(const unsigned char *tmp, /* input */ +static CURLcode Curl_gtls_sha256sum(const unsigned char *tmp, /* input */ size_t tmplen, unsigned char *sha256sum, /* output */ size_t sha256len) @@ -1787,6 +1780,7 @@ static void Curl_gtls_sha256sum(const unsigned char *tmp, /* input */ memcpy(sha256sum, gcry_md_read(SHA256pw, 0), sha256len); gcry_md_close(SHA256pw); #endif + return CURLE_OK; } static bool Curl_gtls_cert_status_request(void) @@ -1808,11 +1802,10 @@ static void *Curl_gtls_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_gnutls = { { CURLSSLBACKEND_GNUTLS, "gnutls" }, /* info */ - 1, /* have_ca_path */ - 1, /* have_certinfo */ - 1, /* have_pinnedpubkey */ - 0, /* have_ssl_ctx */ - 1, /* support_https_proxy */ + SSLSUPP_CA_PATH | + SSLSUPP_CERTINFO | + SSLSUPP_PINNEDPUBKEY | + SSLSUPP_HTTPS_PROXY, sizeof(struct ssl_backend_data), diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index 28251a3..d7759dc 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> - * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -815,7 +815,7 @@ static void Curl_mbedtls_session_free(void *ptr) static size_t Curl_mbedtls_version(char *buffer, size_t size) { unsigned int version = mbedtls_version_get_number(); - return snprintf(buffer, size, "mbedTLS/%d.%d.%d", version>>24, + return snprintf(buffer, size, "mbedTLS/%u.%u.%u", version>>24, (version>>16)&0xff, (version>>8)&0xff); } @@ -1023,13 +1023,20 @@ static bool Curl_mbedtls_data_pending(const struct connectdata *conn, return mbedtls_ssl_get_bytes_avail(&BACKEND->ssl) != 0; } -static void Curl_mbedtls_sha256sum(const unsigned char *input, +static CURLcode Curl_mbedtls_sha256sum(const unsigned char *input, size_t inputlen, unsigned char *sha256sum, size_t sha256len UNUSED_PARAM) { (void)sha256len; +#if MBEDTLS_VERSION_NUMBER < 0x02070000 mbedtls_sha256(input, inputlen, sha256sum, 0); +#else + /* returns 0 on success, otherwise failure */ + if(mbedtls_sha256_ret(input, inputlen, sha256sum, 0) != 0) + return CURLE_BAD_FUNCTION_ARGUMENT; +#endif + return CURLE_OK; } static void *Curl_mbedtls_get_internals(struct ssl_connect_data *connssl, @@ -1042,11 +1049,9 @@ static void *Curl_mbedtls_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_mbedtls = { { CURLSSLBACKEND_MBEDTLS, "mbedtls" }, /* info */ - 1, /* have_ca_path */ - 0, /* have_certinfo */ - 1, /* have_pinnedpubkey */ - 1, /* have_ssl_ctx */ - 0, /* support_https_proxy */ + SSLSUPP_CA_PATH | + SSLSUPP_PINNEDPUBKEY | + SSLSUPP_SSL_CTX, sizeof(struct ssl_backend_data), diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c index a3ef37a..7cd450c 100644 --- a/Utilities/cmcurl/lib/vtls/nss.c +++ b/Utilities/cmcurl/lib/vtls/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -440,7 +440,17 @@ static CURLcode nss_create_object(struct ssl_connect_data *connssl, PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval)); } - obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE); + /* PK11_CreateManagedGenericObject() was introduced in NSS 3.34 because + * PK11_DestroyGenericObject() does not release resources allocated by + * PK11_CreateGenericObject() early enough. */ + obj = +#ifdef HAVE_PK11_CREATEMANAGEDGENERICOBJECT + PK11_CreateManagedGenericObject +#else + PK11_CreateGenericObject +#endif + (slot, attrs, attr_cnt, PR_FALSE); + PK11_FreeSlot(slot); if(!obj) return result; @@ -2304,7 +2314,7 @@ static CURLcode Curl_nss_md5sum(unsigned char *tmp, /* input */ return CURLE_OK; } -static void Curl_nss_sha256sum(const unsigned char *tmp, /* input */ +static CURLcode Curl_nss_sha256sum(const unsigned char *tmp, /* input */ size_t tmplen, unsigned char *sha256sum, /* output */ size_t sha256len) @@ -2315,6 +2325,8 @@ static void Curl_nss_sha256sum(const unsigned char *tmp, /* input */ PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen)); PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len)); PK11_DestroyContext(SHA256pw, PR_TRUE); + + return CURLE_OK; } static bool Curl_nss_cert_status_request(void) @@ -2345,11 +2357,10 @@ static void *Curl_nss_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_nss = { { CURLSSLBACKEND_NSS, "nss" }, /* info */ - 1, /* have_ca_path */ - 1, /* have_certinfo */ - 1, /* have_pinnedpubkey */ - 0, /* have_ssl_ctx */ - 1, /* support_https_proxy */ + SSLSUPP_CA_PATH | + SSLSUPP_CERTINFO | + SSLSUPP_PINNEDPUBKEY | + SSLSUPP_HTTPS_PROXY, sizeof(struct ssl_backend_data), diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 93faa6f..f6a4bd3 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -104,13 +104,22 @@ #endif #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && /* OpenSSL 1.1.0+ */ \ - !defined(LIBRESSL_VERSION_NUMBER) + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) #define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER #define HAVE_X509_GET0_EXTENSIONS 1 /* added in 1.1.0 -pre1 */ #define HAVE_OPAQUE_EVP_PKEY 1 /* since 1.1.0 -pre3 */ #define HAVE_OPAQUE_RSA_DSA_DH 1 /* since 1.1.0 -pre5 */ #define CONST_EXTS const #define HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED 1 + +/* funny typecast define due to difference in API */ +#ifdef LIBRESSL_VERSION_NUMBER +#define ARG2_X509_signature_print (X509_ALGOR *) +#else +#define ARG2_X509_signature_print +#endif + #else /* For OpenSSL before 1.1.0 */ #define ASN1_STRING_get0_data(x) ASN1_STRING_data(x) @@ -128,7 +137,8 @@ static unsigned long OpenSSL_version_num(void) #endif #if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* 1.0.2 or later */ \ - !defined(LIBRESSL_VERSION_NUMBER) + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) #define HAVE_X509_GET0_SIGNATURE 1 #endif @@ -147,7 +157,7 @@ static unsigned long OpenSSL_version_num(void) * Whether SSL_CTX_set_keylog_callback is available. * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) - * LibreSSL: unsupported in at least 2.5.1 (explicitly check for it since it + * LibreSSL: unsupported in at least 2.7.2 (explicitly check for it since it * lies and pretends to be OpenSSL 2.0.0). */ #if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ @@ -259,7 +269,9 @@ static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state) if(!session || !keylog_file_fp) return; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) /* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that * we have a valid SSL context if we have a non-NULL session. */ SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE); @@ -649,18 +661,28 @@ int cert_stuff(struct connectdata *conn, case SSL_FILETYPE_PKCS12: { - FILE *f; - PKCS12 *p12; + BIO *fp = NULL; + PKCS12 *p12 = NULL; EVP_PKEY *pri; STACK_OF(X509) *ca = NULL; - f = fopen(cert_file, "rb"); - if(!f) { + fp = BIO_new(BIO_s_file()); + if(fp == NULL) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + return 0; + } + + if(BIO_read_filename(fp, cert_file) <= 0) { failf(data, "could not open PKCS12 file '%s'", cert_file); + BIO_free(fp); return 0; } - p12 = d2i_PKCS12_fp(f, NULL); - fclose(f); + p12 = d2i_PKCS12_bio(fp, NULL); + BIO_free(fp); if(!p12) { failf(data, "error reading PKCS12 file '%s'", cert_file); @@ -1311,6 +1333,51 @@ static void Curl_ossl_close_all(struct Curl_easy *data) /* ====================================================== */ +/* + * Match subjectAltName against the host name. This requires a conversion + * in CURL_DOES_CONVERSIONS builds. + */ +static bool subj_alt_hostcheck(struct Curl_easy *data, + const char *match_pattern, const char *hostname, + const char *dispname) +#ifdef CURL_DOES_CONVERSIONS +{ + bool res = FALSE; + + /* Curl_cert_hostcheck uses host encoding, but we get ASCII from + OpenSSl. + */ + char *match_pattern2 = strdup(match_pattern); + + if(match_pattern2) { + if(Curl_convert_from_network(data, match_pattern2, + strlen(match_pattern2)) == CURLE_OK) { + if(Curl_cert_hostcheck(match_pattern2, hostname)) { + res = TRUE; + infof(data, + " subjectAltName: host \"%s\" matched cert's \"%s\"\n", + dispname, match_pattern2); + } + } + free(match_pattern2); + } + else { + failf(data, + "SSL: out of memory when allocating temporary for subjectAltName"); + } + return res; +} +#else +{ + if(Curl_cert_hostcheck(match_pattern, hostname)) { + infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"\n", + dispname, match_pattern); + return TRUE; + } + return FALSE; +} +#endif + /* Quote from RFC2818 section 3.1 "Server Identity" @@ -1410,11 +1477,8 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert) if((altlen == strlen(altptr)) && /* if this isn't true, there was an embedded zero in the name string and we cannot match it. */ - Curl_cert_hostcheck(altptr, hostname)) { + subj_alt_hostcheck(data, altptr, hostname, dispname)) { dnsmatched = TRUE; - infof(data, - " subjectAltName: host \"%s\" matched cert's \"%s\"\n", - dispname, altptr); } break; @@ -1725,13 +1789,40 @@ static const char *ssl_msg_type(int ssl_ver, int msg) case SSL3_MT_CERTIFICATE_STATUS: return "Certificate Status"; #endif +#ifdef SSL3_MT_ENCRYPTED_EXTENSIONS + case SSL3_MT_ENCRYPTED_EXTENSIONS: + return "Encrypted Extensions"; +#endif +#ifdef SSL3_MT_END_OF_EARLY_DATA + case SSL3_MT_END_OF_EARLY_DATA: + return "End of early data"; +#endif +#ifdef SSL3_MT_KEY_UPDATE + case SSL3_MT_KEY_UPDATE: + return "Key update"; +#endif +#ifdef SSL3_MT_NEXT_PROTO + case SSL3_MT_NEXT_PROTO: + return "Next protocol"; +#endif +#ifdef SSL3_MT_MESSAGE_HASH + case SSL3_MT_MESSAGE_HASH: + return "Message hash"; +#endif } } return "Unknown"; } -static const char *tls_rt_type(int type) +static const char *tls_rt_type(int type, const void *buf, size_t buflen) { + (void)buf; + (void)buflen; +#ifdef SSL3_RT_INNER_CONTENT_TYPE + if(type == SSL3_RT_INNER_CONTENT_TYPE && buf && buflen >= 1) + type = *(unsigned char *)buf; +#endif + switch(type) { #ifdef SSL3_RT_HEADER case SSL3_RT_HEADER: @@ -1759,10 +1850,7 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, void *userp) { struct Curl_easy *data; - const char *msg_name, *tls_rt_name; - char ssl_buf[1024]; char unknown[32]; - int msg_type, txt_len; const char *verstr = NULL; struct connectdata *conn = userp; @@ -1810,6 +1898,10 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, } if(ssl_ver) { + const char *msg_name, *tls_rt_name; + char ssl_buf[1024]; + int msg_type, txt_len; + /* the info given when the version is zero is not that useful for us */ ssl_ver >>= 8; /* check the upper 8 bits only below */ @@ -1819,17 +1911,28 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, * is at 'buf[0]'. */ if(ssl_ver == SSL3_VERSION_MAJOR && content_type) - tls_rt_name = tls_rt_type(content_type); + tls_rt_name = tls_rt_type(content_type, buf, len); else tls_rt_name = ""; - msg_type = *(char *)buf; - msg_name = ssl_msg_type(ssl_ver, msg_type); +#ifdef SSL3_RT_INNER_CONTENT_TYPE + if(content_type == SSL3_RT_INNER_CONTENT_TYPE) { + msg_type = 0; + msg_name = "[no content]"; + } + else +#endif + { + msg_type = *(char *)buf; + msg_name = ssl_msg_type(ssl_ver, msg_type); + } txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n", verstr, direction?"OUT":"IN", tls_rt_name, msg_name, msg_type); - Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL); + if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) { + Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL); + } } Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT : @@ -2082,8 +2185,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: /* it will be handled later with the context options */ -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ - !defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) req_method = TLS_client_method(); #else req_method = SSLv23_client_method(); @@ -2800,7 +2902,7 @@ static CURLcode get_cert_chain(struct connectdata *conn, ASN1_STRING *a = ASN1_STRING_new(); if(a) { X509_get0_signature(&psig, &palg, x); - X509_signature_print(mem, palg, a); + X509_signature_print(mem, ARG2_X509_signature_print palg, a); ASN1_STRING_free(a); if(palg) { @@ -3035,7 +3137,8 @@ static CURLcode servercert(struct connectdata *conn, long lerr, len; struct Curl_easy *data = conn->data; X509 *issuer; - FILE *fp; + BIO *fp = NULL; + char error_buffer[256]=""; char buffer[2048]; const char *ptr; long * const certverifyresult = SSL_IS_PROXY() ? @@ -3046,8 +3149,20 @@ static CURLcode servercert(struct connectdata *conn, /* we've been asked to gather certificate info! */ (void)get_cert_chain(conn, connssl); + fp = BIO_new(BIO_s_file()); + if(fp == NULL) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + BIO_free(mem); + return 0; + } + BACKEND->server_cert = SSL_get_peer_certificate(BACKEND->handle); if(!BACKEND->server_cert) { + BIO_free(fp); BIO_free(mem); if(!strict) return CURLE_OK; @@ -3077,6 +3192,7 @@ static CURLcode servercert(struct connectdata *conn, if(SSL_CONN_CONFIG(verifyhost)) { result = verifyhost(conn, BACKEND->server_cert); if(result) { + BIO_free(fp); X509_free(BACKEND->server_cert); BACKEND->server_cert = NULL; return result; @@ -3098,35 +3214,35 @@ static CURLcode servercert(struct connectdata *conn, /* e.g. match issuer name with provided issuer certificate */ if(SSL_SET_OPTION(issuercert)) { - fp = fopen(SSL_SET_OPTION(issuercert), FOPEN_READTEXT); - if(!fp) { + if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) { if(strict) failf(data, "SSL: Unable to open issuer cert (%s)", SSL_SET_OPTION(issuercert)); + BIO_free(fp); X509_free(BACKEND->server_cert); BACKEND->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } - issuer = PEM_read_X509(fp, NULL, ZERO_NULL, NULL); + issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL); if(!issuer) { if(strict) failf(data, "SSL: Unable to read issuer cert (%s)", SSL_SET_OPTION(issuercert)); - X509_free(BACKEND->server_cert); + BIO_free(fp); X509_free(issuer); - fclose(fp); + X509_free(BACKEND->server_cert); + BACKEND->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } - fclose(fp); - if(X509_check_issued(issuer, BACKEND->server_cert) != X509_V_OK) { if(strict) failf(data, "SSL: Certificate issuer check failed (%s)", SSL_SET_OPTION(issuercert)); - X509_free(BACKEND->server_cert); + BIO_free(fp); X509_free(issuer); + X509_free(BACKEND->server_cert); BACKEND->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } @@ -3161,6 +3277,7 @@ static CURLcode servercert(struct connectdata *conn, if(SSL_CONN_CONFIG(verifystatus)) { result = verifystatus(conn, connssl); if(result) { + BIO_free(fp); X509_free(BACKEND->server_cert); BACKEND->server_cert = NULL; return result; @@ -3180,6 +3297,7 @@ static CURLcode servercert(struct connectdata *conn, failf(data, "SSL: public key does not match pinned public key!"); } + BIO_free(fp); X509_free(BACKEND->server_cert); BACKEND->server_cert = NULL; connssl->connecting_state = ssl_connect_done; @@ -3580,25 +3698,34 @@ static CURLcode Curl_ossl_md5sum(unsigned char *tmp, /* input */ unsigned char *md5sum /* output */, size_t unused) { - MD5_CTX MD5pw; - (void)unused; - MD5_Init(&MD5pw); - MD5_Update(&MD5pw, tmp, tmplen); - MD5_Final(md5sum, &MD5pw); + EVP_MD_CTX *mdctx; + unsigned int len = 0; + (void) unused; + + mdctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(mdctx, EVP_md5(), NULL); + EVP_DigestUpdate(mdctx, tmp, tmplen); + EVP_DigestFinal_ex(mdctx, md5sum, &len); + EVP_MD_CTX_destroy(mdctx); return CURLE_OK; } #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) -static void Curl_ossl_sha256sum(const unsigned char *tmp, /* input */ +static CURLcode Curl_ossl_sha256sum(const unsigned char *tmp, /* input */ size_t tmplen, unsigned char *sha256sum /* output */, size_t unused) { - SHA256_CTX SHA256pw; - (void)unused; - SHA256_Init(&SHA256pw); - SHA256_Update(&SHA256pw, tmp, tmplen); - SHA256_Final(sha256sum, &SHA256pw); + EVP_MD_CTX *mdctx; + unsigned int len = 0; + (void) unused; + + mdctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL); + EVP_DigestUpdate(mdctx, tmp, tmplen); + EVP_DigestFinal_ex(mdctx, sha256sum, &len); + EVP_MD_CTX_destroy(mdctx); + return CURLE_OK; } #endif @@ -3623,11 +3750,11 @@ static void *Curl_ossl_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_openssl = { { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */ - 1, /* have_ca_path */ - 1, /* have_certinfo */ - 1, /* have_pinnedpubkey */ - 1, /* have_ssl_ctx */ - 1, /* support_https_proxy */ + SSLSUPP_CA_PATH | + SSLSUPP_CERTINFO | + SSLSUPP_PINNEDPUBKEY | + SSLSUPP_SSL_CTX | + SSLSUPP_HTTPS_PROXY, sizeof(struct ssl_backend_data), diff --git a/Utilities/cmcurl/lib/vtls/polarssl.c b/Utilities/cmcurl/lib/vtls/polarssl.c index df29fa9..604cb4c 100644 --- a/Utilities/cmcurl/lib/vtls/polarssl.c +++ b/Utilities/cmcurl/lib/vtls/polarssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> * * This software is licensed as described in the file COPYING, which @@ -620,12 +620,10 @@ polarssl_connect_step3(struct connectdata *conn, ssl_session *our_ssl_sessionid; void *old_ssl_sessionid = NULL; - our_ssl_sessionid = malloc(sizeof(ssl_session)); + our_ssl_sessionid = calloc(1, sizeof(ssl_session)); if(!our_ssl_sessionid) return CURLE_OUT_OF_MEMORY; - memset(our_ssl_sessionid, 0, sizeof(ssl_session)); - ret = ssl_get_session(&BACKEND->ssl, our_ssl_sessionid); if(ret) { failf(data, "ssl_get_session returned -0x%x", -ret); @@ -882,13 +880,14 @@ static bool Curl_polarssl_data_pending(const struct connectdata *conn, return ssl_get_bytes_avail(&BACKEND->ssl) != 0; } -static void Curl_polarssl_sha256sum(const unsigned char *input, +static CURLcode Curl_polarssl_sha256sum(const unsigned char *input, size_t inputlen, unsigned char *sha256sum, size_t sha256len UNUSED_PARAM) { (void)sha256len; sha256(input, inputlen, sha256sum, 0); + return CURLE_OK; } static void *Curl_polarssl_get_internals(struct ssl_connect_data *connssl, @@ -901,11 +900,8 @@ static void *Curl_polarssl_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_polarssl = { { CURLSSLBACKEND_POLARSSL, "polarssl" }, /* info */ - 1, /* have_ca_path */ - 0, /* have_certinfo */ - 1, /* have_pinnedpubkey */ - 0, /* have_ssl_ctx */ - 0, /* support_https_proxy */ + SSLSUPP_CA_PATH | + SSLSUPP_PINNEDPUBKEY, sizeof(struct ssl_backend_data), diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 85c64cf..2cfd5c1 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -7,7 +7,7 @@ * * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -42,13 +42,12 @@ #ifdef USE_SCHANNEL +#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS + #ifndef USE_WINDOWS_SSPI # error "Can't compile SCHANNEL support without SSPI." #endif -#include <schnlsp.h> -#include <schannel.h> -#include "curl_sspi.h" #include "schannel.h" #include "vtls.h" #include "sendf.h" @@ -61,7 +60,6 @@ #include "x509asn1.h" #include "curl_printf.h" #include "system_win32.h" -#include "hostcheck.h" /* The last #include file should be: */ #include "curl_memory.h" @@ -92,6 +90,12 @@ #endif #endif +#ifdef UNICODE +#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W +#else +#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_A +#endif + #ifndef SP_PROT_SSL2_CLIENT #define SP_PROT_SSL2_CLIENT 0x00000008 #endif @@ -124,50 +128,25 @@ #define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096 #define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024 +#define CERT_THUMBPRINT_STR_LEN 40 +#define CERT_THUMBPRINT_DATA_LEN 20 + /* Uncomment to force verbose output * #define infof(x, y, ...) printf(y, __VA_ARGS__) * #define failf(x, y, ...) printf(y, __VA_ARGS__) */ -/* Structs to store Schannel handles */ -struct curl_schannel_cred { - CredHandle cred_handle; - TimeStamp time_stamp; - int refcount; -}; - -struct curl_schannel_ctxt { - CtxtHandle ctxt_handle; - TimeStamp time_stamp; -}; - -struct ssl_backend_data { - struct curl_schannel_cred *cred; - struct curl_schannel_ctxt *ctxt; - SecPkgContext_StreamSizes stream_sizes; - size_t encdata_length, decdata_length; - size_t encdata_offset, decdata_offset; - unsigned char *encdata_buffer, *decdata_buffer; - /* encdata_is_incomplete: if encdata contains only a partial record that - can't be decrypted without another Curl_read_plain (that is, status is - SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes - more bytes into encdata then set this back to false. */ - bool encdata_is_incomplete; - unsigned long req_flags, ret_flags; - CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ - bool recv_sspi_close_notify; /* true if connection closed by close_notify */ - bool recv_connection_closed; /* true if connection closed, regardless how */ - bool use_alpn; /* true if ALPN is used for this connection */ -}; +#ifndef CALG_SHA_256 +# define CALG_SHA_256 0x0000800c +#endif #define BACKEND connssl->backend static Curl_recv schannel_recv; static Curl_send schannel_send; -#ifdef _WIN32_WCE -static CURLcode verify_certificate(struct connectdata *conn, int sockindex); -#endif +static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, + const char *pinnedpubkey); static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, void *BufDataPtr, unsigned long BufByteSize) @@ -221,6 +200,56 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn) } static CURLcode +get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, + TCHAR **thumbprint) +{ + TCHAR *sep; + size_t store_name_len; + + sep = _tcschr(path, TEXT('\\')); + if(sep == NULL) + return CURLE_SSL_CONNECT_ERROR; + + store_name_len = sep - path; + + if(_tcsnccmp(path, TEXT("CurrentUser"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_CURRENT_USER; + else if(_tcsnccmp(path, TEXT("LocalMachine"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE; + else if(_tcsnccmp(path, TEXT("CurrentService"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE; + else if(_tcsnccmp(path, TEXT("Services"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_SERVICES; + else if(_tcsnccmp(path, TEXT("Users"), store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_USERS; + else if(_tcsnccmp(path, TEXT("CurrentUserGroupPolicy"), + store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; + else if(_tcsnccmp(path, TEXT("LocalMachineGroupPolicy"), + store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; + else if(_tcsnccmp(path, TEXT("LocalMachineEnterprise"), + store_name_len) == 0) + *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; + else + return CURLE_SSL_CONNECT_ERROR; + + *store_path = sep + 1; + + sep = _tcschr(*store_path, TEXT('\\')); + if(sep == NULL) + return CURLE_SSL_CONNECT_ERROR; + + *sep = 0; + + *thumbprint = sep + 1; + if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) + return CURLE_SSL_CONNECT_ERROR; + + return CURLE_OK; +} + +static CURLcode schannel_connect_step1(struct connectdata *conn, int sockindex) { ssize_t written = -1; @@ -234,6 +263,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) unsigned char alpn_buffer[128]; #endif SCHANNEL_CRED schannel_cred; + PCCERT_CONTEXT client_certs[1] = { NULL }; SECURITY_STATUS sspi_status = SEC_E_OK; struct curl_schannel_cred *old_cred = NULL; struct in_addr addr; @@ -268,6 +298,26 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) BACKEND->use_alpn = false; #endif +#ifdef _WIN32_WCE + /* certificate validation on CE doesn't seem to work right; we'll + * do it following a more manual process. */ + BACKEND->use_manual_cred_validation = true; +#else + if(SSL_CONN_CONFIG(CAfile)) { + if(Curl_verify_windows_version(6, 1, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + BACKEND->use_manual_cred_validation = true; + } + else { + failf(data, "schannel: this version of Windows is too old to support " + "certificate verification via CA bundle file."); + return CURLE_SSL_CACERT_BADFILE; + } + } + else + BACKEND->use_manual_cred_validation = false; +#endif + BACKEND->cred = NULL; /* check for an existing re-usable credential handle */ @@ -291,26 +341,23 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; if(conn->ssl_config.verifypeer) { -#ifdef _WIN32_WCE - /* certificate validation on CE doesn't seem to work right; we'll - do it following a more manual process. */ - schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | - SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; -#else - schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION; + if(BACKEND->use_manual_cred_validation) + schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION; + else + schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION; + /* TODO s/data->set.ssl.no_revoke/SSL_SET_OPTION(no_revoke)/g */ - if(data->set.ssl.no_revoke) + if(data->set.ssl.no_revoke) { schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; - else - schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; -#endif - if(data->set.ssl.no_revoke) + SCH_CRED_IGNORE_REVOCATION_OFFLINE; + infof(data, "schannel: disabled server certificate revocation " "checks\n"); - else + } + else { + schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; infof(data, "schannel: checking server certificate revocation\n"); + } } else { schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | @@ -354,14 +401,70 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) return CURLE_SSL_CONNECT_ERROR; } + /* client certificate */ + if(data->set.ssl.cert) { + DWORD cert_store_name; + TCHAR *cert_store_path; + TCHAR *cert_thumbprint_str; + CRYPT_HASH_BLOB cert_thumbprint; + BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; + HCERTSTORE cert_store; + + TCHAR *cert_path = Curl_convert_UTF8_to_tchar(data->set.ssl.cert); + if(!cert_path) + return CURLE_OUT_OF_MEMORY; + + result = get_cert_location(cert_path, &cert_store_name, + &cert_store_path, &cert_thumbprint_str); + if(result != CURLE_OK) { + Curl_unicodefree(cert_path); + return result; + } + + cert_store = CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, + (HCRYPTPROV)NULL, + cert_store_name, cert_store_path); + if(!cert_store) { + Curl_unicodefree(cert_path); + return CURLE_SSL_CONNECT_ERROR; + } + + cert_thumbprint.pbData = cert_thumbprint_data; + cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; + + if(!CryptStringToBinary(cert_thumbprint_str, CERT_THUMBPRINT_STR_LEN, + CRYPT_STRING_HEX, + cert_thumbprint_data, &cert_thumbprint.cbData, + NULL, NULL)) { + Curl_unicodefree(cert_path); + return CURLE_SSL_CONNECT_ERROR; + } + + client_certs[0] = CertFindCertificateInStore( + cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_HASH, &cert_thumbprint, NULL); + + Curl_unicodefree(cert_path); + + if(client_certs[0]) { + schannel_cred.cCreds = 1; + schannel_cred.paCred = client_certs; + } + + CertCloseStore(cert_store, 0); + } + /* allocate memory for the re-usable credential handle */ BACKEND->cred = (struct curl_schannel_cred *) - malloc(sizeof(struct curl_schannel_cred)); + calloc(1, sizeof(struct curl_schannel_cred)); if(!BACKEND->cred) { failf(data, "schannel: unable to allocate memory"); + + if(client_certs[0]) + CertFreeCertificateContext(client_certs[0]); + return CURLE_OUT_OF_MEMORY; } - memset(BACKEND->cred, 0, sizeof(struct curl_schannel_cred)); BACKEND->cred->refcount = 1; /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx @@ -373,6 +476,9 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) &BACKEND->cred->cred_handle, &BACKEND->cred->time_stamp); + if(client_certs[0]) + CertFreeCertificateContext(client_certs[0]); + if(sspi_status != SEC_E_OK) { if(sspi_status == SEC_E_WRONG_PRINCIPAL) failf(data, "schannel: SNI or certificate check failed: %s", @@ -438,8 +544,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur); InitSecBufferDesc(&inbuf_desc, &inbuf, 1); } - else - { + else { InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&inbuf_desc, &inbuf, 1); } @@ -459,12 +564,11 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) /* allocate memory for the security context handle */ BACKEND->ctxt = (struct curl_schannel_ctxt *) - malloc(sizeof(struct curl_schannel_ctxt)); + calloc(1, sizeof(struct curl_schannel_ctxt)); if(!BACKEND->ctxt) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } - memset(BACKEND->ctxt, 0, sizeof(struct curl_schannel_ctxt)); host_name = Curl_convert_UTF8_to_tchar(hostname); if(!host_name) @@ -542,6 +646,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) bool doread; char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; + const char *pubkey_ptr; doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; @@ -761,12 +866,20 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) infof(data, "schannel: SSL/TLS handshake complete\n"); } -#ifdef _WIN32_WCE - /* Windows CE doesn't do any server certificate validation. - We have to do it manually. */ - if(conn->ssl_config.verifypeer) + pubkey_ptr = SSL_IS_PROXY() ? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : + data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; + if(pubkey_ptr) { + result = pkp_pin_peer_pubkey(conn, sockindex, pubkey_ptr); + if(result) { + failf(data, "SSL: public key does not match pinned public key!"); + return result; + } + } + + if(conn->ssl_config.verifypeer && BACKEND->use_manual_cred_validation) { return verify_certificate(conn, sockindex); -#endif + } return CURLE_OK; } @@ -1669,145 +1782,136 @@ static CURLcode Curl_schannel_random(struct Curl_easy *data UNUSED_PARAM, return CURLE_OK; } -#ifdef _WIN32_WCE -static CURLcode verify_certificate(struct connectdata *conn, int sockindex) +static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, + const char *pinnedpubkey) { SECURITY_STATUS status; struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - CURLcode result = CURLE_OK; CERT_CONTEXT *pCertContextServer = NULL; - const CERT_CHAIN_CONTEXT *pChainContext = NULL; - const char * const conn_hostname = SSL_IS_PROXY() ? - conn->http_proxy.host.name : - conn->host.name; + const char *x509_der; + DWORD x509_der_len; + curl_X509certificate x509_parsed; + curl_asn1Element *pubkey; - status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &pCertContextServer); + /* Result is returned to caller */ + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - if((status != SEC_E_OK) || (pCertContextServer == NULL)) { - failf(data, "schannel: Failed to read remote certificate context: %s", - Curl_sspi_strerror(conn, status)); - result = CURLE_PEER_FAILED_VERIFICATION; - } + /* if a path wasn't specified, don't pin */ + if(!pinnedpubkey) + return CURLE_OK; - if(result == CURLE_OK) { - CERT_CHAIN_PARA ChainPara; - memset(&ChainPara, 0, sizeof(ChainPara)); - ChainPara.cbSize = sizeof(ChainPara); - - if(!CertGetCertificateChain(NULL, - pCertContextServer, - NULL, - pCertContextServer->hCertStore, - &ChainPara, - (data->set.ssl.no_revoke ? 0 : - CERT_CHAIN_REVOCATION_CHECK_CHAIN), - NULL, - &pChainContext)) { - failf(data, "schannel: CertGetCertificateChain failed: %s", - Curl_sspi_strerror(conn, GetLastError())); - pChainContext = NULL; - result = CURLE_PEER_FAILED_VERIFICATION; - } + do { + status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &pCertContextServer); - if(result == CURLE_OK) { - CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0]; - DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); - dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; - if(dwTrustErrorMask) { - if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_REVOKED"); - else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_PARTIAL_CHAIN"); - else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_UNTRUSTED_ROOT"); - else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_NOT_TIME_VALID"); - else - failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x", - dwTrustErrorMask); - result = CURLE_PEER_FAILED_VERIFICATION; - } + if((status != SEC_E_OK) || (pCertContextServer == NULL)) { + failf(data, "schannel: Failed to read remote certificate context: %s", + Curl_sspi_strerror(conn, status)); + break; /* failed */ } - } - if(result == CURLE_OK) { - if(conn->ssl_config.verifyhost) { - TCHAR cert_hostname_buff[256]; - DWORD len; - - /* TODO: Fix this for certificates with multiple alternative names. - Right now we're only asking for the first preferred alternative name. - Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG - (if WinCE supports that?) and run this section in a loop for each. - https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx - curl: (51) schannel: CertGetNameString() certificate hostname - (.google.com) did not match connection (google.com) - */ - len = CertGetNameString(pCertContextServer, - CERT_NAME_DNS_TYPE, - CERT_NAME_DISABLE_IE4_UTF8_FLAG, - NULL, - cert_hostname_buff, - 256); - if(len > 0) { - const char *cert_hostname; - - /* Comparing the cert name and the connection hostname encoded as UTF-8 - * is acceptable since both values are assumed to use ASCII - * (or some equivalent) encoding - */ - cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname_buff); - if(!cert_hostname) { - result = CURLE_OUT_OF_MEMORY; - } - else{ - int match_result; - - match_result = Curl_cert_hostcheck(cert_hostname, conn->host.name); - if(match_result == CURL_HOST_MATCH) { - infof(data, - "schannel: connection hostname (%s) validated " - "against certificate name (%s)\n", - conn->host.name, - cert_hostname); - result = CURLE_OK; - } - else{ - failf(data, - "schannel: connection hostname (%s) " - "does not match certificate name (%s)", - conn->host.name, - cert_hostname); - result = CURLE_PEER_FAILED_VERIFICATION; - } - Curl_unicodefree(cert_hostname); - } - } - else { - failf(data, - "schannel: CertGetNameString did not provide any " - "certificate name information"); - result = CURLE_PEER_FAILED_VERIFICATION; - } + + if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && + (pCertContextServer->cbCertEncoded > 0))) + break; + + x509_der = (const char *)pCertContextServer->pbCertEncoded; + x509_der_len = pCertContextServer->cbCertEncoded; + memset(&x509_parsed, 0, sizeof x509_parsed); + if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) + break; + + pubkey = &x509_parsed.subjectPublicKeyInfo; + if(!pubkey->header || pubkey->end <= pubkey->header) { + failf(data, "SSL: failed retrieving public key from server certificate"); + break; } - } - if(pChainContext) - CertFreeCertificateChain(pChainContext); + result = Curl_pin_peer_pubkey(data, + pinnedpubkey, + (const unsigned char *)pubkey->header, + (size_t)(pubkey->end - pubkey->header)); + if(result) { + failf(data, "SSL: public key does not match pinned public key!"); + } + } while(0); if(pCertContextServer) CertFreeCertificateContext(pCertContextServer); return result; } -#endif /* _WIN32_WCE */ + +static void Curl_schannel_checksum(const unsigned char *input, + size_t inputlen, + unsigned char *checksum, + size_t checksumlen, + DWORD provType, + const unsigned int algId) +{ + HCRYPTPROV hProv = 0; + HCRYPTHASH hHash = 0; + DWORD cbHashSize = 0; + DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); + DWORD dwChecksumLen = (DWORD)checksumlen; + + /* since this can fail in multiple ways, zero memory first so we never + * return old data + */ + memset(checksum, 0, checksumlen); + + if(!CryptAcquireContext(&hProv, NULL, NULL, provType, + CRYPT_VERIFYCONTEXT)) + return; /* failed */ + + do { + if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) + break; /* failed */ + + if(!CryptHashData(hHash, (const BYTE*)input, (DWORD)inputlen, 0)) + break; /* failed */ + + /* get hash size */ + if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, + &dwHashSizeLen, 0)) + break; /* failed */ + + /* check hash size */ + if(checksumlen < cbHashSize) + break; /* failed */ + + if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0)) + break; /* failed */ + } while(0); + + if(hHash) + CryptDestroyHash(hHash); + + if(hProv) + CryptReleaseContext(hProv, 0); +} + +static CURLcode Curl_schannel_md5sum(unsigned char *input, + size_t inputlen, + unsigned char *md5sum, + size_t md5len) +{ + Curl_schannel_checksum(input, inputlen, md5sum, md5len, + PROV_RSA_FULL, CALG_MD5); + return CURLE_OK; +} + +static CURLcode Curl_schannel_sha256sum(const unsigned char *input, + size_t inputlen, + unsigned char *sha256sum, + size_t sha256len) +{ + Curl_schannel_checksum(input, inputlen, sha256sum, sha256len, + PROV_RSA_AES, CALG_SHA_256); + return CURLE_OK; +} static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) @@ -1819,11 +1923,8 @@ static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_schannel = { { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ - 0, /* have_ca_path */ - 1, /* have_certinfo */ - 0, /* have_pinnedpubkey */ - 0, /* have_ssl_ctx */ - 0, /* support_https_proxy */ + SSLSUPP_CERTINFO | + SSLSUPP_PINNEDPUBKEY, sizeof(struct ssl_backend_data), @@ -1845,8 +1946,8 @@ const struct Curl_ssl Curl_ssl_schannel = { Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */ Curl_none_false_start, /* false_start */ - Curl_none_md5sum, /* md5sum */ - NULL /* sha256sum */ + Curl_schannel_md5sum, /* md5sum */ + Curl_schannel_sha256sum /* sha256sum */ }; #endif /* USE_SCHANNEL */ diff --git a/Utilities/cmcurl/lib/vtls/schannel.h b/Utilities/cmcurl/lib/vtls/schannel.h index 932103d..4476900 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.h +++ b/Utilities/cmcurl/lib/vtls/schannel.h @@ -26,9 +26,49 @@ #ifdef USE_SCHANNEL +#include <schnlsp.h> +#include <schannel.h> +#include "curl_sspi.h" + #include "urldata.h" extern const struct Curl_ssl Curl_ssl_schannel; +CURLcode verify_certificate(struct connectdata *conn, int sockindex); + +/* structs to expose only in schannel.c and schannel_verify.c */ +#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS +struct curl_schannel_cred { + CredHandle cred_handle; + TimeStamp time_stamp; + int refcount; +}; + +struct curl_schannel_ctxt { + CtxtHandle ctxt_handle; + TimeStamp time_stamp; +}; + +struct ssl_backend_data { + struct curl_schannel_cred *cred; + struct curl_schannel_ctxt *ctxt; + SecPkgContext_StreamSizes stream_sizes; + size_t encdata_length, decdata_length; + size_t encdata_offset, decdata_offset; + unsigned char *encdata_buffer, *decdata_buffer; + /* encdata_is_incomplete: if encdata contains only a partial record that + can't be decrypted without another Curl_read_plain (that is, status is + SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes + more bytes into encdata then set this back to false. */ + bool encdata_is_incomplete; + unsigned long req_flags, ret_flags; + CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ + bool recv_sspi_close_notify; /* true if connection closed by close_notify */ + bool recv_connection_closed; /* true if connection closed, regardless how */ + bool use_alpn; /* true if ALPN is used for this connection */ + bool use_manual_cred_validation; /* true if manual cred validation is used */ +}; +#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */ + #endif /* USE_SCHANNEL */ #endif /* HEADER_CURL_SCHANNEL_H */ diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c new file mode 100644 index 0000000..db187dd --- /dev/null +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c @@ -0,0 +1,551 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> + * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> + * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * Source file for SChannel-specific certificate verification. This code should + * only be invoked by code in schannel.c. + */ + +#include "curl_setup.h" + +#ifdef USE_SCHANNEL + +#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS + +#ifndef USE_WINDOWS_SSPI +# error "Can't compile SCHANNEL support without SSPI." +#endif + +#include "schannel.h" +#include "vtls.h" +#include "sendf.h" +#include "strerror.h" +#include "curl_multibyte.h" +#include "curl_printf.h" +#include "hostcheck.h" +#include "system_win32.h" + +/* The last #include file should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#define BACKEND connssl->backend + +#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ +#define BEGIN_CERT "-----BEGIN CERTIFICATE-----\n" +#define END_CERT "\n-----END CERTIFICATE-----" + +typedef struct { + DWORD cbSize; + HCERTSTORE hRestrictedRoot; + HCERTSTORE hRestrictedTrust; + HCERTSTORE hRestrictedOther; + DWORD cAdditionalStore; + HCERTSTORE *rghAdditionalStore; + DWORD dwFlags; + DWORD dwUrlRetrievalTimeout; + DWORD MaximumCachedCertificates; + DWORD CycleDetectionModulus; + HCERTSTORE hExclusiveRoot; + HCERTSTORE hExclusiveTrustedPeople; +} CERT_CHAIN_ENGINE_CONFIG_WIN7, *PCERT_CHAIN_ENGINE_CONFIG_WIN7; + + +static CURLcode add_certs_to_store(HCERTSTORE trust_store, + const char *ca_file, + struct connectdata *conn) +{ + CURLcode result; + struct Curl_easy *data = conn->data; + HANDLE ca_file_handle = INVALID_HANDLE_VALUE; + LARGE_INTEGER file_size; + char *ca_file_buffer = NULL; + char *current_ca_file_ptr = NULL; + const TCHAR *ca_file_tstr = NULL; + size_t ca_file_bufsize = 0; + DWORD total_bytes_read = 0; + bool more_certs = 0; + int num_certs = 0; + size_t END_CERT_LEN; + + ca_file_tstr = Curl_convert_UTF8_to_tchar(ca_file); + if(!ca_file_tstr) { + failf(data, + "schannel: invalid path name for CA file '%s': %s", + ca_file, Curl_strerror(conn, GetLastError())); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + /* + * Read the CA file completely into memory before parsing it. This + * optimizes for the common case where the CA file will be relatively + * small ( < 1 MiB ). + */ + ca_file_handle = CreateFile(ca_file_tstr, + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if(ca_file_handle == INVALID_HANDLE_VALUE) { + failf(data, + "schannel: failed to open CA file '%s': %s", + ca_file, Curl_strerror(conn, GetLastError())); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + if(!GetFileSizeEx(ca_file_handle, &file_size)) { + failf(data, + "schannel: failed to determine size of CA file '%s': %s", + ca_file, Curl_strerror(conn, GetLastError())); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + if(file_size.QuadPart > MAX_CAFILE_SIZE) { + failf(data, + "schannel: CA file exceeds max size of %u bytes", + MAX_CAFILE_SIZE); + result = CURLE_OUT_OF_MEMORY; + goto cleanup; + } + + ca_file_bufsize = (size_t)file_size.QuadPart; + ca_file_buffer = (char *)malloc(ca_file_bufsize + 1); + if(!ca_file_buffer) { + result = CURLE_OUT_OF_MEMORY; + goto cleanup; + } + + result = CURLE_OK; + while(total_bytes_read < ca_file_bufsize) { + DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read); + DWORD bytes_read = 0; + + if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read, + bytes_to_read, &bytes_read, NULL)) { + + failf(data, + "schannel: failed to read from CA file '%s': %s", + ca_file, Curl_strerror(conn, GetLastError())); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + if(bytes_read == 0) { + /* Premature EOF -- adjust the bufsize to the new value */ + ca_file_bufsize = total_bytes_read; + } + else { + total_bytes_read += bytes_read; + } + } + + /* Null terminate the buffer */ + ca_file_buffer[ca_file_bufsize] = '\0'; + + if(result != CURLE_OK) { + goto cleanup; + } + + END_CERT_LEN = strlen(END_CERT); + + more_certs = 1; + current_ca_file_ptr = ca_file_buffer; + while(more_certs && *current_ca_file_ptr != '\0') { + char *begin_cert_ptr = strstr(current_ca_file_ptr, BEGIN_CERT); + if(!begin_cert_ptr) { + more_certs = 0; + } + else { + char *end_cert_ptr = strstr(begin_cert_ptr, END_CERT); + if(!end_cert_ptr) { + failf(data, + "schannel: CA file '%s' is not correctly formatted", + ca_file); + result = CURLE_SSL_CACERT_BADFILE; + more_certs = 0; + } + else { + CERT_BLOB cert_blob; + CERT_CONTEXT *cert_context = NULL; + BOOL add_cert_result = FALSE; + DWORD actual_content_type = 0; + DWORD cert_size = (DWORD) + ((end_cert_ptr + END_CERT_LEN) - begin_cert_ptr); + + cert_blob.pbData = (BYTE *)begin_cert_ptr; + cert_blob.cbData = cert_size; + if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, + &cert_blob, + CERT_QUERY_CONTENT_FLAG_CERT, + CERT_QUERY_FORMAT_FLAG_ALL, + 0, + NULL, + &actual_content_type, + NULL, + NULL, + NULL, + &cert_context)) { + + failf(data, + "schannel: failed to extract certificate from CA file " + "'%s': %s", + ca_file, Curl_strerror(conn, GetLastError())); + result = CURLE_SSL_CACERT_BADFILE; + more_certs = 0; + } + else { + current_ca_file_ptr = begin_cert_ptr + cert_size; + + /* Sanity check that the cert_context object is the right type */ + if(CERT_QUERY_CONTENT_CERT != actual_content_type) { + failf(data, + "schannel: unexpected content type '%d' when extracting " + "certificate from CA file '%s'", + actual_content_type, ca_file); + result = CURLE_SSL_CACERT_BADFILE; + more_certs = 0; + } + else { + add_cert_result = + CertAddCertificateContextToStore(trust_store, + cert_context, + CERT_STORE_ADD_ALWAYS, + NULL); + CertFreeCertificateContext(cert_context); + if(!add_cert_result) { + failf(data, + "schannel: failed to add certificate from CA file '%s'" + "to certificate store: %s", + ca_file, Curl_strerror(conn, GetLastError())); + result = CURLE_SSL_CACERT_BADFILE; + more_certs = 0; + } + else { + num_certs++; + } + } + } + } + } + } + + if(result == CURLE_OK) { + if(!num_certs) { + infof(data, + "schannel: did not add any certificates from CA file '%s'\n", + ca_file); + } + else { + infof(data, + "schannel: added %d certificate(s) from CA file '%s'\n", + num_certs, ca_file); + } + } + +cleanup: + if(ca_file_handle != INVALID_HANDLE_VALUE) { + CloseHandle(ca_file_handle); + } + Curl_safefree(ca_file_buffer); + Curl_unicodefree(ca_file_tstr); + + return result; +} + +static CURLcode verify_host(struct Curl_easy *data, + CERT_CONTEXT *pCertContextServer, + const char * const conn_hostname) +{ + CURLcode result = CURLE_PEER_FAILED_VERIFICATION; + TCHAR *cert_hostname_buff = NULL; + size_t cert_hostname_buff_index = 0; + DWORD len = 0; + DWORD actual_len = 0; + + /* CertGetNameString will provide the 8-bit character string without + * any decoding */ + DWORD name_flags = CERT_NAME_DISABLE_IE4_UTF8_FLAG; + +#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG + name_flags |= CERT_NAME_SEARCH_ALL_NAMES_FLAG; +#endif + + /* Determine the size of the string needed for the cert hostname */ + len = CertGetNameString(pCertContextServer, + CERT_NAME_DNS_TYPE, + name_flags, + NULL, + NULL, + 0); + if(len == 0) { + failf(data, + "schannel: CertGetNameString() returned no " + "certificate name information"); + result = CURLE_PEER_FAILED_VERIFICATION; + goto cleanup; + } + + /* CertGetNameString guarantees that the returned name will not contain + * embedded null bytes. This appears to be undocumented behavior. + */ + cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); + actual_len = CertGetNameString(pCertContextServer, + CERT_NAME_DNS_TYPE, + name_flags, + NULL, + (LPTSTR) cert_hostname_buff, + len); + + /* Sanity check */ + if(actual_len != len) { + failf(data, + "schannel: CertGetNameString() returned certificate " + "name information of unexpected size"); + result = CURLE_PEER_FAILED_VERIFICATION; + goto cleanup; + } + + /* If HAVE_CERT_NAME_SEARCH_ALL_NAMES is available, the output + * will contain all DNS names, where each name is null-terminated + * and the last DNS name is double null-terminated. Due to this + * encoding, use the length of the buffer to iterate over all names. + */ + result = CURLE_PEER_FAILED_VERIFICATION; + while(cert_hostname_buff_index < len && + cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') && + result == CURLE_PEER_FAILED_VERIFICATION) { + + char *cert_hostname; + + /* Comparing the cert name and the connection hostname encoded as UTF-8 + * is acceptable since both values are assumed to use ASCII + * (or some equivalent) encoding + */ + cert_hostname = Curl_convert_tchar_to_UTF8( + &cert_hostname_buff[cert_hostname_buff_index]); + if(!cert_hostname) { + result = CURLE_OUT_OF_MEMORY; + } + else { + int match_result; + + match_result = Curl_cert_hostcheck(cert_hostname, conn_hostname); + if(match_result == CURL_HOST_MATCH) { + infof(data, + "schannel: connection hostname (%s) validated " + "against certificate name (%s)\n", + conn_hostname, cert_hostname); + result = CURLE_OK; + } + else { + size_t cert_hostname_len; + + infof(data, + "schannel: connection hostname (%s) did not match " + "against certificate name (%s)\n", + conn_hostname, cert_hostname); + + cert_hostname_len = _tcslen( + &cert_hostname_buff[cert_hostname_buff_index]); + + /* Move on to next cert name */ + cert_hostname_buff_index += cert_hostname_len + 1; + + result = CURLE_PEER_FAILED_VERIFICATION; + } + Curl_unicodefree(cert_hostname); + } + } + + if(result == CURLE_PEER_FAILED_VERIFICATION) { + failf(data, + "schannel: CertGetNameString() failed to match " + "connection hostname (%s) against server certificate names", + conn_hostname); + } + else if(result != CURLE_OK) + failf(data, "schannel: server certificate name verification failed"); + +cleanup: + Curl_unicodefree(cert_hostname_buff); + + return result; +} + +CURLcode verify_certificate(struct connectdata *conn, int sockindex) +{ + SECURITY_STATUS status; + struct Curl_easy *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + CURLcode result = CURLE_OK; + CERT_CONTEXT *pCertContextServer = NULL; + const CERT_CHAIN_CONTEXT *pChainContext = NULL; + HCERTCHAINENGINE cert_chain_engine = NULL; + HCERTSTORE trust_store = NULL; + const char * const conn_hostname = SSL_IS_PROXY() ? + conn->http_proxy.host.name : + conn->host.name; + + status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &pCertContextServer); + + if((status != SEC_E_OK) || (pCertContextServer == NULL)) { + failf(data, "schannel: Failed to read remote certificate context: %s", + Curl_sspi_strerror(conn, status)); + result = CURLE_PEER_FAILED_VERIFICATION; + } + + if(result == CURLE_OK && SSL_CONN_CONFIG(CAfile) && + BACKEND->use_manual_cred_validation) { + /* + * Create a chain engine that uses the certificates in the CA file as + * trusted certificates. This is only supported on Windows 7+. + */ + + if(Curl_verify_windows_version(6, 1, PLATFORM_WINNT, VERSION_LESS_THAN)) { + failf(data, "schannel: this version of Windows is too old to support " + "certificate verification via CA bundle file."); + result = CURLE_SSL_CACERT_BADFILE; + } + else { + /* Open the certificate store */ + trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY, + 0, + (HCRYPTPROV)NULL, + CERT_STORE_CREATE_NEW_FLAG, + NULL); + if(!trust_store) { + failf(data, "schannel: failed to create certificate store: %s", + Curl_strerror(conn, GetLastError())); + result = CURLE_SSL_CACERT_BADFILE; + } + else { + result = add_certs_to_store(trust_store, SSL_CONN_CONFIG(CAfile), + conn); + } + } + + if(result == CURLE_OK) { + CERT_CHAIN_ENGINE_CONFIG_WIN7 engine_config; + BOOL create_engine_result; + + memset(&engine_config, 0, sizeof(engine_config)); + engine_config.cbSize = sizeof(engine_config); + engine_config.hExclusiveRoot = trust_store; + + /* CertCreateCertificateChainEngine will check the expected size of the + * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size + * does not match the expected size. When this occurs, it indicates that + * CAINFO is not supported on the version of Windows in use. + */ + create_engine_result = + CertCreateCertificateChainEngine( + (CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine); + if(!create_engine_result) { + failf(data, + "schannel: failed to create certificate chain engine: %s", + Curl_strerror(conn, GetLastError())); + result = CURLE_SSL_CACERT_BADFILE; + } + } + } + + if(result == CURLE_OK) { + CERT_CHAIN_PARA ChainPara; + + memset(&ChainPara, 0, sizeof(ChainPara)); + ChainPara.cbSize = sizeof(ChainPara); + + if(!CertGetCertificateChain(cert_chain_engine, + pCertContextServer, + NULL, + pCertContextServer->hCertStore, + &ChainPara, + (data->set.ssl.no_revoke ? 0 : + CERT_CHAIN_REVOCATION_CHECK_CHAIN), + NULL, + &pChainContext)) { + failf(data, "schannel: CertGetCertificateChain failed: %s", + Curl_sspi_strerror(conn, GetLastError())); + pChainContext = NULL; + result = CURLE_PEER_FAILED_VERIFICATION; + } + + if(result == CURLE_OK) { + CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0]; + DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); + dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; + if(dwTrustErrorMask) { + if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_IS_REVOKED"); + else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_IS_PARTIAL_CHAIN"); + else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_IS_UNTRUSTED_ROOT"); + else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_IS_NOT_TIME_VALID"); + else if(dwTrustErrorMask & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_REVOCATION_STATUS_UNKNOWN"); + else + failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x", + dwTrustErrorMask); + result = CURLE_PEER_FAILED_VERIFICATION; + } + } + } + + if(result == CURLE_OK) { + if(SSL_CONN_CONFIG(verifyhost)) { + result = verify_host(conn->data, pCertContextServer, conn_hostname); + } + } + + if(cert_chain_engine) { + CertFreeCertificateChainEngine(cert_chain_engine); + } + + if(trust_store) { + CertCloseStore(trust_store, 0); + } + + if(pChainContext) + CertFreeCertificateChain(pChainContext); + + if(pCertContextServer) + CertFreeCertificateContext(pCertContextServer); + + return result; +} + +#endif /* USE_SCHANNEL */ diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index def1d30..ee5bc7a 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -211,7 +211,7 @@ ssl_connect_init_proxy(struct connectdata *conn, int sockindex) !conn->proxy_ssl[sockindex].use) { struct ssl_backend_data *pbdata; - if(!Curl_ssl->support_https_proxy) + if(!(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY)) return CURLE_NOT_BUILT_IN; /* The pointers to the ssl backend data, which is opaque here, are swapped @@ -511,7 +511,7 @@ void Curl_ssl_close_all(struct Curl_easy *data) #if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \ defined(USE_DARWINSSL) || defined(USE_POLARSSL) || defined(USE_NSS) || \ - defined(USE_MBEDTLS) + defined(USE_MBEDTLS) || defined(USE_CYASSL) int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks) { @@ -831,8 +831,12 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, sha256sumdigest = malloc(CURL_SHA256_DIGEST_LENGTH); if(!sha256sumdigest) return CURLE_OUT_OF_MEMORY; - Curl_ssl->sha256sum(pubkey, pubkeylen, + encode = Curl_ssl->sha256sum(pubkey, pubkeylen, sha256sumdigest, CURL_SHA256_DIGEST_LENGTH); + + if(encode != CURLE_OK) + return encode; + encode = Curl_base64_encode(data, (char *)sha256sumdigest, CURL_SHA256_DIGEST_LENGTH, &encoded, &encodedlen); @@ -1127,13 +1131,7 @@ static void Curl_multissl_close(struct connectdata *conn, int sockindex) static const struct Curl_ssl Curl_ssl_multi = { { CURLSSLBACKEND_NONE, "multi" }, /* info */ - - 0, /* have_ca_path */ - 0, /* have_certinfo */ - 0, /* have_pinnedpubkey */ - 0, /* have_ssl_ctx */ - 0, /* support_https_proxy */ - + 0, /* supports nothing */ (size_t)-1, /* something insanely large to be on the safe side */ Curl_multissl_init, /* init */ @@ -1300,6 +1298,9 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, { int i; + if(avail) + *avail = (const curl_ssl_backend **)&available_backends; + if(Curl_ssl != &Curl_ssl_multi) return id == Curl_ssl->info.id ? CURLSSLSET_OK : CURLSSLSET_TOO_LATE; @@ -1311,8 +1312,6 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, } } - if(avail) - *avail = (const curl_ssl_backend **)&available_backends; return CURLSSLSET_UNKNOWN_BACKEND; } diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index c5f9d4a..e7b87c4 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,20 +26,19 @@ struct connectdata; struct ssl_connect_data; +#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */ +#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */ +#define SSLSUPP_PINNEDPUBKEY (1<<2) /* supports CURLOPT_PINNEDPUBLICKEY */ +#define SSLSUPP_SSL_CTX (1<<3) /* supports CURLOPT_SSL_CTX */ +#define SSLSUPP_HTTPS_PROXY (1<<4) /* supports access via HTTPS proxies */ + struct Curl_ssl { /* * This *must* be the first entry to allow returning the list of available * backends in curl_global_sslset(). */ curl_ssl_backend info; - - unsigned have_ca_path:1; /* supports CAPATH */ - unsigned have_certinfo:1; /* supports CURLOPT_CERTINFO */ - unsigned have_pinnedpubkey:1; /* supports CURLOPT_PINNEDPUBLICKEY */ - unsigned have_ssl_ctx:1; /* supports CURLOPT_SSL_CTX_* */ - - unsigned support_https_proxy:1; /* supports access via HTTPS proxies */ - + unsigned int supports; /* bitfield, see above */ size_t sizeof_ssl_backend_data; int (*init)(void); @@ -72,7 +71,7 @@ struct Curl_ssl { CURLcode (*md5sum)(unsigned char *input, size_t inputlen, unsigned char *md5sum, size_t md5sumlen); - void (*sha256sum)(const unsigned char *input, size_t inputlen, + CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, unsigned char *sha256sum, size_t sha256sumlen); }; @@ -113,8 +112,10 @@ CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen, #endif #ifndef MD5_DIGEST_LENGTH +#ifndef LIBWOLFSSL_VERSION_HEX /* because WolfSSL borks this */ #define MD5_DIGEST_LENGTH 16 /* fixed size */ #endif +#endif #ifndef CURL_SHA256_DIGEST_LENGTH #define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ |