-
Notifications
You must be signed in to change notification settings - Fork 22
Support GSSAPI channel bindings #57
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
@cryptomilk can you test if this patch works for you? I am not absolutely certain it will because it is largely untested. In order to get the cert locally I had to set stream=True on the requests.get call in order to preserve the HTTPConnection struct which holds the ssl scoket necessary to retrieve the peer cert. |
@jborean93 before merging we should wait for @cryptomilk to confirm it works as needed, we know there is still some issue in the test environment we are trying to figure out. The Windows Server seem not fully happy with this yet, although it is doing the same thing request-kerberos also does ... |
It would be great to have a release as soon as channel bindings support is added. |
This change adds support for GSSAPI channel bindings, which helps to protect against man-in-the-middle relay attacks by tying the authentication to the underlying secure channel. A `channel_bindings` parameter is added to `HTTPSPNEGOAuth`. When set to 'tls- server-end-point', the server's TLS certificate is retrieved from the socket, hashed, and used to create the GSSAPI channel bindings. This feature requires the `cryptography` library as an optional dependency. If it's not available, channel bindings cannot be used and a warning is logged. Co-authored-by: Gemini <[email protected]> Signed-off-by: Simo Sorce <[email protected]>
Signed-off-by: Simo Sorce <[email protected]>
Signed-off-by: Simo Sorce <[email protected]>
I can have a test against a real env tomorrow with CBT enforced to verify but the impl at a glance looks good. |
I've tested it against a Windows Server 2025 which requires it and it works. This is my WIP branch: https://github.com/cryptomilk/cepces/tree/asn-gssapi |
I've tested this against just a plain pip install I've done this in diff --git a/src/requests_gssapi/gssapi_.py b/src/requests_gssapi/gssapi_.py
index cb24378..f3556aa 100644
--- a/src/requests_gssapi/gssapi_.py
+++ b/src/requests_gssapi/gssapi_.py
@@ -154,18 +154,21 @@ class HTTPSPNEGOAuth(AuthBase):
gss_cb = None
if self.channel_bindings == "tls-server-end-point":
+ try:
+ sock = response.raw._fp.fp.raw._sock
+ except AttributeError:
+ sock = None
+
if is_preemptive:
raise SPNEGOExchangeError(
"channel_bindings were requested, but are unavailable for opportunistic authentication"
)
# The 'connection' attribute on raw is a public urllib3 API
# and can be None if the connection has been released.
- elif getattr(response.raw, "connection", None) and getattr(response.raw.connection, "sock", None):
+ elif sock:
# Defer import so it's not a hard dependency.
from cryptography import x509
- sock = response.raw.connection.sock
-
der_cert = sock.getpeercert(binary_form=True)
cert = x509.load_der_x509_certificate(der_cert)
hash = cert.signature_hash_algorithm |
@jborean93 Why would you use internal undocumented attributes, when there are perfectly valid public ones? |
Because at least the ones in the PR right now seem to be set to I also only just saw your original comments saying I’m not saying we should use my example, if yours works with |
stream=True is needed only if you need access to the socket after the get operation is completed, but it should not be needed during a gssapi request as the gssapi operation happens after we connect to the server but before we are done with the negotiate, so for request-gssapi it should not be needed and our tests seem to confirm that. |
I’ll have a dig in tomorrow but the PR as it stands didn’t have a socket to get the cert from and thus failed to authenticate. I’m not sure whether it’s due to a different backend, the lack of |
I wonder if it could be an HTTP 1.0 vs HTTP 1.1 thing maybe? But POST may also be the issue here, I wonder if we have a way to interject a capturing event right during the request, to ensure the sock is present ... |
My manual tests also used |
Maybe we should recommend use of Sessions which will cause urllib3 to use connection pooling and keep connections open ? |
Looking at the cepces code it does requests.post() using HTTP 1.1.
|
Debug log, cepces with channel bindings: https://cpaste.org/?71dafeb41c002b98#Hc1u3vQMrCtZCqzgWLfYb1mfdAzFsZvagDAkW1HnXLaU |
Maybe ask |
What about this: cryptomilk@bdadc2f |
@jborean93 could you try with commit: cryptomilk@bdadc2f ? |
It works but seems to be using non-public attributes and now also relies on methods being called in certain orders internally inside requests. The diff I shared in #57 (comment) also uses non-public attributes but isn't reliant on any patching of functions inside requests or that those functions are called in a certain way for the connection/cert to be present. It certainly would be nice if there was a public way to get this but it seems like requests is in the exact same scenario from 8 years ago when I first implemented support for CB in requests-kerberos and went the non-public way. |
This change adds support for GSSAPI channel bindings, which helps to protect against man-in-the-middle relay attacks by tying the authentication to the underlying secure channel.
A
channel_bindings
parameter is added toHTTPSPNEGOAuth
. When set to 'tls- server-end-point', the server's TLS certificate is retrieved from the socket, hashed, and used to create the GSSAPI channel bindings.This feature requires the
cryptography
library as an optional dependency. If it's not available, channel bindings cannot be used and a warning is logged.Fixes #56