-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
gh-80192: Use windows api if ssl cert verification fails #127622
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
base: main
Are you sure you want to change the base?
Conversation
This avoids rejecting certificates that should be accepted, see python#80192
Such change requires some tests at least I guess. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this needs a test. FYI, test_ssl
is failing.
@@ -0,0 +1 @@ | |||
Fixed valid ssl certificates being rejected. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a reference:
Fixed valid ssl certificates being rejected. | |
Fixed valid :mod:`ssl` certificates being rejected. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reference added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would see a broader text, e.g. the PR verifies certifications rejected by OpenSSL by additional calls to Windows specific API functions.
The exact proposed text is definitely not fine, but you have got my idea.
return NULL; | ||
} | ||
|
||
cert_bytes = PyMem_RawMalloc(cert_bytes_length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to use pymalloc (PyMem_Malloc
) here? Typically, we only need RawMalloc
if we plan on using it inside Py_BEGIN_ALLOW_THREADS
areas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using PyMem_Malloc I got the following error in my sample program:
Fatal Python error: _PyMem_DebugMalloc: Python memory allocator called without holding the GIL
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured. It might be worth adding a comment mentioning that the GIL isn't held (so no Python APIs can get called).
Modules/_ssl.c
Outdated
case CERT_E_WRONG_USAGE: | ||
case CERT_E_CRITICAL: | ||
case CERT_E_PURPOSE: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a chance we need to use _Py_FALLTHROUGH
here, but it looks like build isn't complaining. If it does start failing feel free to use that.
CERT_E_UNTRUSTEDROOT: A certification chain processed correctly but terminated in a root certificate that is not trusted by the trust provider. X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: The issuer certificate could not be found: this occurs if the issuer certificate of an untrusted certificate cannot be found. - https://docs.openssl.org/master/man3/X509_STORE_CTX_get_error/#error-codes - https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_policy_status#members
Ok, I'll have to look into how to create a sample certificate chain that reproduces the problem. |
I don't think you can reliably test this other than by deleting a root CA, at least not for the default behaviour. You might need to refactor things to be able to call the verification function directly (from tests), and also to validate that it has been set on SSLContext objects. Also, just FYI, historically our SSL experts have roundly rejected overriding the verification callback, which is why we haven't done this (it's not like we were unaware of this approach 😉 ). I don't recall the details, but it might be worth someone doing the archaeology on GitHub and perhaps the old email archives at mail.python.org to see what the specifics were. For my part, I know that many projects have documented the OpenSSL environment variables that affect certificate validation. If possible, we should try and ensure those work (possibly just by checking if they're set and if so, don't override the callback). |
@@ -0,0 +1 @@ | |||
Fixed valid :mod:`ssl` certificates being rejected. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with Andrew's comment - this needs to be significantly expanded, because this is a very impactful change.
We also would need a section in Doc/whatsnew/3.14.rst
explaining the impact and options for workarounds.
We might also need to use a deprecation period to turn this on by default, though part of me would prefer not to.
I wonder if @sethmlarson has thoughts? |
@zooba I think the latest discussion on the topic was this thread on DPO (cc @woodruffw). In general I'm in favor of moving to using system certificate stores by default, but I think it should happen through the PEP process to get the API right. |
Agreed with this. (Tangentially, I think the approach in this patch is unfortunately hard to follow -- if the goal is to inject the Windows cert store into the path validation flow, then IMO that should be done by loading the store and injecting it into the OpenSSL equivalent, rather than adding a verification callback and calling the equivalent WinCrypt APIs in situ. The latter effectively means that two path validation flows are running at the same time!) |
Unfortunately, this is precisely what doesn't work, because Windows will update the store dynamically based on validation requests. So you can't just read it into OpenSSL and be done (or this wouldn't continue being raised as a problem for the last 15+ years). IMHO, I'd love to drop OpenSSL on Windows entirely in favour of the native TLS support (unless you specifically want it, in which case you can use the native TLS support to install it). I suspect I'm the only person who thinks in those terms though, and everyone else assumes that OpenSSL is inevitable. I don't have the bandwidth outside of work to build the library, unfortunately, and I don't have support from work to release it publicly, so probably people should stop asking my opinion about this, since they never seem to like it (and in exchange, I'll stop paying attention to users who hit this issue 😉 ) |
Oh, huh. Is this because they're doing a variant of the Chrome intermediate cache? I wasn't aware any OS-native TLS stacks were doing that! Given that, I suspect there are two slightly different goals here: there's "use the OS root of trust so that Python and PyPI don't operate informal root programs anymore," and there's "use the OS root of trust so that Python's HTTPS behavior aligns more closely with browser agents in terms of fixing real-world broken chains."
I'm a big fan of this! I'd really love it if PEP 748 (or a replacement) could fit this exactly, although my colleagues and I are unfortunately in a similar position around bandwidth. |
This is the reason why Truststore doesn't simply load certificates into an OpenSSL store, we use Windows' validation. Doesn't macOS do the same? |
I'm not sure -- I'm aware of this technique in the browser world for handling broken/misconfigured HTTPS servers, but I wasn't aware of it being employed within the OS validator itself (I was under the impression that Chrome et al. did it by maintaining a browser-side intermediate cache, which they then fed into the system APIs, with the latter remaining stateless). Looking at this some more, I'm not actually sure that's what's happening in the Windows case: I think the Windows system validator might be doing AIA chasing instead, i.e. fetching the intermediate from the |
Windows used to distribute root CA updates through Windows Update, so my assumption is that it's a replica of that process, just more finely grained. Probably also works as an alternative to CRLs. The Windows cert store isn't just used for TLS, so the design is going to be less biased towards online scenarios, as it has to include software and driver certifications as well. Though it wouldn't surprise me if most code signing CAs are included out of the box, or else OEMs/users would have a terrible time adding drivers. |
This avoids rejecting certificates that should be accepted, see #80192.
Here is the relevant openssl documentation: https://docs.openssl.org/master/man3/SSL_CTX_set_verify/.
Also, here are the windows api docs:
I've been testing manually by deleting
DigiCert Global Root G2
fromThird Party Root Certificates
in the user certificate store (this is safe, the certificate will be added back automatically), and running the following code:The main branch errors with:
Whereas with this patch, the certificate is verified and the connection is established.