From 8beff4355956e3d18ceb3afc21c1f3edec82543c Mon Sep 17 00:00:00 2001 From: Wyatt O'Day Date: Fri, 22 Jul 2022 10:45:28 -0400 Subject: [PATCH] schannel: Add TLS 1.3 support - Support TLS 1.3 as the default max TLS version for Windows Server 2022 and Windows 11. - Support specifying TLS 1.3 ciphers via existing option CURLOPT_TLS13_CIPHERS (tool: --tls13-ciphers). Closes https://github.com/curl/curl/pull/8419 --- docs/CIPHERS.md | 13 +- .../opts/CURLOPT_PROXY_TLS13_CIPHERS.3 | 2 +- docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 | 11 +- lib/vtls/schannel.c | 405 +++++++++++++++--- lib/vtls/schannel.h | 67 +++ 5 files changed, 434 insertions(+), 64 deletions(-) diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index e1df7642cf..ff35ff2835 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -6,7 +6,8 @@ and [`--ciphers`](https://curl.se/docs/manpage.html#--ciphers) users can control which ciphers to consider when negotiating TLS connections. -TLS 1.3 ciphers are supported since curl 7.61 for OpenSSL 1.1.1+ with options +TLS 1.3 ciphers are supported since curl 7.61 for OpenSSL 1.1.1+, and since +curl 7.85 for SChannel with options [`CURLOPT_TLS13_CIPHERS`](https://curl.se/libcurl/c/CURLOPT_TLS13_CIPHERS.html) and [`--tls13-ciphers`](https://curl.se/docs/manpage.html#--tls13-ciphers) @@ -521,6 +522,16 @@ documentation](https://docs.microsoft.com/en-us/windows/win32/secauthn/tls-ciphe Note that the supported ciphers in this case follow the OS version, so if you are running an outdated OS you might still be supporting weak ciphers. +### TLS 1.3 cipher suites + +(Note these ciphers are set with `CURLOPT_TLS13_CIPHERS` and `--tls13-ciphers`) + +`TLS_AES_256_GCM_SHA384` +`TLS_AES_128_GCM_SHA256` +`TLS_CHACHA20_POLY1305_SHA256` +`TLS_AES_128_CCM_8_SHA256` +`TLS_AES_128_CCM_SHA256` + ## BearSSL BearSSL ciphers can be specified by either the OpenSSL name (`ECDHE-RSA-AES128-GCM-SHA256`) or the IANA name (`TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`). diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.3 b/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.3 index fc41b0d11d..5835d3413e 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.3 +++ b/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.3 @@ -58,7 +58,7 @@ CURL *curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_PROXY_TLS13_CIPHERS, - "TLS13-CHACHA20-POLY1305-SHA256"); + "TLS_CHACHA20_POLY1305_SHA256"); ret = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 b/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 index 2349d4e17c..5521a1ebb1 100644 --- a/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 +++ b/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 @@ -41,8 +41,8 @@ you will find more details about cipher lists on this URL: https://curl.se/docs/ssl-ciphers.html This option is currently used only when curl is built to use OpenSSL 1.1.1 or -later. If you are using a different SSL backend you can try setting TLS 1.3 -cipher suites by using the CURLOPT_SSL_CIPHER_LIST option. +later or SChannel. If you are using a different SSL backend you can try +setting TLS 1.3 cipher suites by using the CURLOPT_SSL_CIPHER_LIST option. The application does not have to keep the string around after setting this option. @@ -56,14 +56,15 @@ CURL *curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_TLS13_CIPHERS, - "TLS13-CHACHA20-POLY1305-SHA256"); + "TLS_CHACHA20_POLY1305_SHA256"); ret = curl_easy_perform(curl); curl_easy_cleanup(curl); } .fi .SH AVAILABILITY -Added in 7.61.0. -Available when built with OpenSSL >= 1.1.1. +Added in 7.61.0 for OpenSSL. Available when built with OpenSSL >= 1.1.1. + +Added in 7.85.0 for SChannel. .SH RETURN VALUE Returns CURLE_OK if supported, CURLE_NOT_BUILT_IN otherwise. .SH "SEE ALSO" diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index cfeec002cb..c34a68ccbe 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -84,7 +84,39 @@ #endif #endif -#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) +#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM +#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305" +#endif + +#ifndef BCRYPT_CHAIN_MODE_CCM +#define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM" +#endif + +#ifndef BCRYPT_CHAIN_MODE_GCM +#define BCRYPT_CHAIN_MODE_GCM L"ChainingModeGCM" +#endif + +#ifndef BCRYPT_AES_ALGORITHM +#define BCRYPT_AES_ALGORITHM L"AES" +#endif + +#ifndef BCRYPT_SHA256_ALGORITHM +#define BCRYPT_SHA256_ALGORITHM L"SHA256" +#endif + +#ifndef BCRYPT_SHA384_ALGORITHM +#define BCRYPT_SHA384_ALGORITHM L"SHA384" +#endif + +/* Workaround broken compilers like MingW. +Return the number of elements in a statically sized array. +*/ +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) \ + && !defined(DISABLE_SCHANNEL_CLIENT_CERT) #define HAS_CLIENT_CERT_PATH #endif @@ -120,6 +152,10 @@ #define SP_PROT_TLS1_2_CLIENT 0x00000800 #endif +#ifndef SP_PROT_TLS1_3_CLIENT +#define SP_PROT_TLS1_3_CLIENT 0x00002000 +#endif + #ifndef SCH_USE_STRONG_CRYPTO #define SCH_USE_STRONG_CRYPTO 0x00400000 #endif @@ -174,7 +210,7 @@ static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr, } static CURLcode -set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct Curl_easy *data, +set_ssl_version_min_max(DWORD *enabled_protocols, struct Curl_easy *data, struct connectdata *conn) { long ssl_version = SSL_CONN_CONFIG(version); @@ -184,23 +220,45 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct Curl_easy *data, switch(ssl_version_max) { case CURL_SSLVERSION_MAX_NONE: case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; + + /* Windows Server 2022 and newer (including Windows 11) + support TLS 1.3 built-in. Previous builds of Windows 10 + had broken TLS 1.3 implementations that could be enabled + via registry. + */ + if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; + } + else /* Windows 10 and older */ + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; + break; } + for(; i <= (ssl_version_max >> 16); ++i) { switch(i) { case CURL_SSLVERSION_TLSv1_0: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT; + (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT; break; case CURL_SSLVERSION_TLSv1_1: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; + (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT; break; case CURL_SSLVERSION_TLSv1_2: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; + (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT; break; case CURL_SSLVERSION_TLSv1_3: - failf(data, "schannel: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; + + /* Windows Server 2022 and newer */ + if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT; + break; + } + else { /* Windows 10 and older */ + failf(data, "schannel: TLS 1.3 not supported on Windows prior to 11"); + return CURLE_SSL_CONNECT_ERROR; + } } } return CURLE_OK; @@ -217,8 +275,12 @@ get_alg_id_by_name(char *name) { char tmp[LONGEST_ALG_ID] = { 0 }; char *nameEnd = strchr(name, ':'); - size_t n = nameEnd ? min((size_t)(nameEnd - name), LONGEST_ALG_ID - 1) : \ - min(strlen(name), LONGEST_ALG_ID - 1); + size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name); + + /* reject too-long alg names */ + if(n > (LONGEST_ALG_ID - 1)) + return 0; + strncpy(tmp, name, n); tmp[n] = 0; CIPHEROPTION(CALG_MD2); @@ -422,49 +484,51 @@ schannel_acquire_credential_handle(struct Curl_easy *data, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SCHANNEL_CRED schannel_cred; - ALG_ID algIds[NUM_CIPHERS]; + +#ifdef HAS_CLIENT_CERT_PATH PCCERT_CONTEXT client_certs[1] = { NULL }; +#endif SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; + + /* setup Schannel API options */ + DWORD flags = 0; + DWORD enabled_protocols = 0; + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(backend); - /* setup Schannel API options */ - memset(&schannel_cred, 0, sizeof(schannel_cred)); - schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; - if(conn->ssl_config.verifypeer) { #ifdef HAS_MANUAL_VERIFY_API if(backend->use_manual_cred_validation) - schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION; + flags = SCH_CRED_MANUAL_CRED_VALIDATION; else #endif - schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION; + flags = SCH_CRED_AUTO_CRED_VALIDATION; if(SSL_SET_OPTION(no_revoke)) { - schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE; DEBUGF(infof(data, "schannel: disabled server certificate revocation " "checks")); } else if(SSL_SET_OPTION(revoke_best_effort)) { - schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN; DEBUGF(infof(data, "schannel: ignore revocation offline errors")); } else { - schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; + flags |= SCH_CRED_REVOCATION_CHECK_CHAIN; DEBUGF(infof(data, "schannel: checking server certificate revocation")); } } else { - schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | + flags = SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE; DEBUGF(infof(data, @@ -472,15 +536,15 @@ schannel_acquire_credential_handle(struct Curl_easy *data, } if(!conn->ssl_config.verifyhost) { - schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; + flags |= SCH_CRED_NO_SERVERNAME_CHECK; DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from " "comparing the supplied target name with the subject " "names in server certificates.")); } if(!SSL_SET_OPTION(auto_client_cert)) { - schannel_cred.dwFlags &= ~SCH_CRED_USE_DEFAULT_CREDS; - schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; + flags &= ~SCH_CRED_USE_DEFAULT_CREDS; + flags |= SCH_CRED_NO_DEFAULT_CREDS; infof(data, "schannel: disabled automatic use of client certificate"); } else @@ -494,7 +558,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data, case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: { - result = set_ssl_version_min_max(&schannel_cred, data, conn); + result = set_ssl_version_min_max(&enabled_protocols, data, conn); if(result != CURLE_OK) return result; break; @@ -508,16 +572,6 @@ schannel_acquire_credential_handle(struct Curl_easy *data, return CURLE_SSL_CONNECT_ERROR; } - if(SSL_CONN_CONFIG(cipher_list)) { - result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list), - algIds); - if(CURLE_OK != result) { - failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG"); - return result; - } - } - - #ifdef HAS_CLIENT_CERT_PATH /* client certificate */ if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { @@ -651,9 +705,6 @@ schannel_acquire_credential_handle(struct Curl_easy *data, CertCloseStore(cert_store, 0); return CURLE_SSL_CERTPROBLEM; } - - schannel_cred.cCreds = 1; - schannel_cred.paCred = client_certs; } else { cert_store = @@ -691,11 +742,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data, curlx_unicodefree(cert_path); - if(client_certs[0]) { - schannel_cred.cCreds = 1; - schannel_cred.paCred = client_certs; - } - else { + if(!client_certs[0]) { /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ CertCloseStore(cert_store, 0); return CURLE_SSL_CERTPROBLEM; @@ -716,22 +763,270 @@ schannel_acquire_credential_handle(struct Curl_easy *data, if(!backend->cred) { failf(data, "schannel: unable to allocate memory"); +#ifdef HAS_CLIENT_CERT_PATH if(client_certs[0]) CertFreeCertificateContext(client_certs[0]); +#endif return CURLE_OUT_OF_MEMORY; } backend->cred->refcount = 1; - sspi_status = - s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, - SECPKG_CRED_OUTBOUND, NULL, - &schannel_cred, NULL, NULL, - &backend->cred->cred_handle, - &backend->cred->time_stamp); + /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */ + if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + + char *ciphers13 = 0; + + bool disable_aes_gcm_sha384 = FALSE; + bool disable_aes_gcm_sha256 = FALSE; + bool disable_chacha_poly = FALSE; + bool disable_aes_ccm_8_sha256 = FALSE; + bool disable_aes_ccm_sha256 = FALSE; + + SCH_CREDENTIALS credentials = { 0 }; + TLS_PARAMETERS tls_parameters = { 0 }; + CRYPTO_SETTINGS crypto_settings[4] = { 0 }; + UNICODE_STRING blocked_ccm_modes[1] = { 0 }; + UNICODE_STRING blocked_gcm_modes[1] = { 0 }; + + int crypto_settings_idx = 0; + + + /* If TLS 1.3 ciphers are explictly listed, then + * disable all the ciphers and re-enable which + * ciphers the user has provided. + */ + ciphers13 = SSL_CONN_CONFIG(cipher_list13); + if(ciphers13) { + const int remaining_ciphers = 5; + + /* detect which remaining ciphers to enable + and then disable everything else. + */ + + char *startCur = ciphers13; + int algCount = 0; + char tmp[LONGEST_ALG_ID] = { 0 }; + char *nameEnd; + size_t n; + + disable_aes_gcm_sha384 = TRUE; + disable_aes_gcm_sha256 = TRUE; + disable_chacha_poly = TRUE; + disable_aes_ccm_8_sha256 = TRUE; + disable_aes_ccm_sha256 = TRUE; + + while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) { + nameEnd = strchr(startCur, ':'); + n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur); + + /* reject too-long cipher names */ + if(n > (LONGEST_ALG_ID - 1)) { + failf(data, "Cipher name too long, not checked."); + return CURLE_SSL_CIPHER; + } + + strncpy(tmp, startCur, n); + tmp[n] = 0; + + if(disable_aes_gcm_sha384 + && !strcmp("TLS_AES_256_GCM_SHA384", tmp)) { + disable_aes_gcm_sha384 = FALSE; + } + else if(disable_aes_gcm_sha256 + && !strcmp("TLS_AES_128_GCM_SHA256", tmp)) { + disable_aes_gcm_sha256 = FALSE; + } + else if(disable_chacha_poly + && !strcmp("TLS_CHACHA20_POLY1305_SHA256", tmp)) { + disable_chacha_poly = FALSE; + } + else if(disable_aes_ccm_8_sha256 + && !strcmp("TLS_AES_128_CCM_8_SHA256", tmp)) { + disable_aes_ccm_8_sha256 = FALSE; + } + else if(disable_aes_ccm_sha256 + && !strcmp("TLS_AES_128_CCM_SHA256", tmp)) { + disable_aes_ccm_sha256 = FALSE; + } + else { + failf(data, "Passed in an unknown TLS 1.3 cipher."); + return CURLE_SSL_CIPHER; + } + + startCur = nameEnd; + if(startCur) + startCur++; + + algCount++; + } + } + + if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256 + && disable_chacha_poly && disable_aes_ccm_8_sha256 + && disable_aes_ccm_sha256) { + failf(data, "All available TLS 1.3 ciphers were disabled."); + return CURLE_SSL_CIPHER; + } + + /* Disable TLS_AES_128_CCM_8_SHA256 and/or TLS_AES_128_CCM_SHA256 */ + if(disable_aes_ccm_8_sha256 || disable_aes_ccm_sha256) { + /* + Disallow AES_CCM algorithm. + */ + blocked_ccm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_CCM); + blocked_ccm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_CCM); + blocked_ccm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_CCM; + + crypto_settings[crypto_settings_idx].eAlgorithmUsage = + TlsParametersCngAlgUsageCipher; + crypto_settings[crypto_settings_idx].rgstrChainingModes = + blocked_ccm_modes; + crypto_settings[crypto_settings_idx].cChainingModes = + ARRAYSIZE(blocked_ccm_modes); + crypto_settings[crypto_settings_idx].strCngAlgId.Length = + sizeof(BCRYPT_AES_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = + sizeof(BCRYPT_AES_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = + (PWSTR)BCRYPT_AES_ALGORITHM; + + /* only disabling one of the CCM modes */ + if(disable_aes_ccm_8_sha256 != disable_aes_ccm_sha256) { + if(disable_aes_ccm_8_sha256) + crypto_settings[crypto_settings_idx].dwMinBitLength = 128; + else /* disable_aes_ccm_sha256 */ + crypto_settings[crypto_settings_idx].dwMaxBitLength = 64; + } + + crypto_settings_idx++; + } + + /* Disable TLS_AES_256_GCM_SHA384 and/or TLS_AES_128_GCM_SHA256 */ + if(disable_aes_gcm_sha384 || disable_aes_gcm_sha256) { + + /* + Disallow AES_GCM algorithm + */ + blocked_gcm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_GCM); + blocked_gcm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_GCM); + blocked_gcm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_GCM; + + /* if only one is disabled, then explictly disable the + digest cipher suite (sha384 or sha256) */ + if(disable_aes_gcm_sha384 != disable_aes_gcm_sha256) { + crypto_settings[crypto_settings_idx].eAlgorithmUsage = + TlsParametersCngAlgUsageDigest; + crypto_settings[crypto_settings_idx].strCngAlgId.Length = + sizeof(disable_aes_gcm_sha384 ? + BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = + sizeof(disable_aes_gcm_sha384 ? + BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = + (PWSTR)(disable_aes_gcm_sha384 ? + BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM); + } + else { /* Disable both AES_GCM ciphers */ + crypto_settings[crypto_settings_idx].eAlgorithmUsage = + TlsParametersCngAlgUsageCipher; + crypto_settings[crypto_settings_idx].strCngAlgId.Length = + sizeof(BCRYPT_AES_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = + sizeof(BCRYPT_AES_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = + (PWSTR)BCRYPT_AES_ALGORITHM; + } + + crypto_settings[crypto_settings_idx].rgstrChainingModes = + blocked_gcm_modes; + crypto_settings[crypto_settings_idx].cChainingModes = 1; + + crypto_settings_idx++; + } + + /* + Disable ChaCha20-Poly1305. + */ + if(disable_chacha_poly) { + crypto_settings[crypto_settings_idx].eAlgorithmUsage = + TlsParametersCngAlgUsageCipher; + crypto_settings[crypto_settings_idx].strCngAlgId.Length = + sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = + sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM); + crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = + (PWSTR)BCRYPT_CHACHA20_POLY1305_ALGORITHM; + crypto_settings_idx++; + } + + tls_parameters.pDisabledCrypto = crypto_settings; + + /* The number of blocked suites */ + tls_parameters.cDisabledCrypto = crypto_settings_idx; + credentials.pTlsParameters = &tls_parameters; + credentials.cTlsParameters = 1; + + credentials.dwVersion = SCH_CREDENTIALS_VERSION; + credentials.dwFlags = flags | SCH_USE_STRONG_CRYPTO; + + credentials.pTlsParameters->grbitDisabledProtocols = + (DWORD)~enabled_protocols; + +#ifdef HAS_CLIENT_CERT_PATH + if(client_certs[0]) { + credentials.cCreds = 1; + credentials.paCred = client_certs; + } +#endif + + sspi_status = + s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, + SECPKG_CRED_OUTBOUND, NULL, + &credentials, NULL, NULL, + &backend->cred->cred_handle, + &backend->cred->time_stamp); + } + else { + /* Pre-Windows 10 1809 */ + ALG_ID algIds[NUM_CIPHERS]; + char *ciphers = SSL_CONN_CONFIG(cipher_list); + SCHANNEL_CRED schannel_cred = { 0 }; + schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + schannel_cred.dwFlags = flags; + schannel_cred.grbitEnabledProtocols = enabled_protocols; + + if(ciphers) { + result = set_ssl_ciphers(&schannel_cred, ciphers, algIds); + if(CURLE_OK != result) { + failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG"); + return result; + } + } + else { + schannel_cred.dwFlags = flags | SCH_USE_STRONG_CRYPTO; + } + +#ifdef HAS_CLIENT_CERT_PATH + if(client_certs[0]) { + schannel_cred.cCreds = 1; + schannel_cred.paCred = client_certs; + } +#endif + sspi_status = + s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, + SECPKG_CRED_OUTBOUND, NULL, + &schannel_cred, NULL, NULL, + &backend->cred->cred_handle, + &backend->cred->time_stamp); + } + +#ifdef HAS_CLIENT_CERT_PATH if(client_certs[0]) CertFreeCertificateContext(client_certs[0]); +#endif if(sspi_status != SEC_E_OK) { char buffer[STRERROR_LEN]; @@ -1993,12 +2288,7 @@ schannel_recv(struct Curl_easy *data, int sockindex, infof(data, "schannel: can't renegotiate, an error is pending"); goto cleanup; } - if(backend->encdata_offset) { - *err = CURLE_RECV_ERROR; - infof(data, "schannel: can't renegotiate, " - "encrypted data available"); - goto cleanup; - } + /* begin renegotiation */ infof(data, "schannel: renegotiating SSL/TLS connection"); connssl->state = ssl_connection_negotiating; @@ -2448,7 +2738,8 @@ const struct Curl_ssl Curl_ssl_schannel = { #ifdef HAS_MANUAL_VERIFY_API SSLSUPP_CAINFO_BLOB | #endif - SSLSUPP_PINNEDPUBKEY, + SSLSUPP_PINNEDPUBKEY | + SSLSUPP_TLS13_CIPHERSUITES, sizeof(struct ssl_backend_data), diff --git a/lib/vtls/schannel.h b/lib/vtls/schannel.h index 05323c7a7e..b2d222ac24 100644 --- a/lib/vtls/schannel.h +++ b/lib/vtls/schannel.h @@ -28,6 +28,16 @@ #ifdef USE_SCHANNEL +#define SCHANNEL_USE_BLACKLISTS 1 + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4201) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif /* Wincrypt must be included before anything that could include OpenSSL. */ #if defined(USE_WIN32_CRYPTO) #include @@ -84,6 +94,63 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data, #endif #endif +#ifndef SCH_CREDENTIALS_VERSION + +#define SCH_CREDENTIALS_VERSION 0x00000005 + +typedef enum _eTlsAlgorithmUsage +{ + TlsParametersCngAlgUsageKeyExchange, + TlsParametersCngAlgUsageSignature, + TlsParametersCngAlgUsageCipher, + TlsParametersCngAlgUsageDigest, + TlsParametersCngAlgUsageCertSig +} eTlsAlgorithmUsage; + +typedef struct _CRYPTO_SETTINGS +{ + eTlsAlgorithmUsage eAlgorithmUsage; + UNICODE_STRING strCngAlgId; + DWORD cChainingModes; + PUNICODE_STRING rgstrChainingModes; + DWORD dwMinBitLength; + DWORD dwMaxBitLength; +} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; + +typedef struct _TLS_PARAMETERS +{ + DWORD cAlpnIds; + PUNICODE_STRING rgstrAlpnIds; + DWORD grbitDisabledProtocols; + DWORD cDisabledCrypto; + PCRYPTO_SETTINGS pDisabledCrypto; + DWORD dwFlags; +} TLS_PARAMETERS, * PTLS_PARAMETERS; + +typedef struct _SCH_CREDENTIALS +{ + DWORD dwVersion; + DWORD dwCredFormat; + DWORD cCreds; + PCCERT_CONTEXT* paCred; + HCERTSTORE hRootStore; + + DWORD cMappers; + struct _HMAPPER **aphMappers; + + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD cTlsParameters; + PTLS_PARAMETERS pTlsParameters; +} SCH_CREDENTIALS, * PSCH_CREDENTIALS; + +#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16 +#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16 +#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16 +#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16 + +#endif + struct Curl_schannel_cred { CredHandle cred_handle; TimeStamp time_stamp; -- 2.47.3