Skip to content

Commit e2a7a3d

Browse files
committed
bpo-37463: match_hostname requires quad-dotted IPv4
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 <[email protected]>
1 parent 7cb9204 commit e2a7a3d

File tree

3 files changed

+32
-10
lines changed

3 files changed

+32
-10
lines changed

Lib/ssl.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -327,12 +327,22 @@ def _inet_paton(ipname):
327327
Supports IPv4 addresses on all platforms and IPv6 on platforms with IPv6
328328
support.
329329
"""
330-
# inet_aton() also accepts strings like '1'
331-
if ipname.count('.') == 3:
332-
try:
333-
return _socket.inet_aton(ipname)
334-
except OSError:
335-
pass
330+
# inet_aton() also accepts strings like '1', '127.1', some also trailing
331+
# data like '127.0.0.1 whatever'.
332+
try:
333+
addr = _socket.inet_aton(ipname)
334+
except OSError:
335+
# not an IPv4 address
336+
pass
337+
else:
338+
if _socket.inet_ntoa(addr) == ipname:
339+
# only accept injective ipnames
340+
return addr
341+
else:
342+
# refuse for short IPv4 notation and additional trailing data
343+
raise ValueError(
344+
"{!r} is not a quad-dotted IPv4 address.".format(ipname)
345+
)
336346

337347
try:
338348
return _socket.inet_pton(_socket.AF_INET6, ipname)
@@ -346,14 +356,15 @@ def _inet_paton(ipname):
346356
raise ValueError("{!r} is not an IPv4 address.".format(ipname))
347357

348358

349-
def _ipaddress_match(ipname, host_ip):
359+
def _ipaddress_match(cert_ipaddress, host_ip):
350360
"""Exact matching of IP addresses.
351361
352362
RFC 6125 explicitly doesn't define an algorithm for this
353363
(section 1.7.2 - "Out of Scope").
354364
"""
355-
# OpenSSL may add a trailing newline to a subjectAltName's IP address
356-
ip = _inet_paton(ipname.rstrip())
365+
# OpenSSL may add a trailing newline to a subjectAltName's IP address,
366+
# commonly woth IPv6 addresses. Strip off trailing \n.
367+
ip = _inet_paton(cert_ipaddress.rstrip())
357368
return ip == host_ip
358369

359370

Lib/test/test_ssl.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,9 +669,14 @@ def fail(cert, hostname):
669669
cert = {'subject': ((('commonName', 'example.com'),),),
670670
'subjectAltName': (('DNS', 'example.com'),
671671
('IP Address', '10.11.12.13'),
672-
('IP Address', '14.15.16.17'))}
672+
('IP Address', '14.15.16.17'),
673+
('IP Address', '127.0.0.1'))}
673674
ok(cert, '10.11.12.13')
674675
ok(cert, '14.15.16.17')
676+
# socket.inet_ntoa(socket.inet_aton('127.1')) == '127.0.0.1'
677+
fail(cert, '127.1')
678+
fail(cert, '14.15.16.17 ')
679+
fail(cert, '14.15.16.17 extra data')
675680
fail(cert, '14.15.16.18')
676681
fail(cert, 'example.net')
677682

@@ -684,6 +689,8 @@ def fail(cert, hostname):
684689
('IP Address', '2003:0:0:0:0:0:0:BABA\n'))}
685690
ok(cert, '2001::cafe')
686691
ok(cert, '2003::baba')
692+
fail(cert, '2003::baba ')
693+
fail(cert, '2003::baba extra data')
687694
fail(cert, '2003::bebe')
688695
fail(cert, 'example.net')
689696

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ssl.match_hostname() no longer accepts IPv4 addresses with additional text
2+
after the address and only quad-dotted notation without trailing
3+
whitespaces. Some inet_aton() implementations ignore whitespace and all data
4+
after whitespace, e.g. '127.0.0.1 whatever'.

0 commit comments

Comments
 (0)