Skip to content

Commit 3cba3d3

Browse files
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 <[email protected]> https://bugs.python.org/issue37463 (cherry picked from commit 477b1b2) Co-authored-by: Christian Heimes <[email protected]>
1 parent 632cb36 commit 3cba3d3

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
@@ -666,9 +666,14 @@ def fail(cert, hostname):
666666
cert = {'subject': ((('commonName', 'example.com'),),),
667667
'subjectAltName': (('DNS', 'example.com'),
668668
('IP Address', '10.11.12.13'),
669-
('IP Address', '14.15.16.17'))}
669+
('IP Address', '14.15.16.17'),
670+
('IP Address', '127.0.0.1'))}
670671
ok(cert, '10.11.12.13')
671672
ok(cert, '14.15.16.17')
673+
# socket.inet_ntoa(socket.inet_aton('127.1')) == '127.0.0.1'
674+
fail(cert, '127.1')
675+
fail(cert, '14.15.16.17 ')
676+
fail(cert, '14.15.16.17 extra data')
672677
fail(cert, '14.15.16.18')
673678
fail(cert, 'example.net')
674679

@@ -681,6 +686,8 @@ def fail(cert, hostname):
681686
('IP Address', '2003:0:0:0:0:0:0:BABA\n'))}
682687
ok(cert, '2001::cafe')
683688
ok(cert, '2003::baba')
689+
fail(cert, '2003::baba ')
690+
fail(cert, '2003::baba extra data')
684691
fail(cert, '2003::bebe')
685692
fail(cert, 'example.net')
686693

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)