bpo-37463: match_hostname requires quad-dotted IPv4 (GH-14499)

ssl.match_hostname() no longer accepts IPv4 addresses with additional text
after the address and only quad-dotted notation without trailing
whitespaces. Some inet_aton() implementations ignore whitespace and all data
after whitespace, e.g. '127.0.0.1 whatever'.

Short notations like '127.1' for '127.0.0.1' were already filtered out.

The bug was initially found by Dominik Czarnota and reported by Paul Kehrer.

Signed-off-by: Christian Heimes <christian@python.org>

https://bugs.python.org/issue37463
(cherry picked from commit 477b1b2576)

Co-authored-by: Christian Heimes <christian@python.org>
This commit is contained in:
Miss Islington (bot) 2019-07-02 14:06:18 -07:00 committed by GitHub
parent 632cb36084
commit 3cba3d3c55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 10 deletions

View File

@ -327,12 +327,22 @@ def _inet_paton(ipname):
Supports IPv4 addresses on all platforms and IPv6 on platforms with IPv6
support.
"""
# inet_aton() also accepts strings like '1'
if ipname.count('.') == 3:
try:
return _socket.inet_aton(ipname)
except OSError:
pass
# inet_aton() also accepts strings like '1', '127.1', some also trailing
# data like '127.0.0.1 whatever'.
try:
addr = _socket.inet_aton(ipname)
except OSError:
# not an IPv4 address
pass
else:
if _socket.inet_ntoa(addr) == ipname:
# only accept injective ipnames
return addr
else:
# refuse for short IPv4 notation and additional trailing data
raise ValueError(
"{!r} is not a quad-dotted IPv4 address.".format(ipname)
)
try:
return _socket.inet_pton(_socket.AF_INET6, ipname)
@ -346,14 +356,15 @@ def _inet_paton(ipname):
raise ValueError("{!r} is not an IPv4 address.".format(ipname))
def _ipaddress_match(ipname, host_ip):
def _ipaddress_match(cert_ipaddress, host_ip):
"""Exact matching of IP addresses.
RFC 6125 explicitly doesn't define an algorithm for this
(section 1.7.2 - "Out of Scope").
"""
# OpenSSL may add a trailing newline to a subjectAltName's IP address
ip = _inet_paton(ipname.rstrip())
# OpenSSL may add a trailing newline to a subjectAltName's IP address,
# commonly woth IPv6 addresses. Strip off trailing \n.
ip = _inet_paton(cert_ipaddress.rstrip())
return ip == host_ip

View File

@ -666,9 +666,14 @@ class BasicSocketTests(unittest.TestCase):
cert = {'subject': ((('commonName', 'example.com'),),),
'subjectAltName': (('DNS', 'example.com'),
('IP Address', '10.11.12.13'),
('IP Address', '14.15.16.17'))}
('IP Address', '14.15.16.17'),
('IP Address', '127.0.0.1'))}
ok(cert, '10.11.12.13')
ok(cert, '14.15.16.17')
# socket.inet_ntoa(socket.inet_aton('127.1')) == '127.0.0.1'
fail(cert, '127.1')
fail(cert, '14.15.16.17 ')
fail(cert, '14.15.16.17 extra data')
fail(cert, '14.15.16.18')
fail(cert, 'example.net')
@ -681,6 +686,8 @@ class BasicSocketTests(unittest.TestCase):
('IP Address', '2003:0:0:0:0:0:0:BABA\n'))}
ok(cert, '2001::cafe')
ok(cert, '2003::baba')
fail(cert, '2003::baba ')
fail(cert, '2003::baba extra data')
fail(cert, '2003::bebe')
fail(cert, 'example.net')

View File

@ -0,0 +1,4 @@
ssl.match_hostname() no longer accepts IPv4 addresses with additional text
after the address and only quad-dotted notation without trailing
whitespaces. Some inet_aton() implementations ignore whitespace and all data
after whitespace, e.g. '127.0.0.1 whatever'.