urllib2ext.py 3.3 KB

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