Skip to content

Commit

Permalink
schannel: Disable auto credentials; add an option to enable it
Browse files Browse the repository at this point in the history
- Disable auto credentials by default. This is a breaking change
  for clients that are using it, wittingly or not.

- New libcurl ssl option value CURLSSLOPT_AUTO_CLIENT_CERT tells libcurl
  to automatically locate and use a client certificate for
  authentication, when requested by the server.

- New curl tool options --ssl-auto-client-cert and
  --proxy-ssl-auto-client-cert map to CURLSSLOPT_AUTO_CLIENT_CERT.

This option is only supported for Schannel (the native Windows SSL
library). Prior to this change Schannel would, with no notification to
the client, attempt to locate a client certificate and send it to the
server, when requested by the server. Since the server can request any
certificate that supports client authentication in the OS certificate
store it could be a privacy violation and unexpected.

Fixes #2262
Reported-by: Jeroen Ooms
Assisted-by: Wes Hinsley
Assisted-by: Rich FitzJohn

Ref: https://curl.se/mail/lib-2021-02/0066.html
Reported-by: Morten Minde Neergaard

Closes #6673
  • Loading branch information
jay committed Apr 22, 2021
1 parent e4ba999 commit 54e7475
Show file tree
Hide file tree
Showing 20 changed files with 108 additions and 28 deletions.
14 changes: 0 additions & 14 deletions docs/TODO
Expand Up @@ -126,7 +126,6 @@
15. Schannel
15.1 Extend support for client certificate authentication
15.2 Extend support for the --ciphers option
15.3 Add option to disable client certificate auto-send
15.4 Add option to allow abrupt server closure

16. SASL
Expand Down Expand Up @@ -861,19 +860,6 @@
- Specifying Schannel Ciphers and Cipher Strengths
https://msdn.microsoft.com/en-us/library/windows/desktop/aa380161.aspx

15.3 Add option to disable client certificate auto-send

Microsoft says "By default, Schannel will, with no notification to the client,
attempt to locate a client certificate and send it to the server." That could
be considered a privacy violation and unexpected.

Some Windows users have come to expect that default behavior and to change the
default to make it consistent with other SSL backends would be a breaking
change. An option should be added that can be used to disable the default
Schannel auto-send behavior.

https://github.com/curl/curl/issues/2262

15.4 Add option to allow abrupt server closure

libcurl w/schannel will error without a known termination point from the
Expand Down
2 changes: 2 additions & 0 deletions docs/cmdline-opts/Makefile.inc
Expand Up @@ -180,6 +180,7 @@ DPAGES = \
proxy-pinnedpubkey.d \
proxy-service-name.d \
proxy-ssl-allow-beast.d \
proxy-ssl-auto-client-cert.d \
proxy-tls13-ciphers.d \
proxy-tlsauthtype.d \
proxy-tlspassword.d \
Expand Down Expand Up @@ -223,6 +224,7 @@ DPAGES = \
speed-limit.d \
speed-time.d \
ssl-allow-beast.d \
ssl-auto-client-cert.d \
ssl-no-revoke.d \
ssl-reqd.d \
ssl-revoke-best-effort.d \
Expand Down
6 changes: 6 additions & 0 deletions docs/cmdline-opts/proxy-ssl-auto-client-cert.d
@@ -0,0 +1,6 @@
Long: proxy-ssl-auto-client-cert
Help: Use auto client certificate for proxy (Schannel)
Added: 7.77.0
Category: proxy tls
---
Same as --ssl-auto-client-cert but used in HTTPS proxy context.
12 changes: 12 additions & 0 deletions docs/cmdline-opts/ssl-auto-client-cert.d
@@ -0,0 +1,12 @@
Long: ssl-auto-client-cert
Help: Use auto client certificate (Schannel)
Added: 7.77.0
See-also: proxy-ssl-auto-client-cert
Category: tls
---
Tell libcurl to automatically locate and use a client certificate for
authentication, when requested by the server. This option is only supported
for Schannel (the native Windows SSL library). Prior to 7.77.0 this was the
default behavior in libcurl with Schannel. Since the server can request any
certificate that supports client authentication in the OS certificate store it
could be a privacy violation and unexpected.
9 changes: 8 additions & 1 deletion docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.3
Expand Up @@ -49,13 +49,20 @@ Tells libcurl to not accept "partial" certificate chains, which it otherwise
does by default. This option is only supported for OpenSSL and will fail the
certificate verification if the chain ends with an intermediate certificate
and not with a root cert. (Added in 7.68.0)

.IP CURLSSLOPT_REVOKE_BEST_EFFORT
Tells libcurl to ignore certificate revocation checks in case of missing or
offline distribution points for those SSL backends where such behavior is
present. This option is only supported for Schannel (the native Windows SSL
library). If combined with \fICURLSSLOPT_NO_REVOKE\fP, the latter takes
precedence. (Added in 7.70.0)
.IP CURLSSLOPT_AUTO_CLIENT_CERT
Tell libcurl to automatically locate and use a client certificate for
authentication, when requested by the server. This option is only supported
for Schannel (the native Windows SSL library). Prior to 7.77.0 this was the
default behavior in libcurl with Schannel. Since the server can request any
certificate that supports client authentication in the OS certificate store it
could be a privacy violation and unexpected.
(Added in 7.77.0)
.SH DEFAULT
0
.SH PROTOCOLS
Expand Down
8 changes: 8 additions & 0 deletions docs/libcurl/opts/CURLOPT_SSL_OPTIONS.3
Expand Up @@ -60,6 +60,14 @@ Tell libcurl to use the operating system's native CA store for certificate
verification. Works only on Windows when built to use OpenSSL. This option is
experimental and behavior is subject to change.
(Added in 7.71.0)
.IP CURLSSLOPT_AUTO_CLIENT_CERT
Tell libcurl to automatically locate and use a client certificate for
authentication, when requested by the server. This option is only supported
for Schannel (the native Windows SSL library). Prior to 7.77.0 this was the
default behavior in libcurl with Schannel. Since the server can request any
certificate that supports client authentication in the OS certificate store it
could be a privacy violation and unexpected.
(Added in 7.77.0)
.SH DEFAULT
0
.SH PROTOCOLS
Expand Down
1 change: 1 addition & 0 deletions docs/libcurl/symbols-in-versions
Expand Up @@ -816,6 +816,7 @@ CURLSSLBACKEND_SCHANNEL 7.34.0
CURLSSLBACKEND_SECURETRANSPORT 7.64.1
CURLSSLBACKEND_WOLFSSL 7.49.0
CURLSSLOPT_ALLOW_BEAST 7.25.0
CURLSSLOPT_AUTO_CLIENT_CERT 7.77.0
CURLSSLOPT_NATIVE_CA 7.71.0
CURLSSLOPT_NO_PARTIALCHAIN 7.68.0
CURLSSLOPT_NO_REVOKE 7.44.0
Expand Down
2 changes: 2 additions & 0 deletions docs/options-in-versions
Expand Up @@ -169,6 +169,7 @@
--proxy-pinnedpubkey 7.59.0
--proxy-service-name 7.43.0
--proxy-ssl-allow-beast 7.52.0
--proxy-ssl-auto-client-cert 7.77.0
--proxy-tls13-ciphers 7.61.0
--proxy-tlsauthtype 7.52.0
--proxy-tlspassword 7.52.0
Expand Down Expand Up @@ -212,6 +213,7 @@
--speed-time (-y) 4.7
--ssl 7.20.0
--ssl-allow-beast 7.25.0
--ssl-auto-client-cert 7.77.0
--ssl-no-revoke 7.44.0
--ssl-reqd 7.20.0
--ssl-revoke-best-effort 7.70.0
Expand Down
4 changes: 4 additions & 0 deletions include/curl/curl.h
Expand Up @@ -888,6 +888,10 @@ typedef enum {
operating system. Currently implemented under MS-Windows. */
#define CURLSSLOPT_NATIVE_CA (1<<4)

/* - CURLSSLOPT_AUTO_CLIENT_CERT tells libcurl to automatically locate and use
a client certificate for authentication. (Schannel) */
#define CURLSSLOPT_AUTO_CLIENT_CERT (1<<5)

/* The default connection attempt delay in milliseconds for happy eyeballs.
CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document
this value, keep them in sync. */
Expand Down
5 changes: 4 additions & 1 deletion lib/doh.c
Expand Up @@ -351,7 +351,10 @@ static CURLcode dohprobe(struct Curl_easy *data,
(data->set.ssl.revoke_best_effort ?
CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
(data->set.ssl.native_ca_store ?
CURLSSLOPT_NATIVE_CA : 0);
CURLSSLOPT_NATIVE_CA : 0) |
(data->set.ssl.auto_client_cert ?
CURLSSLOPT_AUTO_CLIENT_CERT : 0);

curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
}

Expand Down
5 changes: 4 additions & 1 deletion lib/setopt.c
Expand Up @@ -2290,6 +2290,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
data->set.ssl.auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
/* If a setting is added here it should also be added in dohprobe()
which sets its own CURLOPT_SSL_OPTIONS based on these settings. */
break;
Expand All @@ -2301,9 +2302,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
(bool)((arg&CURLSSLOPT_ALLOW_BEAST) ? TRUE : FALSE);
data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
data->set.proxy_ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
data->set.proxy_ssl.revoke_best_effort =
!!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
data->set.proxy_ssl.auto_client_cert =
!!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
break;
#endif

Expand Down
2 changes: 2 additions & 0 deletions lib/urldata.h
Expand Up @@ -286,6 +286,8 @@ struct ssl_config_data {
BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation
list errors */
BIT(native_ca_store); /* use the native ca store of operating system */
BIT(auto_client_cert); /* automatically locate and use a client
certificate for authentication (Schannel) */
};

struct ssl_general_config {
Expand Down
19 changes: 14 additions & 5 deletions lib/vtls/schannel.c
Expand Up @@ -553,6 +553,20 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
"names in server certificates.\n"));
}

/* security request flags */
BACKEND->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
ISC_REQ_STREAM;

if(!SSL_SET_OPTION(auto_client_cert)) {
schannel_cred.dwFlags &= ~SCH_CRED_USE_DEFAULT_CREDS;
schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
infof(data, "schannel: disabled automatic use of client certificate\n");
}
else
infof(data, "schannel: enabled automatic use of client certificate\n");

switch(conn->ssl_config.version) {
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
Expand Down Expand Up @@ -886,11 +900,6 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);

/* setup request flags */
BACKEND->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
ISC_REQ_STREAM;

/* allocate memory for the security context handle */
BACKEND->ctxt = (struct Curl_schannel_ctxt *)
calloc(1, sizeof(struct Curl_schannel_ctxt));
Expand Down
2 changes: 2 additions & 0 deletions packages/OS400/curl.inc.in
Expand Up @@ -870,6 +870,8 @@
d c X'0008'
d CURLSSLOPT_NATIVE_CA...
d c X'0010'
d CURLSSLOPT_AUTO_CLIENT_CERT...
d c X'0020'
*
d CURL_HET_DEFAULT...
d c 200
Expand Down
3 changes: 3 additions & 0 deletions src/tool_cfgable.h
Expand Up @@ -267,6 +267,9 @@ struct OperationConfig {
revocation list errors */

bool native_ca_store; /* use the native os ca store */
bool ssl_auto_client_cert; /* automatically locate and use a client
certificate for authentication (Schannel) */
bool proxy_ssl_auto_client_cert; /* proxy version of ssl_auto_client_cert */

bool use_metalink; /* process given URLs as metalink XML file */
struct metalinkfile *metalinkfile_list; /* point to the first node */
Expand Down
13 changes: 12 additions & 1 deletion src/tool_getparam.c
Expand Up @@ -247,7 +247,8 @@ static const struct LongShort aliases[]= {
{"El", "tlspassword", ARG_STRING},
{"Em", "tlsauthtype", ARG_STRING},
{"En", "ssl-allow-beast", ARG_BOOL},
/* Eo */
{"Eo", "ssl-auto-client-cert", ARG_BOOL},
{"EO", "proxy-ssl-auto-client-cert", ARG_BOOL},
{"Ep", "pinnedpubkey", ARG_STRING},
{"EP", "proxy-pinnedpubkey", ARG_STRING},
{"Eq", "cert-status", ARG_BOOL},
Expand Down Expand Up @@ -1651,6 +1652,16 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
config->ssl_allow_beast = toggle;
break;

case 'o': /* --ssl-auto-client-cert */
if(curlinfo->features & CURL_VERSION_SSL)
config->ssl_auto_client_cert = toggle;
break;

case 'O': /* --proxy-ssl-auto-client-cert */
if(curlinfo->features & CURL_VERSION_SSL)
config->proxy_ssl_auto_client_cert = toggle;
break;

case 'p': /* Pinned public key DER file */
GetStr(&config->pinnedpubkey, nextarg);
break;
Expand Down
6 changes: 6 additions & 0 deletions src/tool_help.c
Expand Up @@ -598,6 +598,9 @@ static const struct helptxt helptext[] = {
{" --proxy-ssl-allow-beast",
"Allow security flaw for interop for HTTPS proxy",
CURLHELP_PROXY | CURLHELP_TLS},
{" --proxy-ssl-auto-client-cert",
"Use auto client certificate for proxy (Schannel)",
CURLHELP_PROXY | CURLHELP_TLS},
{" --proxy-tls13-ciphers <ciphersuite list>",
"TLS 1.3 proxy cipher suites",
CURLHELP_PROXY | CURLHELP_TLS},
Expand Down Expand Up @@ -727,6 +730,9 @@ static const struct helptxt helptext[] = {
{" --ssl-allow-beast",
"Allow security flaw to improve interop",
CURLHELP_TLS},
{" --ssl-auto-client-cert",
"Use auto client certificate (Schannel)",
CURLHELP_TLS},
{" --ssl-no-revoke",
"Disable cert revocation checks (Schannel)",
CURLHELP_TLS},
Expand Down
21 changes: 16 additions & 5 deletions src/tool_operate.c
Expand Up @@ -1720,20 +1720,31 @@ static CURLcode single_transfer(struct GlobalConfig *global,

{
long mask =
(config->ssl_allow_beast ? CURLSSLOPT_ALLOW_BEAST : 0) |
(config->ssl_allow_beast ?
CURLSSLOPT_ALLOW_BEAST : 0) |
(config->ssl_no_revoke ?
CURLSSLOPT_NO_REVOKE : 0) |
(config->ssl_revoke_best_effort ?
CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
(config->native_ca_store ?
CURLSSLOPT_NATIVE_CA : 0) |
(config->ssl_no_revoke ? CURLSSLOPT_NO_REVOKE : 0);
(config->ssl_auto_client_cert ?
CURLSSLOPT_AUTO_CLIENT_CERT : 0);

if(mask)
my_setopt_bitmask(curl, CURLOPT_SSL_OPTIONS, mask);
}

if(config->proxy_ssl_allow_beast)
my_setopt(curl, CURLOPT_PROXY_SSL_OPTIONS,
(long)CURLSSLOPT_ALLOW_BEAST);
{
long mask =
(config->proxy_ssl_allow_beast ?
CURLSSLOPT_ALLOW_BEAST : 0) |
(config->proxy_ssl_auto_client_cert ?
CURLSSLOPT_AUTO_CLIENT_CERT : 0);

if(mask)
my_setopt_bitmask(curl, CURLOPT_PROXY_SSL_OPTIONS, mask);
}
}

if(config->path_as_is)
Expand Down
1 change: 1 addition & 0 deletions src/tool_setopt.c
Expand Up @@ -133,6 +133,7 @@ const struct NameValueUnsigned setopt_nv_CURLSSLOPT[] = {
NV(CURLSSLOPT_NO_PARTIALCHAIN),
NV(CURLSSLOPT_REVOKE_BEST_EFFORT),
NV(CURLSSLOPT_NATIVE_CA),
NV(CURLSSLOPT_AUTO_CLIENT_CERT),
NVEND,
};

Expand Down
1 change: 1 addition & 0 deletions src/tool_setopt.h
Expand Up @@ -76,6 +76,7 @@ extern const struct NameValueUnsigned setopt_nv_CURLHSTS[];
#define setopt_nv_CURLOPT_FTP_SSL_CCC setopt_nv_CURLFTPSSL_CCC
#define setopt_nv_CURLOPT_USE_SSL setopt_nv_CURLUSESSL
#define setopt_nv_CURLOPT_SSL_OPTIONS setopt_nv_CURLSSLOPT
#define setopt_nv_CURLOPT_PROXY_SSL_OPTIONS setopt_nv_CURLSSLOPT
#define setopt_nv_CURLOPT_NETRC setopt_nv_CURL_NETRC
#define setopt_nv_CURLOPT_PROTOCOLS setopt_nv_CURLPROTO
#define setopt_nv_CURLOPT_REDIR_PROTOCOLS setopt_nv_CURLPROTO
Expand Down

0 comments on commit 54e7475

Please sign in to comment.