You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
3.3 KiB
Python
90 lines
3.3 KiB
Python
from __future__ import print_function
|
|
|
|
import logging
|
|
import urllib2
|
|
|
|
import kerberos as krb
|
|
|
|
class GssapiAuthError(Exception):
|
|
"""raised on error during authentication process"""
|
|
|
|
import re
|
|
RGX = re.compile('(?:.*,)*\s*Negotiate\s*([^,]*),?', re.I)
|
|
|
|
def get_negociate_value(headers):
|
|
for authreq in headers.getheaders('www-authenticate'):
|
|
match = RGX.search(authreq)
|
|
if match:
|
|
return match.group(1)
|
|
|
|
class HTTPGssapiAuthHandler(urllib2.BaseHandler):
|
|
"""Negotiate HTTP authentication using context from GSSAPI"""
|
|
|
|
handler_order = 400 # before Digest Auth
|
|
|
|
def __init__(self):
|
|
self._reset()
|
|
|
|
def _reset(self):
|
|
self._retried = 0
|
|
self._context = None
|
|
|
|
def clean_context(self):
|
|
if self._context is not None:
|
|
krb.authGSSClientClean(self._context)
|
|
|
|
def http_error_401(self, req, fp, code, msg, headers):
|
|
try:
|
|
if self._retried > 5:
|
|
raise urllib2.HTTPError(req.get_full_url(), 401,
|
|
"negotiate auth failed", headers, None)
|
|
self._retried += 1
|
|
logging.debug('gssapi handler, try %s' % self._retried)
|
|
negotiate = get_negociate_value(headers)
|
|
if negotiate is None:
|
|
logging.debug('no negociate found in a www-authenticate header')
|
|
return None
|
|
logging.debug('HTTPGssapiAuthHandler: negotiate 1 is %r' % negotiate)
|
|
result, self._context = krb.authGSSClientInit("HTTP@%s" % req.get_host())
|
|
if result < 1:
|
|
raise GssapiAuthError("HTTPGssapiAuthHandler: init failed with %d" % result)
|
|
result = krb.authGSSClientStep(self._context, negotiate)
|
|
if result < 0:
|
|
raise GssapiAuthError("HTTPGssapiAuthHandler: step 1 failed with %d" % result)
|
|
client_response = krb.authGSSClientResponse(self._context)
|
|
logging.debug('HTTPGssapiAuthHandler: client response is %s...' % client_response[:10])
|
|
req.add_unredirected_header('Authorization', "Negotiate %s" % client_response)
|
|
server_response = self.parent.open(req)
|
|
negotiate = get_negociate_value(server_response.info())
|
|
if negotiate is None:
|
|
logging.warning('HTTPGssapiAuthHandler: failed to authenticate server')
|
|
else:
|
|
logging.debug('HTTPGssapiAuthHandler negotiate 2: %s' % negotiate)
|
|
result = krb.authGSSClientStep(self._context, negotiate)
|
|
if result < 1:
|
|
raise GssapiAuthError("HTTPGssapiAuthHandler: step 2 failed with %d" % result)
|
|
return server_response
|
|
except GssapiAuthError as exc:
|
|
logging.error(repr(exc))
|
|
finally:
|
|
self.clean_context()
|
|
self._reset()
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
# debug
|
|
import httplib
|
|
httplib.HTTPConnection.debuglevel = 1
|
|
httplib.HTTPSConnection.debuglevel = 1
|
|
# debug
|
|
import logging
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
# handle cookies
|
|
import cookielib
|
|
cj = cookielib.CookieJar()
|
|
ch = urllib2.HTTPCookieProcessor(cj)
|
|
# test with url sys.argv[1]
|
|
h = HTTPGssapiAuthHandler()
|
|
response = urllib2.build_opener(h, ch).open(sys.argv[1])
|
|
print('\nresponse: %s\n--------------\n' % response.code, response.info())
|