[issue31997] SSL lib does not handle trailing dot (period) in hostname or certificate

2017-11-14 Thread Sam Napolitano

Sam Napolitano <samnap+git...@gmail.com> added the comment:

Sorry I wasn't able to get back to you sooner.

If having a trailing dot in the cert is an RFC violation, then case 2 can be 
left alone.

As for case 3, we can be more explicit:  if hostname ends in a dot AND cert 
does not end in a dot, strip dot from hostname.  This seems to be what Ryan was 
saying Chrome does.

I did a test using s_client in openssl.  Testing all 4 cases in the truth table 
returned 200s.  

$ openssl s_client -connect www.google.com.:443
...
# Enter next two lines and press return twice
HEAD / HTTP/1.0
Host: www.google.com.

# Returns 200
HTTP/1.0 200 OK
Date: Sat, 11 Nov 2017 21:20:44 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
...

So it would appear openssl against Google handles dots ok, but I could be 
wrong.  I don't know what server software they are running.

As for testing other server ssl implementations what are you looking for? 

I found a small C openssl client implementation.

https://ubuntuforums.org/showthread.php?t=2217101=12989750#post12989750

Compiling that code with some minor tweaks against openssl and testing it with 
different hostnames and Host headers (dot and no dot), the ssl connection was 
established and data read.  Invalid constructs led to errors.

Yes, you could move the logic to urllib, but I'm not sure it's practical as 
many folks just expect the ssl lib to handle the nuances.  If users have to 
handle it themselves or include urllib, it seems like an extra lift.

I appreciate you taking the time to consider the issue.

--

___
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue31997>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue31997] SSL lib does not handle trailing dot (period) in hostname or certificate

2017-11-09 Thread Sam Napolitano

New submission from Sam Napolitano <samnap+git...@gmail.com>:

I recently came across an issue in the ssl library and have a simple fix to 
address it.

When doing hostname verification against an X.509 certificate, a trailing dot 
(period) in the hostname is matched against the certificate.  But the trailing 
dot should only be applied to the DNS lookup not the certificate match.

Conversely, a certificate that has a trailing dot in its commonName (probably 
rare but allowed) should match a hostname without the trailing dot.  As the ssl 
library is written now, both cases fail.

The truth table below shows the current python ssl DNS matching behavior.

++
|  #  hostnamecertificateMATCH   |
| ++ |
| |   dns  dns.   cname cname. | |
| ++ |
|  1   xxTRUE|
|  2   x  x  FALSE   |
|  3x   xFALSE   |
|  4x x  TRUE|
++

Case 1 and 4 currently match as both hostname and certificate strings match 
exactly when the trailing dot is either present or not.

Case 2 is unlikely, as certificates are rarely signed with a trailing dot in 
the subject commonName and if they were, clients would ALWAYS have to enter the 
hostname with a trailing dot to get a match.

Case 3 is more likely where the hostname has a trailing dot, but the 
certificate does not.  For example, "www.example.com." is used for the DNS 
lookup, but then, "www.example.com." will not match the certificate due to the 
trailing dot missing from the certificate.

I propose the truth table should be true in all cases and just ignore the 
trailing dot in both the hostname and certificate.

As best I can tell, the RFCs [1] are silent on this issue.  Although the 
hostname and commonName strings currently must match, there are a couple of 
precedents where ignoring the trailing dot is done in practice.

Web browsers allow a trailing dot in the URI and will accept a certificate even 
when the certificate doesn't have a trailing dot.  For example, visit Google 
with trailing dot (https://www.google.com./) from a browser of your choice and 
check certificate.  It should check out as valid. [2]

Also, at least two language SSL libraries, Ruby [3] and Go [4], match 
certificates when hostnames contain a trailing dot.  Lastly cURL [5] ignores 
trailing dots in certs and hostnames.

In summary, I don't feel the current python ssl library is wrong - it is 
following an interpretation of the RFC.  But I think it can be more permissive, 
following the spirit of the RFC without sacrificing security.

Patch attached with code change.  If accepted, I can do a more formal PR, 
backport to 2.7 and add tests.

Thoughts?
-Sam


Example of issue


Python 3.7.0a2+ (heads/master-dirty:cbe1756e3e, Nov  3 2017, 15:56:14)
[Clang 8.1.0 (clang-802.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> cert = {'subject': ((('commonName', 'example.com'),),)}
>>> ssl.match_hostname(cert, "example.com")## Case 1 from truth table
>>> ssl.match_hostname(cert, "example.com.")   ## Case 3 from truth table
Traceback (most recent call last):
  File "", line 1, in 
  File ".../cpython/Lib/ssl.py", line 330, in match_hostname
% (hostname, dnsnames[0]))
ssl.CertificateError: hostname 'example.com.' doesn't match 'example.com'

>>> cert = {'subject': ((('commonName', 'example.com.'),),)}
>>> ssl.match_hostname(cert, "example.com.")   ## Case 4 from truth table
>>> ssl.match_hostname(cert, "example.com")## Case 2 from truth table
Traceback (most recent call last):
  File "", line 1, in 
  File ".../cpython/Lib/ssl.py", line 330, in match_hostname
% (hostname, dnsnames[0]))
ssl.CertificateError: hostname 'example.com' doesn't match 'example.com.'

References
==

[1] RFCs - there may be other RFCs addressing X.509 certificates

https://tools.ietf.org/html/rfc6125
https://tools.ietf.org/html/rfc5280
https://tools.ietf.org/html/rfc3986

[2] Old Mozilla thread discussing the trailing dot:

https://bugzilla.mozilla.org/show_bug.cgi?id=134402#c36

I quote:

Yes, it's ok to match "www.example.com." (trailing) to the cert with 
"www.example.com" (no trailing)
It's also OK to match "www.example.com" (no trailing) to the cert with 
"www.example.com." (trailing)

[3] Ruby

Ironically Ruby doesn't even take the trailing dot into consideration as it 
splits the strings using dot as the delimiter.

irb> "www.example.com".split('.') == "www.example.com.".split('.')
=> true

https:/