@@ -166,31 +166,59 @@ class CertificateError(ValueError):
166166 pass
167167
168168
169- def _dnsname_to_pat (dn , max_wildcards = 1 ):
169+ def _dnsname_match (dn , hostname , max_wildcards = 1 ):
170+ """Matching according to RFC 6125, section 6.4.3
171+
172+ http://tools.ietf.org/html/rfc6125#section-6.4.3
173+ """
170174 pats = []
171- for frag in dn .split (r'.' ):
172- if frag .count ('*' ) > max_wildcards :
173- # Issue #17980: avoid denials of service by refusing more
174- # than one wildcard per fragment. A survey of established
175- # policy among SSL implementations showed it to be a
176- # reasonable choice.
177- raise CertificateError (
178- "too many wildcards in certificate DNS name: " + repr (dn ))
179- if frag == '*' :
180- # When '*' is a fragment by itself, it matches a non-empty dotless
181- # fragment.
182- pats .append ('[^.]+' )
183- else :
184- # Otherwise, '*' matches any dotless fragment.
185- frag = re .escape (frag )
186- pats .append (frag .replace (r'\*' , '[^.]*' ))
187- return re .compile (r'\A' + r'\.' .join (pats ) + r'\Z' , re .IGNORECASE )
175+ if not dn :
176+ return False
177+
178+ leftmost , * remainder = dn .split (r'.' )
179+
180+ wildcards = leftmost .count ('*' )
181+ if wildcards > max_wildcards :
182+ # Issue #17980: avoid denials of service by refusing more
183+ # than one wildcard per fragment. A survery of established
184+ # policy among SSL implementations showed it to be a
185+ # reasonable choice.
186+ raise CertificateError (
187+ "too many wildcards in certificate DNS name: " + repr (dn ))
188+
189+ # speed up common case w/o wildcards
190+ if not wildcards :
191+ return dn .lower () == hostname .lower ()
192+
193+ # RFC 6125, section 6.4.3, subitem 1.
194+ # The client SHOULD NOT attempt to match a presented identifier in which
195+ # the wildcard character comprises a label other than the left-most label.
196+ if leftmost == '*' :
197+ # When '*' is a fragment by itself, it matches a non-empty dotless
198+ # fragment.
199+ pats .append ('[^.]+' )
200+ elif leftmost .startswith ('xn--' ) or hostname .startswith ('xn--' ):
201+ # RFC 6125, section 6.4.3, subitem 3.
202+ # The client SHOULD NOT attempt to match a presented identifier
203+ # where the wildcard character is embedded within an A-label or
204+ # U-label of an internationalized domain name.
205+ pats .append (re .escape (leftmost ))
206+ else :
207+ # Otherwise, '*' matches any dotless string, e.g. www*
208+ pats .append (re .escape (leftmost ).replace (r'\*' , '[^.]*' ))
209+
210+ # add the remaining fragments, ignore any wildcards
211+ for frag in remainder :
212+ pats .append (re .escape (frag ))
213+
214+ pat = re .compile (r'\A' + r'\.' .join (pats ) + r'\Z' , re .IGNORECASE )
215+ return pat .match (hostname )
188216
189217
190218def match_hostname (cert , hostname ):
191219 """Verify that *cert* (in decoded format as returned by
192- SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules
193- are mostly followed, but IP addresses are not accepted for *hostname*.
220+ SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
221+ rules are followed, but IP addresses are not accepted for *hostname*.
194222
195223 CertificateError is raised on failure. On success, the function
196224 returns nothing.
@@ -201,7 +229,7 @@ def match_hostname(cert, hostname):
201229 san = cert .get ('subjectAltName' , ())
202230 for key , value in san :
203231 if key == 'DNS' :
204- if _dnsname_to_pat (value ). match ( hostname ):
232+ if _dnsname_match (value , hostname ):
205233 return
206234 dnsnames .append (value )
207235 if not dnsnames :
@@ -212,7 +240,7 @@ def match_hostname(cert, hostname):
212240 # XXX according to RFC 2818, the most specific Common Name
213241 # must be used.
214242 if key == 'commonName' :
215- if _dnsname_to_pat (value ). match ( hostname ):
243+ if _dnsname_match (value , hostname ):
216244 return
217245 dnsnames .append (value )
218246 if len (dnsnames ) > 1 :
0 commit comments