From 1ede418b98ab6a47a93a8ee40cde92082dd5023b Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Wed, 8 Feb 2023 17:15:40 -0500 Subject: [PATCH] Make channel binding data per-host Previously, once you successfully generated channel binding data for one host, it would be stored in self.cbt_struct, and self.cbt_binding_tried would be set to True, preventing ever recalculating self.cbt_struct. This means that once you used an HTTPKerberosAuth object to talk to one host, it would keep sending that channel binding data to other hosts, resulting in failures for servers that enforce channel binding. Instead, store CBTs in a per-host dictionary as we do with GSSAPI contexts. --- requests_kerberos/kerberos_.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/requests_kerberos/kerberos_.py b/requests_kerberos/kerberos_.py index 7c0162a..5f8f250 100644 --- a/requests_kerberos/kerberos_.py +++ b/requests_kerberos/kerberos_.py @@ -183,8 +183,7 @@ def __init__( # Set the CBT values populated after the first response self.send_cbt = send_cbt - self.cbt_binding_tried = False - self.cbt_struct = None + self._cbts = {} def generate_request_header(self, response, host, is_preemptive=False): """ @@ -214,7 +213,7 @@ def generate_request_header(self, response, host, is_preemptive=False): username=self.principal, hostname=kerb_host, service=self.service, - channel_bindings=self.cbt_struct, + channel_bindings=self._cbts.get(host, None), context_req=gssflags, protocol="kerberos", ) @@ -371,16 +370,18 @@ def handle_response(self, response, **kwargs): num_407s = kwargs.pop('num_407s', 0) # Check if we have already tried to get the CBT data value - if not self.cbt_binding_tried and self.send_cbt: + if self.send_cbt: + host = urlparse(response.url).hostname # If we haven't tried, try getting it now - cbt_application_data = _get_channel_bindings_application_data(response) - if cbt_application_data: - self.cbt_struct = spnego.channel_bindings.GssChannelBindings( - application_data=cbt_application_data, - ) - - # Regardless of the result, set tried to True so we don't waste time next time - self.cbt_binding_tried = True + if host not in self._cbts: + cbt_application_data = _get_channel_bindings_application_data(response) + if cbt_application_data: + self._cbts[host] = spnego.channel_bindings.GssChannelBindings( + application_data=cbt_application_data, + ) + else: + # Store None so we don't waste time next time + self._cbts[host] = None if self.pos is not None: # Rewind the file position indicator of the body to where