@@ -129,31 +129,59 @@ class CertificateError(ValueError):
129129 pass
130130
131131
132- def _dnsname_to_pat (dn , max_wildcards = 1 ):
132+ def _dnsname_match (dn , hostname , max_wildcards = 1 ):
133+ """Matching according to RFC 6125, section 6.4.3
134+
135+ http://tools.ietf.org/html/rfc6125#section-6.4.3
136+ """
133137 pats = []
134- for frag in dn .split (r'.' ):
135- if frag .count ('*' ) > max_wildcards :
136- # Issue #17980: avoid denials of service by refusing more
137- # than one wildcard per fragment. A survey of established
138- # policy among SSL implementations showed it to be a
139- # reasonable choice.
140- raise CertificateError (
141- "too many wildcards in certificate DNS name: " + repr (dn ))
142- if frag == '*' :
143- # When '*' is a fragment by itself, it matches a non-empty dotless
144- # fragment.
145- pats .append ('[^.]+' )
146- else :
147- # Otherwise, '*' matches any dotless fragment.
148- frag = re .escape (frag )
149- pats .append (frag .replace (r'\*' , '[^.]*' ))
150- return re .compile (r'\A' + r'\.' .join (pats ) + r'\Z' , re .IGNORECASE )
138+ if not dn :
139+ return False
140+
141+ leftmost , * remainder = dn .split (r'.' )
142+
143+ wildcards = leftmost .count ('*' )
144+ if wildcards > max_wildcards :
145+ # Issue #17980: avoid denials of service by refusing more
146+ # than one wildcard per fragment. A survery of established
147+ # policy among SSL implementations showed it to be a
148+ # reasonable choice.
149+ raise CertificateError (
150+ "too many wildcards in certificate DNS name: " + repr (dn ))
151+
152+ # speed up common case w/o wildcards
153+ if not wildcards :
154+ return dn .lower () == hostname .lower ()
155+
156+ # RFC 6125, section 6.4.3, subitem 1.
157+ # The client SHOULD NOT attempt to match a presented identifier in which
158+ # the wildcard character comprises a label other than the left-most label.
159+ if leftmost == '*' :
160+ # When '*' is a fragment by itself, it matches a non-empty dotless
161+ # fragment.
162+ pats .append ('[^.]+' )
163+ elif leftmost .startswith ('xn--' ) or hostname .startswith ('xn--' ):
164+ # RFC 6125, section 6.4.3, subitem 3.
165+ # The client SHOULD NOT attempt to match a presented identifier
166+ # where the wildcard character is embedded within an A-label or
167+ # U-label of an internationalized domain name.
168+ pats .append (re .escape (leftmost ))
169+ else :
170+ # Otherwise, '*' matches any dotless string, e.g. www*
171+ pats .append (re .escape (leftmost ).replace (r'\*' , '[^.]*' ))
172+
173+ # add the remaining fragments, ignore any wildcards
174+ for frag in remainder :
175+ pats .append (re .escape (frag ))
176+
177+ pat = re .compile (r'\A' + r'\.' .join (pats ) + r'\Z' , re .IGNORECASE )
178+ return pat .match (hostname )
151179
152180
153181def match_hostname (cert , hostname ):
154182 """Verify that *cert* (in decoded format as returned by
155- SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules
156- are mostly followed, but IP addresses are not accepted for *hostname*.
183+ SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
184+ rules are followed, but IP addresses are not accepted for *hostname*.
157185
158186 CertificateError is raised on failure. On success, the function
159187 returns nothing.
@@ -164,7 +192,7 @@ def match_hostname(cert, hostname):
164192 san = cert .get ('subjectAltName' , ())
165193 for key , value in san :
166194 if key == 'DNS' :
167- if _dnsname_to_pat (value ). match ( hostname ):
195+ if _dnsname_match (value , hostname ):
168196 return
169197 dnsnames .append (value )
170198 if not dnsnames :
@@ -175,7 +203,7 @@ def match_hostname(cert, hostname):
175203 # XXX according to RFC 2818, the most specific Common Name
176204 # must be used.
177205 if key == 'commonName' :
178- if _dnsname_to_pat (value ). match ( hostname ):
206+ if _dnsname_match (value , hostname ):
179207 return
180208 dnsnames .append (value )
181209 if len (dnsnames ) > 1 :
0 commit comments