Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schannel regression: CURLOPT_SSL_CIPHER_LIST has no effect on Windows 10 >= 10.0.17763 #10741

Closed
zhihaoy opened this issue Mar 10, 2023 · 3 comments
Labels
TLS Windows Windows-specific

Comments

@zhihaoy
Copy link

zhihaoy commented Mar 10, 2023

I did this

Set CURLOPT_SSL_CIPHER_LIST on a connection that uses Schannel, TLS 1.2, and make a connection to an HTTPS server.

I expected the following

Investigate its Hello message, the cipher suites should match my settings.

curl/libcurl version

libcurl 7.85.0 to the latest

operating system

Fails (CURLOPT_SSL_CIPHER_LIST has no effect) on Windows 10 build 17763 and above, such as 10 Enterprise 1809 and 20H1.
Works (CURLOPT_SSL_CIPHER_LIST is in effect) on Windows 10 build 14393, such as Windows Server 2016

Cause analysis

The problem was introduced by 8beff43. The change moved this chunk of code:

curl/lib/vtls/schannel.c

Lines 503 to 510 in 801bd51

if(SSL_CONN_CONFIG(cipher_list)) {
result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list),
backend->algIds);
if(CURLE_OK != result) {
failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG");
return result;
}
}
to

curl/lib/vtls/schannel.c

Lines 1006 to 1021 in cd95ee9

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;
}
}
However, the new block was an else block started way above

curl/lib/vtls/schannel.c

Lines 790 to 792 in cd95ee9

/* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */
if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT,
VERSION_GREATER_THAN_EQUAL)) {
Then the logic becomes: Since only Windows 10 build 17763 supports TLS 1.3, we configure only TLS 1.3 ciphers on those systems; if we are on a lower version of Windows, we go ahead and configure TLS 1.2 (or lower) ciphers. So the combination of "on a newer version of Windows 10" and "using TLS 1.2" falls into neither branch. Support for CURLOPT_SSL_CIPHER_LIST under that combined condition was lost.

@bagder bagder added TLS Windows Windows-specific labels Mar 10, 2023
jay added a commit to jay/curl that referenced this issue Mar 12, 2023
- If CURLOPT_TLS13_CIPHERS is not set then use CURLOPT_SSL_CIPHER_LIST
  ciphers instead.

Prior to this change libcurl would ignore older ciphers (TLS <= 1.2)
in Windows 10 1809 and later. For example, if the user set anything via
CURLOPT_SSL_CIPHER_LIST it was ignored in favor of CURLOPT_TLS13_CIPHERS
even if the latter wasn't set.

Unresolved: CURLOPT_TLS13_CIPHERS and CURLOPT_SSL_CIPHER_LIST are still
mutually exclusive for Schannel. I'm not sure how to solve this. Since
this commit, if CURLOPT_TLS13_CIPHERS is set then SCH_CREDENTIALS is
used to set banned ciphers based on what is missing from the user's
TLS 1.3 cipher list; however if CURLOPT_TLS13_CIPHERS is not set
then SCH_CREDENTIALS is used to set allowed TLS <= 1.2 ciphers, if any,
set via CURLOPT_SSL_CIPHER_LIST. Can they be combined somehow into a
single credentials struct?

Reported-by: zhihaoy@users.noreply.github.com

Fixes curl#10741
Closes #xxxx
@jay
Copy link
Member

jay commented Mar 12, 2023

I've submitted a fix in PR #10746 which should allow TLS < 1.3 ciphers to be set as long as TLS 1.3 ciphers are not set. I'm not sure if it's possible to combine the two because they use different credential structs and we are only allowed to pass a single credential struct to schannel. The PR is marked as draft for now. If anyone can investigate further that would be helpful.

/cc @wyattoday

@vszakats
Copy link
Member

Does this bug mean that when running on Windows 10 1809+, curl (with Schannel) is forcing TLS 1.3 with no option to use TLS 1.2? Or, requiring --tlsv1.2 option to access servers offering no TLS 1.3? Or, not even with an option?

@wyattoday
Copy link
Contributor

wyattoday commented Mar 14, 2023

Does this bug mean that when running on Windows 10 1809+, curl (with Schannel) is forcing TLS 1.3 with no option to use TLS 1.2? Or, requiring --tlsv1.2 option to access servers offering no TLS 1.3? Or, not even with an option?

Nope, it doesn’t mean that. This is about cipher lists.

See my reply to @jay PR (here: #10746 (comment) ) for my view on this (not a bug) and possible remedies if the consensus is that this is a bug (remedy 3 is probably the most secure of the insecure options).

jay added a commit to jay/curl that referenced this issue Jun 6, 2023
- 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 8beff43 (precedes 7.85.0), libcurl would
ignore legacy algorithms in Windows 10 1809 and later.

Reported-by: zhihaoy@users.noreply.github.com

Fixes curl#10741
Closes #xxxx
jay added a commit to jay/curl that referenced this issue Aug 1, 2023
- 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 8beff43 (precedes 7.85.0), libcurl would
ignore legacy algorithms in Windows 10 1809 and later.

Reported-by: zhihaoy@users.noreply.github.com

Fixes curl#10741
Closes #xxxx
@jay jay closed this as completed in b4f9ae5 Aug 2, 2023
ptitSeb pushed a commit to wasix-org/curl that referenced this issue Sep 25, 2023
- 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 8beff43 (precedes 7.85.0), libcurl would
ignore legacy algorithms in Windows 10 1809 and later.

Reported-by: zhihaoy@users.noreply.github.com

Fixes curl#10741
Closes curl#10746
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
TLS Windows Windows-specific
Development

Successfully merging a pull request may close this issue.

5 participants