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
WinSSL sends client certificate automatically #2262
Comments
That thread is two issues which as far as I can tell are unrelated. The first issue is schannel (aka WinSSL) returning SEC_E_INVALID_TOKEN. The reporter in that thread failed to follow up in that case so I'm ignoring it. Also as I mentioned in the thread I had a similar issue that I think was a bug in schannel, as it was reproducible without libcurl. The second issue is, to sum it up, that the reporter's server requests a client certificate and schannel then automatically locates and responds with a certificate, and then the server replies with fatal alert TLS1_ALERT_BAD_CERTIFICATE, aka SEC_E_CERT_UNKNOWN. (You can read the full map of alerts -> schannel errors here). It is default behavior that schannel chooses the client cert to send automatically: "When the server requests client authentication, the client must send the server one of its certificates. By default, Schannel will, with no notification to the client, attempt to locate a client certificate and send it to the server. To disable this feature, clients specify ISC_REQ_USE_SUPPLIED_CREDS when calling the InitializeSecurityContext (Schannel) function. When this flag is specified, Schannel will return SEC_I_INCOMPLETE_CREDENTIALS to the client when the server requests authentication and the client has not previously supplied a certificate." We already use ISC_REQ_USE_SUPPLIED_CREDS but only if the server returns incomplete credentials: Lines 672 to 681 in d6c21c8
Also of note here is their assumption that the client must send one of its certificates. That may or may not be true, someone needs to check the RFC. I've seen replies with certificate count 0 so maybe it must send it only if one is available? Anyway, in this case one client certificate is available so that is sent to the server in response to its certificate request. The certificate sent doesn't seem to have anything to do with the server, according to one of the reporters in that thread @weshinsley. We may be able to offer a way to disable this behavior for example a CURLOPT_SSL_OPTIONS flag like CURLSSLOPT_NO_DEFAULT_CREDS that if on we use to set flag ISC_REQ_USE_SUPPLIED_CREDS at some point before schannel is notified of the client certificate request. I'm not sure if we are responsible here. If the server (vault - being used as an intranet server by the reporter) rejects an unknown client certificate fatally even if one is not required then one could argue that's the server's fault. /cc @richfitz @weshinsley |
Would logs from the server help identify if where the fault lies? |
@jay I agree with one could argue that's the server's fault, but the problem does not seem to appear for any browser (including IE) or any other libcurl SSL back-end on the same machine. So it would be nice if the default behavior were a bit more robust against picky servers. Perhaps in case of |
I took a look at RFC 5246 - The Transport Layer Security (TLS) Protocol Version 1.2. Section 7.4.4 says if the CA list provided by the server is empty (which is what's happening) then the client MAY send any certificate of the appropriate type (which is what's happening).
So we can add an "external arrangement", ie the flag NO_DEFAULT_CREDS. Section 7.4.6 allows the server to send a fatal alert or continue the handshake in response to the client's certificate message.
So we have a server that is asking for any certificate and then fatally terminating the handshake by saying the certificate is bad... I'm thinking the server should be at fault here.
You could check and see if you can spot where the certificate is rejected. And then maybe you can walk that back to something in the configuration. I don't suggest posting the entire log here because it may contain sensitive information.
Based on the info we have right now I'm hesitant to support this. IE is probably over compensating for server problems. We already have code to continue the connection, and that could probably be improved for other alerts such as unknown certificate. But in this case it's a fatal error. I'm just not convinced that we should retry on fatal error. |
Thanks for looking up the appropriate RFC sections.
Hmm the way I read it is that the server only lets the client know it MAY authenticate, if it wishes to identify itself. I don't think the intention here is to send a random cert. The current default behavior is actually a privacy concern. I do not expect curl to reveal my personal identity to any random server by default. For example in some countries you need to install a client cert to do your taxes online, but I certainly do not want this cert to be used for any other website. I don't understand how the user should set
Is there something similar for Windows to identify a client cert from the certificate store? |
This is the log from a failed request:
|
Whose intention. With an empty CA list given by the server the client MAY send any certificate for authentication in reply and that is what's happening: "By default, Schannel will, with no notification to the client, attempt to locate a client certificate and send it to the server." So I think the schannel intention is clear. The issue here is should we defer to that. Obviously the MS wording and the RFC differ in that the MS says the client must reply with one of its certificates whereas the RFC says the client must reply to the certificate request but that reply can be 0 certificates. If a cert is sent but the server doesn't recognize it then the server can continue without client auth or respond with a fatal alert which is why I think the onus is on the server.
Yes I agree but I also think it would be a breaking change now if we override the schannel default behavior. I'm not 100% against it I just think it's going to break transfers of people who have come to expect that the same way they do in IE.
There is no setting right now, if there's a cert it's sent automatically.
@ is not valid in PRINTABLESTRING so technically they're correct the string is invalid. As noted in that wikipedia and elsewhere on the web e-mails are put in commonName even though that is not correct. |
In case it's useful, I did some more testing today. I requested and got a new personal client certificate from Imperial - this one I'll call the GOOD certificate - it doesn't have @ in the PRINTABLESTRING, and the certificate path reports OK as far as the Imperial College Root. It doesn't have anything specifically to do with support.montagu.dide.ic.ac.uk:8200 - but the root certificate authority is .ic.ac.uk. I'll call my old Lyncpool cert the "BAD" one, because of problems with both those things. (If I only I had an UGLY certificate too...) Testing takes some care, as after a successful connection, some caching happens somewhere and I seem to get success no matter what I change. I found a local reboot cleared that. Also, some drag and drop procedures in certmgr.msc seem to not take effect until you push F5 to refresh, even when the result of the drag-drop is visible on screen. That one confused me a few times. So in the end, for each test, I set up the certificates, refresh, reboot, and do the R call... The variable is: which certificates are in my "Personal Certificates" folder, on my Win 10 desktop. Four tests: BAD certificate only: SEC_E_CERT_UNKNOWN This last one: does this means that all (well, both!) my client certs get sent to the server, and then a fatal error gets thrown if only one of them is bad? Edit - I tried the GOOD & BAD a few times to try and see if it was picking just one to send, but got consistent SEC_E_CERT_UNKNOWN. There's no "ordering" as far as I can tell in certmgr.msc - F5 sorts alphabetically. I also tried chronologically both orders to see if "most recently added"... but no difference. So it's either both get sent and it fails, or it consistently chooses to send my BAD certificate, via a criteria I don't know. (Or I've just been very unlucky each time) |
It can send any certificate since the server is not requesting a specific CA. Run Wireshark and look at the certificate details to see which certificate(s) it's sending. Check the certificate that is sent, for example from your older wireshark capture in the other thread:
|
Right - it's just sending one certificate each time - but it's always the bad one! |
Ok. We can assume MS doesn't think of the cert as bad if it's sending it. Though surely we would like to be able to stop it from doing that, but short of disabling auto credentials I don't know what else we can do. I think the way forward is one of:
As far as I know this is only an issue with WinSSL, no other backend is automatically choosing client certs is it? |
If we apply the principle of least surprise, I would expect the behavior to be as much as possible consistent with other libcurl tls back-ends. So that means disabling the auto credential behavior and making it opt-in via If the Schannel API does not provide a mechanism to use a particular cert (which I find very odd) perhaps it can support |
Here is a webpage that can be opened by R on linux/OSX (openssl) but not windows (winssl) "https://edoras.sdsu.edu/~jjfan/stat678/pbcWH.txt" |
@bathyg it seems to work fine here on windows. Do you have any custom certs installed in your windows certificate manager? |
- New libcurl ssl option value CURLSSLOPT_NO_DEFAULT_CREDS tells libcurl to not automatically locate and use a client certificate for authentication. - New curl tool options --ssl-no-default-creds and --proxy-ssl-no-default-creds map to CURLSSLOPT_NO_DEFAULT_CREDS. This option is only supported for Schannel (the native Windows SSL library). By default, Schannel will, with no notification to the client, attempt to locate a client certificate and send it to the server (when requested by the server). That could be considered a privacy violation and unexpected. Fixes curl#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 #xxxx
- 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_CREDS tells libcurl to automatically locate and use a client certificate for authentication, when requested by the server. - New curl tool options --ssl-auto-creds and --proxy-ssl-auto-creds map to CURLSSLOPT_AUTO_CREDS. 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 curl#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 #xxxx
To address this issue I've created two PRs. #6672 would leave auto credentials as the default and add an option to disable it I'm leaning towards #6673, please take feedback there. Edit: To work on this issue I used an nginx server with option New-SelfSignedCertificate -Type Custom -DnsName test -KeySpec Signature `
-Subject "CN=test" -KeyExportPolicy Exportable `
-HashAlgorithm sha256 -KeyLength 2048 `
-CertStoreLocation "Cert:\CurrentUser\My" `
-NotAfter (Get-Date).AddYears(100) `
-TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2") test-cert-with-client-auth.zip can be imported into the Personal Certificate store on Windows 7+. |
- 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
The following issue was reported in the repo for the libcurl bindings for the R programming language.
Earlier this year we switched the R curl package from openssl to winssl on windows. Several Windows users have since then complained about getting an error
schannel: next InitializeSecurityContext failed
for certain servers (usually intranet).The issue is difficult to reproduce but it seems to be caused by libcurl using an inappropriate client certificate when connecting over https to a server that has not requested a client certificate at all. Most httpd servers will simply ignore the client cert, but some servers (such as vault) will actually refuse the connection if they fail to validate the (unneeded) client cert.
The text was updated successfully, but these errors were encountered: