Go to the documentation of this file.00001 """The match_hostname() function from Python 3.3.3, essential when using SSL."""
00002
00003 import re
00004
00005 __version__ = '3.4.0.2'
00006
00007 class CertificateError(ValueError):
00008 pass
00009
00010
00011 def _dnsname_match(dn, hostname, max_wildcards=1):
00012 """Matching according to RFC 6125, section 6.4.3
00013
00014 http://tools.ietf.org/html/rfc6125#section-6.4.3
00015 """
00016 pats = []
00017 if not dn:
00018 return False
00019
00020
00021
00022 parts = dn.split(r'.')
00023 leftmost = parts[0]
00024 remainder = parts[1:]
00025
00026 wildcards = leftmost.count('*')
00027 if wildcards > max_wildcards:
00028
00029
00030
00031
00032 raise CertificateError(
00033 "too many wildcards in certificate DNS name: " + repr(dn))
00034
00035
00036 if not wildcards:
00037 return dn.lower() == hostname.lower()
00038
00039
00040
00041
00042 if leftmost == '*':
00043
00044
00045 pats.append('[^.]+')
00046 elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
00047
00048
00049
00050
00051 pats.append(re.escape(leftmost))
00052 else:
00053
00054 pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
00055
00056
00057 for frag in remainder:
00058 pats.append(re.escape(frag))
00059
00060 pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
00061 return pat.match(hostname)
00062
00063
00064 def match_hostname(cert, hostname):
00065 """Verify that *cert* (in decoded format as returned by
00066 SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
00067 rules are followed, but IP addresses are not accepted for *hostname*.
00068
00069 CertificateError is raised on failure. On success, the function
00070 returns nothing.
00071 """
00072 if not cert:
00073 raise ValueError("empty or no certificate")
00074 dnsnames = []
00075 san = cert.get('subjectAltName', ())
00076 for key, value in san:
00077 if key == 'DNS':
00078 if _dnsname_match(value, hostname):
00079 return
00080 dnsnames.append(value)
00081 if not dnsnames:
00082
00083
00084 for sub in cert.get('subject', ()):
00085 for key, value in sub:
00086
00087
00088 if key == 'commonName':
00089 if _dnsname_match(value, hostname):
00090 return
00091 dnsnames.append(value)
00092 if len(dnsnames) > 1:
00093 raise CertificateError("hostname %r "
00094 "doesn't match either of %s"
00095 % (hostname, ', '.join(map(repr, dnsnames))))
00096 elif len(dnsnames) == 1:
00097 raise CertificateError("hostname %r "
00098 "doesn't match %r"
00099 % (hostname, dnsnames[0]))
00100 else:
00101 raise CertificateError("no appropriate commonName or "
00102 "subjectAltName fields were found")