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
This commit is contained in:
Wyatt O'Day 2022-07-22 10:45:28 -04:00 committed by Jay Satiro
parent 92179353e5
commit 8beff43559
5 changed files with 434 additions and 64 deletions

View File

@ -6,7 +6,8 @@ and
[`--ciphers`](https://curl.se/docs/manpage.html#--ciphers) [`--ciphers`](https://curl.se/docs/manpage.html#--ciphers)
users can control which ciphers to consider when negotiating TLS connections. 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) [`CURLOPT_TLS13_CIPHERS`](https://curl.se/libcurl/c/CURLOPT_TLS13_CIPHERS.html)
and and
[`--tls13-ciphers`](https://curl.se/docs/manpage.html#--tls13-ciphers) [`--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 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. 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
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`). 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`).

View File

@ -58,7 +58,7 @@ CURL *curl = curl_easy_init();
if(curl) { if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
curl_easy_setopt(curl, CURLOPT_PROXY_TLS13_CIPHERS, curl_easy_setopt(curl, CURLOPT_PROXY_TLS13_CIPHERS,
"TLS13-CHACHA20-POLY1305-SHA256"); "TLS_CHACHA20_POLY1305_SHA256");
ret = curl_easy_perform(curl); ret = curl_easy_perform(curl);
curl_easy_cleanup(curl); curl_easy_cleanup(curl);
} }

View File

@ -41,8 +41,8 @@ you will find more details about cipher lists on this URL:
https://curl.se/docs/ssl-ciphers.html https://curl.se/docs/ssl-ciphers.html
This option is currently used only when curl is built to use OpenSSL 1.1.1 or 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 later or SChannel. If you are using a different SSL backend you can try
cipher suites by using the CURLOPT_SSL_CIPHER_LIST option. 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 The application does not have to keep the string around after setting this
option. option.
@ -56,14 +56,15 @@ CURL *curl = curl_easy_init();
if(curl) { if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
curl_easy_setopt(curl, CURLOPT_TLS13_CIPHERS, curl_easy_setopt(curl, CURLOPT_TLS13_CIPHERS,
"TLS13-CHACHA20-POLY1305-SHA256"); "TLS_CHACHA20_POLY1305_SHA256");
ret = curl_easy_perform(curl); ret = curl_easy_perform(curl);
curl_easy_cleanup(curl); curl_easy_cleanup(curl);
} }
.fi .fi
.SH AVAILABILITY .SH AVAILABILITY
Added in 7.61.0. Added in 7.61.0 for OpenSSL. Available when built with OpenSSL >= 1.1.1.
Available when built with OpenSSL >= 1.1.1.
Added in 7.85.0 for SChannel.
.SH RETURN VALUE .SH RETURN VALUE
Returns CURLE_OK if supported, CURLE_NOT_BUILT_IN otherwise. Returns CURLE_OK if supported, CURLE_NOT_BUILT_IN otherwise.
.SH "SEE ALSO" .SH "SEE ALSO"

View File

@ -84,7 +84,39 @@
#endif #endif
#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 #define HAS_CLIENT_CERT_PATH
#endif #endif
@ -120,6 +152,10 @@
#define SP_PROT_TLS1_2_CLIENT 0x00000800 #define SP_PROT_TLS1_2_CLIENT 0x00000800
#endif #endif
#ifndef SP_PROT_TLS1_3_CLIENT
#define SP_PROT_TLS1_3_CLIENT 0x00002000
#endif
#ifndef SCH_USE_STRONG_CRYPTO #ifndef SCH_USE_STRONG_CRYPTO
#define SCH_USE_STRONG_CRYPTO 0x00400000 #define SCH_USE_STRONG_CRYPTO 0x00400000
#endif #endif
@ -174,7 +210,7 @@ static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
} }
static CURLcode 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) struct connectdata *conn)
{ {
long ssl_version = SSL_CONN_CONFIG(version); 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) { switch(ssl_version_max) {
case CURL_SSLVERSION_MAX_NONE: case CURL_SSLVERSION_MAX_NONE:
case CURL_SSLVERSION_MAX_DEFAULT: 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; break;
} }
for(; i <= (ssl_version_max >> 16); ++i) { for(; i <= (ssl_version_max >> 16); ++i) {
switch(i) { switch(i) {
case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_0:
schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT; (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT;
break; break;
case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_1:
schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT;
break; break;
case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_2:
schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT;
break; break;
case CURL_SSLVERSION_TLSv1_3: 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; return CURLE_OK;
@ -217,8 +275,12 @@ get_alg_id_by_name(char *name)
{ {
char tmp[LONGEST_ALG_ID] = { 0 }; char tmp[LONGEST_ALG_ID] = { 0 };
char *nameEnd = strchr(name, ':'); char *nameEnd = strchr(name, ':');
size_t n = nameEnd ? min((size_t)(nameEnd - name), LONGEST_ALG_ID - 1) : \ size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name);
min(strlen(name), LONGEST_ALG_ID - 1);
/* reject too-long alg names */
if(n > (LONGEST_ALG_ID - 1))
return 0;
strncpy(tmp, name, n); strncpy(tmp, name, n);
tmp[n] = 0; tmp[n] = 0;
CIPHEROPTION(CALG_MD2); CIPHEROPTION(CALG_MD2);
@ -422,49 +484,51 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
int sockindex) int sockindex)
{ {
struct ssl_connect_data *connssl = &conn->ssl[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 }; PCCERT_CONTEXT client_certs[1] = { NULL };
#endif
SECURITY_STATUS sspi_status = SEC_E_OK; SECURITY_STATUS sspi_status = SEC_E_OK;
CURLcode result; CURLcode result;
/* setup Schannel API options */
DWORD flags = 0;
DWORD enabled_protocols = 0;
struct ssl_backend_data *backend = connssl->backend; struct ssl_backend_data *backend = connssl->backend;
DEBUGASSERT(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) { if(conn->ssl_config.verifypeer) {
#ifdef HAS_MANUAL_VERIFY_API #ifdef HAS_MANUAL_VERIFY_API
if(backend->use_manual_cred_validation) if(backend->use_manual_cred_validation)
schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION; flags = SCH_CRED_MANUAL_CRED_VALIDATION;
else else
#endif #endif
schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION; flags = SCH_CRED_AUTO_CRED_VALIDATION;
if(SSL_SET_OPTION(no_revoke)) { 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; SCH_CRED_IGNORE_REVOCATION_OFFLINE;
DEBUGF(infof(data, "schannel: disabled server certificate revocation " DEBUGF(infof(data, "schannel: disabled server certificate revocation "
"checks")); "checks"));
} }
else if(SSL_SET_OPTION(revoke_best_effort)) { 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; SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN;
DEBUGF(infof(data, "schannel: ignore revocation offline errors")); DEBUGF(infof(data, "schannel: ignore revocation offline errors"));
} }
else { else {
schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; flags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
DEBUGF(infof(data, DEBUGF(infof(data,
"schannel: checking server certificate revocation")); "schannel: checking server certificate revocation"));
} }
} }
else { else {
schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | flags = SCH_CRED_MANUAL_CRED_VALIDATION |
SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
SCH_CRED_IGNORE_REVOCATION_OFFLINE; SCH_CRED_IGNORE_REVOCATION_OFFLINE;
DEBUGF(infof(data, DEBUGF(infof(data,
@ -472,15 +536,15 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
} }
if(!conn->ssl_config.verifyhost) { 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 " DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from "
"comparing the supplied target name with the subject " "comparing the supplied target name with the subject "
"names in server certificates.")); "names in server certificates."));
} }
if(!SSL_SET_OPTION(auto_client_cert)) { if(!SSL_SET_OPTION(auto_client_cert)) {
schannel_cred.dwFlags &= ~SCH_CRED_USE_DEFAULT_CREDS; flags &= ~SCH_CRED_USE_DEFAULT_CREDS;
schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; flags |= SCH_CRED_NO_DEFAULT_CREDS;
infof(data, "schannel: disabled automatic use of client certificate"); infof(data, "schannel: disabled automatic use of client certificate");
} }
else else
@ -494,7 +558,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_2:
case CURL_SSLVERSION_TLSv1_3: 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) if(result != CURLE_OK)
return result; return result;
break; break;
@ -508,16 +572,6 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
return CURLE_SSL_CONNECT_ERROR; 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 #ifdef HAS_CLIENT_CERT_PATH
/* client certificate */ /* client certificate */
if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { 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); CertCloseStore(cert_store, 0);
return CURLE_SSL_CERTPROBLEM; return CURLE_SSL_CERTPROBLEM;
} }
schannel_cred.cCreds = 1;
schannel_cred.paCred = client_certs;
} }
else { else {
cert_store = cert_store =
@ -691,11 +742,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
curlx_unicodefree(cert_path); curlx_unicodefree(cert_path);
if(client_certs[0]) { if(!client_certs[0]) {
schannel_cred.cCreds = 1;
schannel_cred.paCred = client_certs;
}
else {
/* CRYPT_E_NOT_FOUND / E_INVALIDARG */ /* CRYPT_E_NOT_FOUND / E_INVALIDARG */
CertCloseStore(cert_store, 0); CertCloseStore(cert_store, 0);
return CURLE_SSL_CERTPROBLEM; return CURLE_SSL_CERTPROBLEM;
@ -716,22 +763,270 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
if(!backend->cred) { if(!backend->cred) {
failf(data, "schannel: unable to allocate memory"); failf(data, "schannel: unable to allocate memory");
#ifdef HAS_CLIENT_CERT_PATH
if(client_certs[0]) if(client_certs[0])
CertFreeCertificateContext(client_certs[0]); CertFreeCertificateContext(client_certs[0]);
#endif
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
} }
backend->cred->refcount = 1; backend->cred->refcount = 1;
sspi_status = /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */
s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT,
SECPKG_CRED_OUTBOUND, NULL, VERSION_GREATER_THAN_EQUAL)) {
&schannel_cred, NULL, NULL,
&backend->cred->cred_handle,
&backend->cred->time_stamp);
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]) if(client_certs[0])
CertFreeCertificateContext(client_certs[0]); CertFreeCertificateContext(client_certs[0]);
#endif
if(sspi_status != SEC_E_OK) { if(sspi_status != SEC_E_OK) {
char buffer[STRERROR_LEN]; 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"); infof(data, "schannel: can't renegotiate, an error is pending");
goto cleanup; goto cleanup;
} }
if(backend->encdata_offset) {
*err = CURLE_RECV_ERROR;
infof(data, "schannel: can't renegotiate, "
"encrypted data available");
goto cleanup;
}
/* begin renegotiation */ /* begin renegotiation */
infof(data, "schannel: renegotiating SSL/TLS connection"); infof(data, "schannel: renegotiating SSL/TLS connection");
connssl->state = ssl_connection_negotiating; connssl->state = ssl_connection_negotiating;
@ -2448,7 +2738,8 @@ const struct Curl_ssl Curl_ssl_schannel = {
#ifdef HAS_MANUAL_VERIFY_API #ifdef HAS_MANUAL_VERIFY_API
SSLSUPP_CAINFO_BLOB | SSLSUPP_CAINFO_BLOB |
#endif #endif
SSLSUPP_PINNEDPUBKEY, SSLSUPP_PINNEDPUBKEY |
SSLSUPP_TLS13_CIPHERSUITES,
sizeof(struct ssl_backend_data), sizeof(struct ssl_backend_data),

View File

@ -28,6 +28,16 @@
#ifdef USE_SCHANNEL #ifdef USE_SCHANNEL
#define SCHANNEL_USE_BLACKLISTS 1
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4201)
#endif
#include <subauth.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
/* Wincrypt must be included before anything that could include OpenSSL. */ /* Wincrypt must be included before anything that could include OpenSSL. */
#if defined(USE_WIN32_CRYPTO) #if defined(USE_WIN32_CRYPTO)
#include <wincrypt.h> #include <wincrypt.h>
@ -84,6 +94,63 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data,
#endif #endif
#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 { struct Curl_schannel_cred {
CredHandle cred_handle; CredHandle cred_handle;
TimeStamp time_stamp; TimeStamp time_stamp;