[Python-checkins] python/dist/src/Lib urllib2.py,1.53.6.1,1.53.6.2

anthonybaxter at users.sourceforge.net anthonybaxter at users.sourceforge.net
Tue Nov 4 08:53:40 EST 2003


Update of /cvsroot/python/python/dist/src/Lib
In directory sc8-pr-cvs1:/tmp/cvs-serv1660
Modified Files:
 Tag: release23-maint
	urllib2.py 
Log Message:
backport of 1.56
Apply patch 823328 -- support for rfc 2617 digestion authentication.
This fixes the current (broken) digest auth code.
Index: urllib2.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/urllib2.py,v
retrieving revision 1.53.6.1
retrieving revision 1.53.6.2
diff -C2 -d -r1.53.6.1 -r1.53.6.2
*** urllib2.py	20 Oct 2003 14:34:46 -0000	1.53.6.1
--- urllib2.py	4 Nov 2003 13:53:38 -0000	1.53.6.2
***************
*** 88,107 ****
 # check digest against correct (i.e. non-apache) implementation
 
! import socket
 import httplib
 import inspect
- import re
- import base64
- import urlparse
 import md5
 import mimetypes
 import mimetools
 import rfc822
! import ftplib
 import sys
 import time
! import os
! import gopherlib
! import posixpath
 
 try:
--- 88,109 ----
 # check digest against correct (i.e. non-apache) implementation
 
! import base64
! import ftplib
! import gopherlib
 import httplib
 import inspect
 import md5
 import mimetypes
 import mimetools
+ import os
+ import posixpath
+ import random
+ import re
 import rfc822
! import sha
! import socket
 import sys
 import time
! import urlparse
 
 try:
***************
*** 110,119 ****
 from StringIO import StringIO
 
- try:
- import sha
- except ImportError:
- # need 1.5.2 final
- sha = None
- 
 # not sure how many of these need to be gotten rid of
 from urllib import unwrap, unquote, splittype, splithost, \
--- 112,115 ----
***************
*** 121,131 ****
 splitattr, ftpwrapper, noheaders
 
! # support for proxies via environment variables
! from urllib import getproxies
! 
! # support for FileHandler
! from urllib import localhost, url2pathname
 
! __version__ = "2.0a1"
 
 _opener = None
--- 117,124 ----
 splitattr, ftpwrapper, noheaders
 
! # support for FileHandler, proxies via environment variables
! from urllib import localhost, url2pathname, getproxies
 
! __version__ = "2.1"
 
 _opener = None
***************
*** 677,681 ****
--- 670,697 ----
 
 
+ def randombytes(n):
+ """Return n random bytes."""
+ # Use /dev/urandom if it is available. Fall back to random module
+ # if not. It might be worthwhile to extend this function to use
+ # other platform-specific mechanisms for getting random bytes.
+ if os.path.exists("/dev/urandom"):
+ f = open("/dev/urandom")
+ s = f.read(n)
+ f.close()
+ return s
+ else:
+ L = [chr(random.randrange(0, 256)) for i in range(n)]
+ return "".join(L)
+ 
 class AbstractDigestAuthHandler:
+ # Digest authentication is specified in RFC 2617.
+ 
+ # XXX The client does not inspect the Authentication-Info header
+ # in a successful response.
+ 
+ # XXX It should be possible to test this implementation against
+ # a mock server that just generates a static set of challenges.
+ 
+ # XXX qop="auth-int" supports is shaky
 
 def __init__(self, passwd=None):
***************
*** 684,694 ****
 self.passwd = passwd
 self.add_password = self.passwd.add_password
 
! def http_error_auth_reqed(self, authreq, host, req, headers):
! authreq = headers.get(self.auth_header, None)
 if authreq:
! kind = authreq.split()[0]
! if kind == 'Digest':
 return self.retry_http_digest_auth(req, authreq)
 
 def retry_http_digest_auth(self, req, auth):
--- 700,728 ----
 self.passwd = passwd
 self.add_password = self.passwd.add_password
+ self.retried = 0
+ self.nonce_count = 0
 
! def reset_retry_count(self):
! self.retried = 0
! 
! def http_error_auth_reqed(self, auth_header, host, req, headers):
! authreq = headers.get(auth_header, None)
! if self.retried > 5:
! # Don't fail endlessly - if we failed once, we'll probably
! # fail a second time. Hm. Unless the Password Manager is
! # prompting for the information. Crap. This isn't great
! # but it's better than the current 'repeat until recursion
! # depth exceeded' approach <wink>
! raise HTTPError(req.get_full_url(), 401, "digest auth failed", 
! headers, None)
! else:
! self.retried += 1
 if authreq:
! scheme = authreq.split()[0]
! if scheme.lower() == 'digest':
 return self.retry_http_digest_auth(req, authreq)
+ else:
+ raise ValueError("AbstractDigestAuthHandler doesn't know "
+ "about %s"%(scheme))
 
 def retry_http_digest_auth(self, req, auth):
***************
*** 704,711 ****
--- 738,756 ----
 return resp
 
+ def get_cnonce(self, nonce):
+ # The cnonce-value is an opaque
+ # quoted string value provided by the client and used by both client
+ # and server to avoid chosen plaintext attacks, to provide mutual
+ # authentication, and to provide some message integrity protection.
+ # This isn't a fabulous effort, but it's probably Good Enough.
+ dig = sha.new("%s:%s:%s:%s" % (self.nonce_count, nonce, time.ctime(),
+ randombytes(8))).hexdigest()
+ return dig[:16]
+ 
 def get_authorization(self, req, chal):
 try:
 realm = chal['realm']
 nonce = chal['nonce']
+ qop = chal.get('qop')
 algorithm = chal.get('algorithm', 'MD5')
 # mod_digest doesn't send an opaque, even though it isn't
***************
*** 719,724 ****
 return None
 
! user, pw = self.passwd.find_user_password(realm,
! req.get_full_url())
 if user is None:
 return None
--- 764,768 ----
 return None
 
! user, pw = self.passwd.find_user_password(realm, req.get_full_url())
 if user is None:
 return None
***************
*** 734,738 ****
 # XXX selector: what about proxies and full urls
 req.get_selector())
! respdig = KD(H(A1), "%s:%s" % (nonce, H(A2)))
 # XXX should the partial digests be encoded too?
 
--- 778,793 ----
 # XXX selector: what about proxies and full urls
 req.get_selector())
! if qop == 'auth':
! self.nonce_count += 1
! ncvalue = '%08x' % self.nonce_count
! cnonce = self.get_cnonce(nonce)
! noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2))
! respdig = KD(H(A1), noncebit)
! elif qop is None:
! respdig = KD(H(A1), "%s:%s" % (nonce, H(A2)))
! else:
! # XXX handle auth-int.
! pass
! 
 # XXX should the partial digests be encoded too?
 
***************
*** 746,749 ****
--- 801,806 ----
 if algorithm != 'MD5':
 base = base + ', algorithm="%s"' % algorithm
+ if qop:
+ base = base + ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce)
 return base
 
***************
*** 751,759 ****
 # lambdas assume digest modules are imported at the top level
 if algorithm == 'MD5':
! H = lambda x, e=encode_digest:e(md5.new(x).digest())
 elif algorithm == 'SHA':
! H = lambda x, e=encode_digest:e(sha.new(x).digest())
 # XXX MD5-sess
! KD = lambda s, d, H=H: H("%s:%s" % (s, d))
 return H, KD
 
--- 808,816 ----
 # lambdas assume digest modules are imported at the top level
 if algorithm == 'MD5':
! H = lambda x: md5.new(x).hexdigest()
 elif algorithm == 'SHA':
! H = lambda x: sha.new(x).hexdigest()
 # XXX MD5-sess
! KD = lambda s, d: H("%s:%s" % (s, d))
 return H, KD
 
***************
*** 774,778 ****
 def http_error_401(self, req, fp, code, msg, headers):
 host = urlparse.urlparse(req.get_full_url())[1]
! self.http_error_auth_reqed('www-authenticate', host, req, headers)
 
 
--- 831,838 ----
 def http_error_401(self, req, fp, code, msg, headers):
 host = urlparse.urlparse(req.get_full_url())[1]
! retry = self.http_error_auth_reqed('www-authenticate', 
! host, req, headers)
! self.reset_retry_count()
! return retry
 
 
***************
*** 783,798 ****
 def http_error_407(self, req, fp, code, msg, headers):
 host = req.get_host()
! self.http_error_auth_reqed('proxy-authenticate', host, req, headers)
! 
! 
! def encode_digest(digest):
! hexrep = []
! for c in digest:
! n = (ord(c) >> 4) & 0xf
! hexrep.append(hex(n)[-1])
! n = ord(c) & 0xf
! hexrep.append(hex(n)[-1])
! return ''.join(hexrep)
! 
 
 class AbstractHTTPHandler(BaseHandler):
--- 843,850 ----
 def http_error_407(self, req, fp, code, msg, headers):
 host = req.get_host()
! retry = self.http_error_auth_reqed('proxy-authenticate', 
! host, req, headers)
! self.reset_retry_count()
! return retry
 
 class AbstractHTTPHandler(BaseHandler):


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /