1import logging 2import urllib2 3 4import kerberos as krb 5 6 7import re 8 9RGX = re.compile(r"(?:.*,)*\s*Negotiate\s*([^,]*),?", re.I) 10 11 12class GssapiAuthError(Exception): 13 """raised on error during authentication process""" 14 15 16def get_negociate_value(headers): 17 for authreq in headers.getheaders("www-authenticate"): 18 match = RGX.search(authreq) 19 if match: 20 return match.group(1) 21 22 23class HTTPGssapiAuthHandler(urllib2.BaseHandler): 24 """Negotiate HTTP authentication using context from GSSAPI""" 25 26 handler_order = 400 # before Digest Auth 27 28 def __init__(self): 29 self._reset() 30 31 def _reset(self): 32 self._retried = 0 33 self._context = None 34 35 def clean_context(self): 36 if self._context is not None: 37 krb.authGSSClientClean(self._context) 38 39 def http_error_401(self, req, fp, code, msg, headers): 40 try: 41 if self._retried > 5: 42 raise urllib2.HTTPError( 43 req.get_full_url(), 401, "negotiate auth failed", headers, None 44 ) 45 self._retried += 1 46 logging.debug("gssapi handler, try %s" % self._retried) 47 negotiate = get_negociate_value(headers) 48 if negotiate is None: 49 logging.debug("no negociate found in a www-authenticate header") 50 return None 51 logging.debug("HTTPGssapiAuthHandler: negotiate 1 is %r" % negotiate) 52 result, self._context = krb.authGSSClientInit("HTTP@%s" % req.get_host()) 53 if result < 1: 54 raise GssapiAuthError("HTTPGssapiAuthHandler: init failed with %d" % result) 55 result = krb.authGSSClientStep(self._context, negotiate) 56 if result < 0: 57 raise GssapiAuthError("HTTPGssapiAuthHandler: step 1 failed with %d" % result) 58 client_response = krb.authGSSClientResponse(self._context) 59 logging.debug("HTTPGssapiAuthHandler: client response is %s..." % client_response[:10]) 60 req.add_unredirected_header("Authorization", "Negotiate %s" % client_response) 61 server_response = self.parent.open(req) 62 negotiate = get_negociate_value(server_response.info()) 63 if negotiate is None: 64 logging.warning("HTTPGssapiAuthHandler: failed to authenticate server") 65 else: 66 logging.debug("HTTPGssapiAuthHandler negotiate 2: %s" % negotiate) 67 result = krb.authGSSClientStep(self._context, negotiate) 68 if result < 1: 69 raise GssapiAuthError("HTTPGssapiAuthHandler: step 2 failed with %d" % result) 70 return server_response 71 except GssapiAuthError as exc: 72 logging.error(repr(exc)) 73 finally: 74 self.clean_context() 75 self._reset() 76 77 78if __name__ == "__main__": 79 import sys 80 81 # debug 82 import httplib 83 84 httplib.HTTPConnection.debuglevel = 1 85 httplib.HTTPSConnection.debuglevel = 1 86 87 logging.basicConfig(level=logging.DEBUG) 88 # handle cookies 89 import cookielib 90 91 cj = cookielib.CookieJar() 92 ch = urllib2.HTTPCookieProcessor(cj) 93 # test with url sys.argv[1] 94 h = HTTPGssapiAuthHandler() 95 response = urllib2.build_opener(h, ch).open(sys.argv[1]) 96 print("\nresponse: %s\n--------------\n" % response.code, response.info()) 97