Index: python-pip-1.0/pip/download.py =================================================================== --- python-pip-1.0.orig/pip/download.py 2012-06-20 01:53:13.114394158 -0700 +++ python-pip-1.0/pip/download.py 2012-06-20 02:35:16.568152013 -0700 @@ -15,6 +15,85 @@ from pip.log import logger +# Monkey-patch httplib.HTTPSConnection so that it verifies SSL +# certificates +try: + import httplib + import socket + import ssl +except ImportError: + pass +else: + class CertificateError(ValueError): + pass + + def _dnsname_to_pat(dn): + pats = [] + for frag in dn.split(r'.'): + if frag == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + else: + # Otherwise, '*' matches any dotless fragment. + frag = re.escape(frag) + pats.append(frag.replace(r'\*', '[^.]*')) + return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + + def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 + rules are mostly followed, but IP addresses are not accepted + for *hostname*. + + CertificateError is raised on failure. On success, the + function returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_to_pat(value).match(hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_to_pat(value).match(hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") + + def _new_https_connect(self): + sock = socket.create_connection((self.host, self.port), + self.timeout, self.source_address) + if self._tunnel_host: + self.sock = sock + self._tunnel() + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, + cert_reqs=ssl.CERT_REQUIRED, + ca_certs='/etc/ssl/certs/ca-certificates.crt') + match_hostname(self.sock.getpeercert(), self.host) + httplib.HTTPSConnection.connect = _new_https_connect + + __all__ = ['xmlrpclib_transport', 'get_file_content', 'urlopen', 'is_url', 'url_to_path', 'path_to_url', 'path_to_url2', 'geturl', 'is_archive_file', 'unpack_vcs_link',