Path: blob/master/venv/Lib/site-packages/setuptools/ssl_support.py
811 views
import os1import socket2import atexit3import re4import functools56from setuptools.extern.six.moves import urllib, http_client, map, filter78from pkg_resources import ResolutionError, ExtractionError910try:11import ssl12except ImportError:13ssl = None1415__all__ = [16'VerifyingHTTPSHandler', 'find_ca_bundle', 'is_available', 'cert_paths',17'opener_for'18]1920cert_paths = """21/etc/pki/tls/certs/ca-bundle.crt22/etc/ssl/certs/ca-certificates.crt23/usr/share/ssl/certs/ca-bundle.crt24/usr/local/share/certs/ca-root.crt25/etc/ssl/cert.pem26/System/Library/OpenSSL/certs/cert.pem27/usr/local/share/certs/ca-root-nss.crt28/etc/ssl/ca-bundle.pem29""".strip().split()3031try:32HTTPSHandler = urllib.request.HTTPSHandler33HTTPSConnection = http_client.HTTPSConnection34except AttributeError:35HTTPSHandler = HTTPSConnection = object3637is_available = ssl is not None and object not in (38HTTPSHandler, HTTPSConnection)394041try:42from ssl import CertificateError, match_hostname43except ImportError:44try:45from backports.ssl_match_hostname import CertificateError46from backports.ssl_match_hostname import match_hostname47except ImportError:48CertificateError = None49match_hostname = None5051if not CertificateError:5253class CertificateError(ValueError):54pass555657if not match_hostname:5859def _dnsname_match(dn, hostname, max_wildcards=1):60"""Matching according to RFC 6125, section 6.4.36162https://tools.ietf.org/html/rfc6125#section-6.4.363"""64pats = []65if not dn:66return False6768# Ported from python3-syntax:69# leftmost, *remainder = dn.split(r'.')70parts = dn.split(r'.')71leftmost = parts[0]72remainder = parts[1:]7374wildcards = leftmost.count('*')75if wildcards > max_wildcards:76# Issue #17980: avoid denials of service by refusing more77# than one wildcard per fragment. A survey of established78# policy among SSL implementations showed it to be a79# reasonable choice.80raise CertificateError(81"too many wildcards in certificate DNS name: " + repr(dn))8283# speed up common case w/o wildcards84if not wildcards:85return dn.lower() == hostname.lower()8687# RFC 6125, section 6.4.3, subitem 1.88# The client SHOULD NOT attempt to match a89# presented identifier in which the wildcard90# character comprises a label other than the91# left-most label.92if leftmost == '*':93# When '*' is a fragment by itself, it matches a non-empty dotless94# fragment.95pats.append('[^.]+')96elif leftmost.startswith('xn--') or hostname.startswith('xn--'):97# RFC 6125, section 6.4.3, subitem 3.98# The client SHOULD NOT attempt to match a presented identifier99# where the wildcard character is embedded within an A-label or100# U-label of an internationalized domain name.101pats.append(re.escape(leftmost))102else:103# Otherwise, '*' matches any dotless string, e.g. www*104pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))105106# add the remaining fragments, ignore any wildcards107for frag in remainder:108pats.append(re.escape(frag))109110pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)111return pat.match(hostname)112113def match_hostname(cert, hostname):114"""Verify that *cert* (in decoded format as returned by115SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125116rules are followed, but IP addresses are not accepted for *hostname*.117118CertificateError is raised on failure. On success, the function119returns nothing.120"""121if not cert:122raise ValueError("empty or no certificate")123dnsnames = []124san = cert.get('subjectAltName', ())125for key, value in san:126if key == 'DNS':127if _dnsname_match(value, hostname):128return129dnsnames.append(value)130if not dnsnames:131# The subject is only checked when there is no dNSName entry132# in subjectAltName133for sub in cert.get('subject', ()):134for key, value in sub:135# XXX according to RFC 2818, the most specific Common Name136# must be used.137if key == 'commonName':138if _dnsname_match(value, hostname):139return140dnsnames.append(value)141if len(dnsnames) > 1:142raise CertificateError(143"hostname %r doesn't match either of %s"144% (hostname, ', '.join(map(repr, dnsnames))))145elif len(dnsnames) == 1:146raise CertificateError(147"hostname %r doesn't match %r"148% (hostname, dnsnames[0]))149else:150raise CertificateError(151"no appropriate commonName or "152"subjectAltName fields were found")153154155class VerifyingHTTPSHandler(HTTPSHandler):156"""Simple verifying handler: no auth, subclasses, timeouts, etc."""157158def __init__(self, ca_bundle):159self.ca_bundle = ca_bundle160HTTPSHandler.__init__(self)161162def https_open(self, req):163return self.do_open(164lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw),165req166)167168169class VerifyingHTTPSConn(HTTPSConnection):170"""Simple verifying connection: no auth, subclasses, timeouts, etc."""171172def __init__(self, host, ca_bundle, **kw):173HTTPSConnection.__init__(self, host, **kw)174self.ca_bundle = ca_bundle175176def connect(self):177sock = socket.create_connection(178(self.host, self.port), getattr(self, 'source_address', None)179)180181# Handle the socket if a (proxy) tunnel is present182if hasattr(self, '_tunnel') and getattr(self, '_tunnel_host', None):183self.sock = sock184self._tunnel()185# http://bugs.python.org/issue7776: Python>=3.4.1 and >=2.7.7186# change self.host to mean the proxy server host when tunneling is187# being used. Adapt, since we are interested in the destination188# host for the match_hostname() comparison.189actual_host = self._tunnel_host190else:191actual_host = self.host192193if hasattr(ssl, 'create_default_context'):194ctx = ssl.create_default_context(cafile=self.ca_bundle)195self.sock = ctx.wrap_socket(sock, server_hostname=actual_host)196else:197# This is for python < 2.7.9 and < 3.4?198self.sock = ssl.wrap_socket(199sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle200)201try:202match_hostname(self.sock.getpeercert(), actual_host)203except CertificateError:204self.sock.shutdown(socket.SHUT_RDWR)205self.sock.close()206raise207208209def opener_for(ca_bundle=None):210"""Get a urlopen() replacement that uses ca_bundle for verification"""211return urllib.request.build_opener(212VerifyingHTTPSHandler(ca_bundle or find_ca_bundle())213).open214215216# from jaraco.functools217def once(func):218@functools.wraps(func)219def wrapper(*args, **kwargs):220if not hasattr(func, 'always_returns'):221func.always_returns = func(*args, **kwargs)222return func.always_returns223return wrapper224225226@once227def get_win_certfile():228try:229import wincertstore230except ImportError:231return None232233class CertFile(wincertstore.CertFile):234def __init__(self):235super(CertFile, self).__init__()236atexit.register(self.close)237238def close(self):239try:240super(CertFile, self).close()241except OSError:242pass243244_wincerts = CertFile()245_wincerts.addstore('CA')246_wincerts.addstore('ROOT')247return _wincerts.name248249250def find_ca_bundle():251"""Return an existing CA bundle path, or None"""252extant_cert_paths = filter(os.path.isfile, cert_paths)253return (254get_win_certfile()255or next(extant_cert_paths, None)256or _certifi_where()257)258259260def _certifi_where():261try:262return __import__('certifi').where()263except (ImportError, ResolutionError, ExtractionError):264pass265266267