1 """The match_hostname() function from Python 3.3.3, essential when using SSL."""
3 # Note: This file is under the PSF license as the code comes from the python
4 # stdlib. http://docs.python.org/3/license.html
8 __version__ = '3.4.0.2'
10 class CertificateError(ValueError):
14 def _dnsname_match(dn, hostname, max_wildcards=1):
15 """Matching according to RFC 6125, section 6.4.3
17 http://tools.ietf.org/html/rfc6125#section-6.4.3
23 # Ported from python3-syntax:
24 # leftmost, *remainder = dn.split(r'.')
25 parts = dn.split(r'.')
29 wildcards = leftmost.count('*')
30 if wildcards > max_wildcards:
31 # Issue #17980: avoid denials of service by refusing more
32 # than one wildcard per fragment. A survey of established
33 # policy among SSL implementations showed it to be a
35 raise CertificateError(
36 "too many wildcards in certificate DNS name: " + repr(dn))
38 # speed up common case w/o wildcards
40 return dn.lower() == hostname.lower()
42 # RFC 6125, section 6.4.3, subitem 1.
43 # The client SHOULD NOT attempt to match a presented identifier in which
44 # the wildcard character comprises a label other than the left-most label.
46 # When '*' is a fragment by itself, it matches a non-empty dotless
49 elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
50 # RFC 6125, section 6.4.3, subitem 3.
51 # The client SHOULD NOT attempt to match a presented identifier
52 # where the wildcard character is embedded within an A-label or
53 # U-label of an internationalized domain name.
54 pats.append(re.escape(leftmost))
56 # Otherwise, '*' matches any dotless string, e.g. www*
57 pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
59 # add the remaining fragments, ignore any wildcards
60 for frag in remainder:
61 pats.append(re.escape(frag))
63 pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
64 return pat.match(hostname)
67 def match_hostname(cert, hostname):
68 """Verify that *cert* (in decoded format as returned by
69 SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
70 rules are followed, but IP addresses are not accepted for *hostname*.
72 CertificateError is raised on failure. On success, the function
76 raise ValueError("empty or no certificate")
78 san = cert.get('subjectAltName', ())
79 for key, value in san:
81 if _dnsname_match(value, hostname):
83 dnsnames.append(value)
85 # The subject is only checked when there is no dNSName entry
87 for sub in cert.get('subject', ()):
88 for key, value in sub:
89 # XXX according to RFC 2818, the most specific Common Name
91 if key == 'commonName':
92 if _dnsname_match(value, hostname):
94 dnsnames.append(value)
96 raise CertificateError("hostname %r "
97 "doesn't match either of %s"
98 % (hostname, ', '.join(map(repr, dnsnames))))
99 elif len(dnsnames) == 1:
100 raise CertificateError("hostname %r "
102 % (hostname, dnsnames[0]))
104 raise CertificateError("no appropriate commonName or "
105 "subjectAltName fields were found")