Skip to content
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

Various fixes and improvements: #6

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 38 additions & 70 deletions urllib_gssapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class AbstractSPNEGOAuthHandler:

def __init__(self):
"""Initialize an instance of a AbstractSPNEGOAuthHandler."""
self.retried = 0
self.pre_auth = False
self.context = None

neg_regex = re.compile(r'(?:.*,)*\s*Negotiate\s*([^,]*),?', re.I)
Expand All @@ -53,120 +53,88 @@ def negotiate_value(self, headers):
"""
authreqs = headers.get(self.auth_header).split(',')

if authreqs:
for authreq in authreqs:
mo = self.neg_regex.search(authreq)
if mo:
return b64decode(mo.group(1))
else:
log.debug("regex failed on: %s" % authreq)

else:
if not authreqs:
log.debug("%s header not found" % self.auth_header)
return None

return None
for authreq in authreqs:
mo = self.neg_regex.search(authreq)
if mo:
return b64decode(mo.group(1))
log.debug("regex failed on: %s" % authreq)

def generate_request_header(self, req, headers, neg_value):
self.retried += 1
log.debug("retry count: %d" % self.retried)
return None

host = urlparse(req.get_full_url()).netloc
log.debug("urlparse(req.get_full_url()).netloc returned %s" % host)
def generate_request_header(self, req, neg_value):
if not self.context:
host = urlparse(req.get_full_url()).netloc
log.debug("urlparse(req.get_full_url()).netloc returned %s" % host)

domain = host.rsplit(':', 1)[0]
domain = host.rsplit(':', 1)[0]

remote_name = gssapi.Name("HTTP@%s" % domain,
gssapi.NameType.hostbased_service)
self.context = gssapi.SecurityContext(usage="initiate",
name=remote_name)
log.debug("created GSSAPI context")
remote_name = gssapi.Name("HTTP@%s" % domain, gssapi.NameType.hostbased_service)
self.context = gssapi.SecurityContext(usage="initiate", name=remote_name)
log.debug("created GSSAPI context")

response = self.context.step(neg_value)
log.debug("successfully stepped context")
if not response: return None
return "Negotiate %s" % b64encode(response).decode()

def authenticate_server(self, headers):
neg_value = self.negotiate_value(headers)
if neg_value is None:
log.critical("mutual auth failed. No negotiate header")
return None

token = self.context.step(neg_value)
if token is not None:
log.critical(
"mutual auth failed: authGSSClientStep returned a token")
pass

def clean_context(self):
if self.context is not None:
log.debug("cleaning context")
self.context = None

def http_error_auth_reqed(self, host, req, headers):
neg_value = self.negotiate_value(headers) # Check for auth_header
if neg_value is not None:
if not self.retried > 0:
return self.retry_http_gssapi_auth(req, headers, neg_value)
else:
return None
else:
self.retried = 0

def retry_http_gssapi_auth(self, req, headers, neg_value):
try:
neg_hdr = self.generate_request_header(req, headers, neg_value)

if neg_hdr is None:
log.debug("neg_hdr was None")
return None
self.context = None
rcode = self.auth_code
neg_value = self.negotiate_value(headers)
if neg_value is None: return None

while (rcode == self.auth_code):
neg_hdr = self.generate_request_header(req, neg_value)
req.add_unredirected_header(self.authz_header, neg_hdr)
if req.data and hasattr(req.data, 'seek'): req.data.seek(0)
resp = self.parent.open(req)
rcode = resp.getcode()
neg_value = self.negotiate_value(resp.info())

if resp.getcode() != 200:
self.authenticate_server(resp.info())
self.context.step(neg_value)
self.pre_auth = self.context.complete

return resp

except gssapi.exceptions.GSSError as e:
self.clean_context()
self.retried = 0
log.critical("GSSAPI Error: %s" % e.gen_message())
return None

self.clean_context()
self.retried = 0
return resp

def http_request(self, req):
if self.pre_auth:
self.context = None
neg_hdr = self.generate_request_header(req, None)
req.add_unredirected_header(self.authz_header, neg_hdr)
return req

class ProxySPNEGOAuthHandler(BaseHandler, AbstractSPNEGOAuthHandler):
"""SPNEGO Negotiation handler for HTTP proxy auth
"""

authz_header = 'Proxy-Authorization'
auth_header = 'proxy-authenticate'
auth_code = 407

handler_order = 480 # before Digest auth

def http_error_407(self, req, fp, code, msg, headers):
log.debug("inside http_error_407")
host = urlparse(req.get_full_url()).netloc
retry = self.http_error_auth_reqed(host, req, headers)
self.retried = 0
return retry


class HTTPSPNEGOAuthHandler(BaseHandler, AbstractSPNEGOAuthHandler):
"""SPNEGO Negotiation handler for HTTP auth
"""

authz_header = 'Authorization'
auth_header = 'www-authenticate'
auth_code = 401

handler_order = 480 # before Digest auth

def http_error_401(self, req, fp, code, msg, headers):
log.debug("inside http_error_401")
host = urlparse(req.get_full_url()).netloc
retry = self.http_error_auth_reqed(host, req, headers)
self.retried = 0
return retry