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:
parent
92179353e5
commit
8beff43559
@ -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`).
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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),
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user