From b4f9ae5126d85f82f736610dc1ea24eca83a72f1 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Sun, 19 Mar 2023 04:05:08 -0400 Subject: [PATCH] schannel: fix user-set legacy algorithms in Windows 10 & 11 - If the user set a legacy algorithm list (CURLOPT_SSL_CIPHER_LIST) then use the SCHANNEL_CRED legacy structure to pass the list to Schannel. - If the user set both a legacy algorithm list and a TLS 1.3 cipher list then abort. Although MS doesn't document it, Schannel will not negotiate TLS 1.3 when SCHANNEL_CRED is used. That means setting a legacy algorithm list limits the user to earlier versions of TLS. Prior to this change, since 8beff435 (precedes 7.85.0), libcurl would ignore legacy algorithms in Windows 10 1809 and later. Reported-by: zhihaoy@users.noreply.github.com Fixes https://github.com/curl/curl/pull/10741 Closes https://github.com/curl/curl/pull/10746 --- docs/CIPHERS.md | 17 ++++++++++-- docs/cmdline-opts/tls13-ciphers.d | 2 +- docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.3 | 5 +++- docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 | 4 +-- lib/vtls/schannel.c | 30 ++++++++++++++++----- 5 files changed, 45 insertions(+), 13 deletions(-) diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index 4b4560a60e..0e1d3cb284 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -346,10 +346,13 @@ maps them to the following case-insensitive names. ## Schannel Schannel allows the enabling and disabling of encryption algorithms, but not -specific cipher suites. They are +specific cipher suites, prior to TLS 1.3. The algorithms are [defined](https://docs.microsoft.com/windows/desktop/SecCrypto/alg-id) by Microsoft. +The algorithms below are for TLS 1.2 and earlier. TLS 1.3 is covered in the +next section. + There is also the case that the selected algorithm is not supported by the protocol or does not match the ciphers offered by the server during the SSL negotiation. In this case curl will return error @@ -412,7 +415,12 @@ 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`) +You can set TLS 1.3 ciphers for Schannel by using `CURLOPT_TLS13_CIPHERS` or +`--tls13-ciphers` with the names below. + +If TLS 1.3 cipher suites are set then libcurl will add or restrict Schannel TLS +1.3 algorithms automatically. Essentially, libcurl is emulating support for +individual TLS 1.3 cipher suites since Schannel does not support it directly. `TLS_AES_256_GCM_SHA384` `TLS_AES_128_GCM_SHA256` @@ -420,6 +428,11 @@ are running an outdated OS you might still be supporting weak ciphers. `TLS_AES_128_CCM_8_SHA256` `TLS_AES_128_CCM_SHA256` +Note if you set TLS 1.3 ciphers without also setting the minimum TLS version to +1.3 then it's possible Schannel may negotiate an earlier TLS version and cipher +suite if your libcurl and OS settings allow it. You can set the minimum TLS +version by using `CURLOPT_SSLVERSION` or `--tlsv1.3`. + ## 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/cmdline-opts/tls13-ciphers.d b/docs/cmdline-opts/tls13-ciphers.d index e319341915..34c607dd1b 100644 --- a/docs/cmdline-opts/tls13-ciphers.d +++ b/docs/cmdline-opts/tls13-ciphers.d @@ -17,5 +17,5 @@ cipher suite details 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 or Schannel. If you are using a different SSL backend you can try +later, or Schannel. If you are using a different SSL backend you can try setting TLS 1.3 cipher suites by using the --ciphers option. diff --git a/docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.3 b/docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.3 index 254970e39b..15d153cf9c 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.3 +++ b/docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.3 @@ -52,7 +52,10 @@ etc. With BearSSL you do not add/remove ciphers. If one uses this option then all known ciphers are disabled and only those passed in are enabled. -you will find more details about cipher lists on this URL: +For Schannel, you can use this option to set algorithms but not specific cipher +suites. Refer to the ciphers lists document for algorithms. + +You will find more details about cipher lists on this URL: https://curl.se/docs/ssl-ciphers.html diff --git a/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 b/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 index 37ebd76ec7..3af4531327 100644 --- a/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 +++ b/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.3 @@ -36,12 +36,12 @@ Pass a char *, pointing to a null-terminated string holding the list of cipher suites to use for the TLS 1.3 connection. The list must be syntactically correct, it consists of one or more cipher suite strings separated by colons. -you will find more details about cipher lists on this URL: +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 or Schannel. If you are using a different SSL backend you can try +later, or Schannel. If you are using a different SSL backend you can try setting TLS 1.3 cipher suites by using the \fICURLOPT_SSL_CIPHER_LIST(3)\fP option. diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 85f0fa7a09..cd15ac6b38 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -793,8 +793,11 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, backend->cred->client_cert_store = client_cert_store; #endif - /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */ - if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, + /* We support TLS 1.3 starting in Windows 10 version 1809 (OS build 17763) as + long as the user did not set a legacy algorithm list + (CURLOPT_SSL_CIPHER_LIST). */ + if(!conn_config->cipher_list && + curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { char *ciphers13 = 0; @@ -844,7 +847,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, /* reject too-long cipher names */ if(n > (LONGEST_ALG_ID - 1)) { - failf(data, "Cipher name too long, not checked."); + failf(data, "schannel: Cipher name too long, not checked"); return CURLE_SSL_CIPHER; } @@ -872,7 +875,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, disable_aes_ccm_sha256 = FALSE; } else { - failf(data, "Passed in an unknown TLS 1.3 cipher."); + failf(data, "schannel: Unknown TLS 1.3 cipher: %s", tmp); return CURLE_SSL_CIPHER; } @@ -887,7 +890,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, 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."); + failf(data, "schannel: All available TLS 1.3 ciphers were disabled"); return CURLE_SSL_CIPHER; } @@ -1010,7 +1013,9 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, &backend->cred->time_stamp); } else { - /* Pre-Windows 10 1809 */ + /* Pre-Windows 10 1809 or the user set a legacy algorithm list. Although MS + doesn't document it, currently Schannel will not negotiate TLS 1.3 when + SCHANNEL_CRED is used. */ ALG_ID algIds[NUM_CIPHERS]; char *ciphers = conn_config->cipher_list; SCHANNEL_CRED schannel_cred = { 0 }; @@ -1019,9 +1024,20 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, schannel_cred.grbitEnabledProtocols = enabled_protocols; if(ciphers) { + if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) { + infof(data, "schannel: WARNING: This version of Schannel may " + "negotiate a less-secure TLS version than TLS 1.3 because the " + "user set an algorithm cipher list."); + } + if(conn_config->cipher_list13) { + failf(data, "schannel: This version of Schannel does not support " + "setting an algorithm cipher list and TLS 1.3 cipher list at " + "the same time"); + return CURLE_SSL_CIPHER; + } result = set_ssl_ciphers(&schannel_cred, ciphers, algIds); if(CURLE_OK != result) { - failf(data, "Unable to set ciphers to from connection ssl config"); + failf(data, "schannel: Failed setting algorithm cipher list"); return result; } }