diff options
Diffstat (limited to 'requests/auth.py')
-rw-r--r-- | requests/auth.py | 54 |
1 files changed, 35 insertions, 19 deletions
diff --git a/requests/auth.py b/requests/auth.py index 30529e2..6664cd8 100644 --- a/requests/auth.py +++ b/requests/auth.py @@ -16,6 +16,7 @@ import logging from base64 import b64encode from .compat import urlparse, str +from .cookies import extract_cookies_to_jar from .utils import parse_dict_header log = logging.getLogger(__name__) @@ -63,6 +64,7 @@ class HTTPDigestAuth(AuthBase): self.last_nonce = '' self.nonce_count = 0 self.chal = {} + self.pos = None def build_digest_header(self, method, url): @@ -77,7 +79,7 @@ class HTTPDigestAuth(AuthBase): else: _algorithm = algorithm.upper() # lambdas assume digest modules are imported at the top level - if _algorithm == 'MD5': + if _algorithm == 'MD5' or _algorithm == 'MD5-SESS': def md5_utf8(x): if isinstance(x, str): x = x.encode('utf-8') @@ -89,7 +91,7 @@ class HTTPDigestAuth(AuthBase): x = x.encode('utf-8') return hashlib.sha1(x).hexdigest() hash_utf8 = sha_utf8 - # XXX MD5-sess + KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) if hash_utf8 is None: @@ -105,23 +107,28 @@ class HTTPDigestAuth(AuthBase): A1 = '%s:%s:%s' % (self.username, realm, self.password) A2 = '%s:%s' % (method, path) + HA1 = hash_utf8(A1) + HA2 = hash_utf8(A2) + + if nonce == self.last_nonce: + self.nonce_count += 1 + else: + self.nonce_count = 1 + ncvalue = '%08x' % self.nonce_count + s = str(self.nonce_count).encode('utf-8') + s += nonce.encode('utf-8') + s += time.ctime().encode('utf-8') + s += os.urandom(8) + + cnonce = (hashlib.sha1(s).hexdigest()[:16]) + noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, HA2) + if _algorithm == 'MD5-SESS': + HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce)) + if qop is None: - respdig = KD(hash_utf8(A1), "%s:%s" % (nonce, hash_utf8(A2))) + respdig = KD(HA1, "%s:%s" % (nonce, HA2)) elif qop == 'auth' or 'auth' in qop.split(','): - if nonce == self.last_nonce: - self.nonce_count += 1 - else: - self.nonce_count = 1 - - ncvalue = '%08x' % self.nonce_count - s = str(self.nonce_count).encode('utf-8') - s += nonce.encode('utf-8') - s += time.ctime().encode('utf-8') - s += os.urandom(8) - - cnonce = (hashlib.sha1(s).hexdigest()[:16]) - noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, hash_utf8(A2)) - respdig = KD(hash_utf8(A1), noncebit) + respdig = KD(HA1, noncebit) else: # XXX handle auth-int. return None @@ -138,13 +145,17 @@ class HTTPDigestAuth(AuthBase): if entdig: base += ', digest="%s"' % entdig if qop: - base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce) + base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce) return 'Digest %s' % (base) def handle_401(self, r, **kwargs): """Takes the given response and tries digest-auth, if needed.""" + if self.pos is not None: + # Rewind the file position indicator of the body to where + # it was to resend the request. + r.request.body.seek(self.pos) num_401_calls = getattr(self, 'num_401_calls', 1) s_auth = r.headers.get('www-authenticate', '') @@ -159,7 +170,8 @@ class HTTPDigestAuth(AuthBase): r.content r.raw.release_conn() prep = r.request.copy() - prep.prepare_cookies(r.cookies) + extract_cookies_to_jar(prep._cookies, r.request, r.raw) + prep.prepare_cookies(prep._cookies) prep.headers['Authorization'] = self.build_digest_header( prep.method, prep.url) @@ -176,5 +188,9 @@ class HTTPDigestAuth(AuthBase): # If we have a saved nonce, skip the 401 if self.last_nonce: r.headers['Authorization'] = self.build_digest_header(r.method, r.url) + try: + self.pos = r.body.tell() + except AttributeError: + pass r.register_hook('response', self.handle_401) return r |