schannel: remove TLS 1.3 ciphersuite-list support

Drop TLS 1.3 ciphersuite-list support from SChannel because of the
number of bugs in SChannel itself (a closed-source TLS library). TLS 1.3
with SChannel still works, however the ciphersuite negotiation is left
entirely to SChannel.

Bug: https://hackerone.com/reports/2792484
Reported-by: newfunction on hackerone

Fixes https://github.com/curl/curl/issues/15482
Closes https://github.com/curl/curl/pull/15621
This commit is contained in:
Wyatt O'Day 2024-11-21 09:08:31 -05:00 committed by Jay Satiro
parent e233073f01
commit 6238888ca7
5 changed files with 8 additions and 206 deletions

View File

@ -42,8 +42,7 @@ common cipher suite in the list sent by curl is chosen.
Setting TLS 1.3 cipher suites is supported by curl with
OpenSSL (1.1.1+, curl 7.61.0+), LibreSSL (3.4.1+, curl 8.3.0+),
wolfSSL (curl 8.10.0+), mbedTLS (3.6.0+, curl 8.10.0+) and
Schannel (curl 7.85.0+).
wolfSSL (curl 8.10.0+) and mbedTLS (3.6.0+, curl 8.10.0+).
The list of cipher suites that can be used for the `--tls13-ciphers` option:
```
@ -61,13 +60,6 @@ In addition to above list the following cipher suites can be used:
Usage of these cipher suites is not recommended. (The last two cipher suites
are NULL ciphers, offering no encryption whatsoever.)
### Schannel notes
Schannel does not support setting individual TLS 1.3 cipher suites directly.
To support `--tls13-ciphers` curl emulates it by adding or restricting
algorithms to use. Due to this the specified order of preference of the
cipher suites is not taken into account.
## TLS 1.2 (1.1, 1.0) cipher suites
Setting TLS 1.2 cipher suites is supported by curl with OpenSSL, LibreSSL,

View File

@ -25,7 +25,7 @@ cipher suite details on this URL:
https://curl.se/docs/ssl-ciphers.html
This option is used when curl is built to use OpenSSL 1.1.1 or later,
Schannel, wolfSSL, or mbedTLS 3.6.0 or later.
wolfSSL, or mbedTLS 3.6.0 or later.
Before curl 8.10.0 with mbedTLS or wolfSSL, TLS 1.3 cipher suites were set
by using the --ciphers option.

View File

@ -14,7 +14,6 @@ Protocol:
- TLS
TLS-backend:
- OpenSSL
- Schannel
- wolfSSL
- mbedTLS
- rustls
@ -84,7 +83,6 @@ int main(void)
# HISTORY
OpenSSL support added in 7.61.0, available when built with OpenSSL \>= 1.1.1.
Schannel support added in 7.87.0.
LibreSSL support added in 8.3.0, available when built with LibreSSL \>= 3.4.1.
wolfSSL support added in 8.10.0.
mbedTLS support added in 8.10.0, available when built with mbedTLS \>= 3.6.0.

View File

@ -15,7 +15,6 @@ Protocol:
- TLS
TLS-backend:
- OpenSSL
- Schannel
- wolfSSL
- mbedTLS
- rustls
@ -84,7 +83,6 @@ int main(void)
# HISTORY
OpenSSL support added in 7.61.0, available when built with OpenSSL \>= 1.1.1.
Schannel support added in 7.85.0.
LibreSSL support added in 8.3.0, available when built with LibreSSL \>= 3.4.1.
wolfSSL support added in 8.10.0.
mbedTLS support added in 8.10.0, available when built with mbedTLS \>= 3.6.0.

View File

@ -451,11 +451,6 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
}
#endif
static bool algo(const char *check, char *namep, size_t nlen)
{
return (strlen(check) == nlen) && !strncmp(check, namep, nlen);
}
static CURLcode
schannel_acquire_credential_handle(struct Curl_cfilter *cf,
struct Curl_easy *data)
@ -781,187 +776,14 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
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 explicitly listed, then
* disable all the ciphers and re-enable which
* ciphers the user has provided.
*/
ciphers13 = 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 *nameEnd;
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)) {
size_t n;
char *namep;
nameEnd = strchr(startCur, ':');
n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur);
namep = startCur;
if(disable_aes_gcm_sha384 &&
algo("TLS_AES_256_GCM_SHA384", namep, n)) {
disable_aes_gcm_sha384 = FALSE;
}
else if(disable_aes_gcm_sha256
&& algo("TLS_AES_128_GCM_SHA256", namep, n)) {
disable_aes_gcm_sha256 = FALSE;
}
else if(disable_chacha_poly
&& algo("TLS_CHACHA20_POLY1305_SHA256", namep, n)) {
disable_chacha_poly = FALSE;
}
else if(disable_aes_ccm_8_sha256
&& algo("TLS_AES_128_CCM_8_SHA256", namep, n)) {
disable_aes_ccm_8_sha256 = FALSE;
}
else if(disable_aes_ccm_sha256
&& algo("TLS_AES_128_CCM_SHA256", namep, n)) {
disable_aes_ccm_sha256 = FALSE;
}
else {
failf(data, "schannel: Unknown TLS 1.3 cipher: %.*s", (int)n, namep);
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, "schannel: 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 explicitly 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++;
}
CRYPTO_SETTINGS crypto_settings[1] = { { 0 } };
tls_parameters.pDisabledCrypto = crypto_settings;
/* The number of blocked suites */
tls_parameters.cDisabledCrypto = (DWORD)crypto_settings_idx;
tls_parameters.cDisabledCrypto = (DWORD)0;
credentials.pTlsParameters = &tls_parameters;
credentials.cTlsParameters = 1;
@ -986,9 +808,8 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
&backend->cred->time_stamp);
}
else {
/* Pre-Windows 10 1809 or the user set a legacy algorithm list. Although MS
does not document it, currently Schannel will not negotiate TLS 1.3 when
SCHANNEL_CRED is used. */
/* Pre-Windows 10 1809 or the user set a legacy algorithm list.
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 };
@ -998,16 +819,10 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
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 "
infof(data, "schannel: WARNING: This version of Schannel "
"negotiates 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, "schannel: Failed setting algorithm cipher list");
@ -2974,7 +2789,6 @@ const struct Curl_ssl Curl_ssl_schannel = {
#ifndef CURL_WINDOWS_UWP
SSLSUPP_PINNEDPUBKEY |
#endif
SSLSUPP_TLS13_CIPHERSUITES |
SSLSUPP_CA_CACHE |
SSLSUPP_HTTPS_PROXY |
SSLSUPP_CIPHER_LIST,