9090ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY
9191"""
9292
93- import ipaddress
94- import re
9593import sys
9694import os
9795from collections import namedtuple
160158
161159from socket import socket ,AF_INET ,SOCK_STREAM ,create_connection
162160from socket import SOL_SOCKET ,SO_TYPE
161+ import socket as _socket
163162import base64 # for DER-to-PEM translation
164163import errno
165164import warnings
183182def _dnsname_match (dn ,hostname ):
184183"""Matching according to RFC 6125, section 6.4.3
185184
186- http://tools.ietf.org/html/rfc6125#section-6.4.3
185+ - Hostnames are compared lower case.
186+ - For IDNA, both dn and hostname must be encoded as IDN A-label (ACE).
187+ - Partial wildcards like 'www*.example.org', multiple wildcards, sole
188+ wildcard or wildcards in labels other then the left-most label are not
189+ supported and a CertificateError is raised.
190+ - A wildcard must match at least one character.
187191 """
188- pats = []
189192if not dn :
190193return False
191194
192- leftmost ,* remainder = dn .split (r'.' )
195+ wildcards = dn .count ('*' )
196+ # speed up common case w/o wildcards
197+ if not wildcards :
198+ return dn .lower ()== hostname .lower ()
199+
200+ if wildcards > 1 :
201+ raise CertificateError (
202+ "too many wildcards in certificate DNS name: {!r}." .format (dn ))
193203
194- wildcards = leftmost .count ('*' )
195- if wildcards == 1 and len (leftmost )> 1 :
204+ dn_leftmost ,sep ,dn_remainder = dn .partition ('.' )
205+
206+ if '*' in dn_remainder :
196207# Only match wildcard in leftmost segment.
197208raise CertificateError (
198- "wildcard can only be present in the leftmost segment: " + repr (dn ))
209+ "wildcard can only be present in the leftmost label: "
210+ "{!r}." .format (dn ))
199211
200- if wildcards > 1 :
201- # Issue #17980: avoid denials of service by refusing more
202- # than one wildcard per fragment. A survey of established
203- # policy among SSL implementations showed it to be a
204- # reasonable choice.
212+ if not sep :
213+ # no right side
205214raise CertificateError (
206- "too many wildcards in certificate DNS name: " + repr (dn ))
215+ "sole wildcard without additional labels are not support: "
216+ "{!r}." .format (dn ))
207217
208- # speed up common case w/o wildcards
209- if not wildcards :
210- return dn .lower ()== hostname .lower ()
218+ if dn_leftmost != '*' :
219+ # no partial wildcard matching
220+ raise CertificateError (
221+ "partial wildcards in leftmost label are not supported: "
222+ "{!r}." .format (dn ))
211223
212- # RFC 6125, section 6.4.3, subitem 1.
213- # The client SHOULD NOT attempt to match a presented identifier in which
214- # the wildcard character comprises a label other than the left-most label.
215- if leftmost == '*' :
216- # When '*' is a fragment by itself, it matches a non-empty dotless
217- # fragment.
218- pats .append ('[^.]+' )
219- elif leftmost .startswith ('xn--' )or hostname .startswith ('xn--' ):
220- # RFC 6125, section 6.4.3, subitem 3.
221- # The client SHOULD NOT attempt to match a presented identifier
222- # where the wildcard character is embedded within an A-label or
223- # U-label of an internationalized domain name.
224- pats .append (re .escape (leftmost ))
225- else :
226- # Otherwise, '*' matches any dotless string, e.g. www*
227- pats .append (re .escape (leftmost ).replace (r'\*' ,'[^.]*' ))
224+ hostname_leftmost ,sep ,hostname_remainder = hostname .partition ('.' )
225+ if not hostname_leftmost or not sep :
226+ # wildcard must match at least one char
227+ return False
228+ return dn_remainder .lower ()== hostname_remainder .lower ()
228229
229- # add the remaining fragments, ignore any wildcards
230- for frag in remainder :
231- pats .append (re .escape (frag ))
232230
233- pat = re .compile (r'\A' + r'\.' .join (pats )+ r'\Z' ,re .IGNORECASE )
234- return pat .match (hostname )
231+ def _inet_paton (ipname ):
232+ """Try to convert an IP address to packed binary form
233+
234+ Supports IPv4 addresses on all platforms and IPv6 on platforms with IPv6
235+ support.
236+ """
237+ # inet_aton() also accepts strings like '1'
238+ if ipname .count ('.' )== 3 :
239+ try :
240+ return _socket .inet_aton (ipname )
241+ except OSError :
242+ pass
243+
244+ try :
245+ return _socket .inet_pton (_socket .AF_INET6 ,ipname )
246+ except OSError :
247+ raise ValueError ("{!r} is neither an IPv4 nor an IP6 "
248+ "address." .format (ipname ))
249+ except AttributeError :
250+ # AF_INET6 not available
251+ pass
252+
253+ raise ValueError ("{!r} is not an IPv4 address." .format (ipname ))
235254
236255
237256def _ipaddress_match (ipname ,host_ip ):
@@ -241,14 +260,19 @@ def _ipaddress_match(ipname, host_ip):
241260 (section 1.7.2 - "Out of Scope").
242261 """
243262# OpenSSL may add a trailing newline to a subjectAltName's IP address
244- ip = ipaddress . ip_address (ipname .rstrip ())
263+ ip = _inet_paton (ipname .rstrip ())
245264return ip == host_ip
246265
247266
248267def match_hostname (cert ,hostname ):
249268"""Verify that *cert* (in decoded format as returned by
250269 SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
251- rules are followed, but IP addresses are not accepted for *hostname*.
270+ rules are followed.
271+
272+ The function matches IP addresses rather than dNSNames if hostname is a
273+ valid ipaddress string. IPv4 addresses are supported on all platforms.
274+ IPv6 addresses are supported on platforms with IPv6 support (AF_INET6
275+ and inet_pton).
252276
253277 CertificateError is raised on failure. On success, the function
254278 returns nothing.
@@ -258,7 +282,7 @@ def match_hostname(cert, hostname):
258282"SSL socket or SSL context with either "
259283"CERT_OPTIONAL or CERT_REQUIRED" )
260284try :
261- host_ip = ipaddress . ip_address (hostname )
285+ host_ip = _inet_paton (hostname )
262286except ValueError :
263287# Not an IP address (common case)
264288host_ip = None