From 87af149292d3b377845664bf170b923042b53295 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 22 Nov 2021 14:01:41 +0100 Subject: [PATCH 1/3] Fix getting subnet of ipv4 mapped ipv6 addresses Signed-off-by: Vincent Petry --- lib/private/Security/Normalizer/IpAddress.php | 2 ++ tests/lib/Security/Normalizer/IpAddressTest.php | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/lib/private/Security/Normalizer/IpAddress.php b/lib/private/Security/Normalizer/IpAddress.php index 0a3606de28a6c..f878c9f986b75 100644 --- a/lib/private/Security/Normalizer/IpAddress.php +++ b/lib/private/Security/Normalizer/IpAddress.php @@ -103,6 +103,8 @@ public function getSubnet(): string { $this->ip, 32 ); + } elseif (substr(strtolower($this->ip), 0, 7) === '::ffff:') { + return '::ffff:' . $this->getIPv4Subnet(substr($this->ip, 7), 32); } return $this->getIPv6Subnet( $this->ip, diff --git a/tests/lib/Security/Normalizer/IpAddressTest.php b/tests/lib/Security/Normalizer/IpAddressTest.php index 044fc52b4b9c1..a3c839adc3bfc 100644 --- a/tests/lib/Security/Normalizer/IpAddressTest.php +++ b/tests/lib/Security/Normalizer/IpAddressTest.php @@ -35,6 +35,10 @@ public function subnetDataProvider() { '192.168.0.123', '192.168.0.123/32', ], + [ + '::ffff:192.168.0.123', + '::ffff:192.168.0.123/32', + ], [ '2001:0db8:85a3:0000:0000:8a2e:0370:7334', '2001:db8:85a3::/64', From d9a5f2867e2e6b08895ab8d696c46e09479b39bd Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 22 Nov 2021 15:53:41 +0100 Subject: [PATCH 2/3] Improve normalizer detecting IPv4 inside of IPv6 The subnet for an IPv4 address inside of IPv6 is now returned in its IPv4 form. Signed-off-by: Vincent Petry --- lib/private/Security/Normalizer/IpAddress.php | 44 ++++++++++++++++++- .../lib/Security/Normalizer/IpAddressTest.php | 10 ++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/lib/private/Security/Normalizer/IpAddress.php b/lib/private/Security/Normalizer/IpAddress.php index f878c9f986b75..c442391dc3eb6 100644 --- a/lib/private/Security/Normalizer/IpAddress.php +++ b/lib/private/Security/Normalizer/IpAddress.php @@ -92,6 +92,39 @@ private function getIPv6Subnet(string $ip, int $maskBits = 48): string { return \inet_ntop($binary).'/'.$maskBits; } + /** + * Returns the IPv4 address embedded in an IPv6 if applicable. + * The detected format is "::ffff:x.x.x.x" using the binary form. + * + * @param string $ipv6 IPv6 string + * @return null|string embedded IPv4 string or null if none was found + */ + private function getEmbeddedIpv4($ipv6) { + $binary = inet_pton($ipv6); + if (!$binary) { + return null; + } + for ($i = 0; $i <= 9; $i++) { + if (unpack('C', $binary[$i])[1] !== 0) { + return null; + } + } + + for ($i = 10; $i <= 11; $i++) { + if (unpack('C', $binary[$i])[1] !== 255) { + return null; + } + } + + $binary4 = ''; + for ($i = 12; $i < 16; $i++) { + $binary4 .= $binary[$i]; + } + + return inet_ntop($binary4); + } + + /** * Gets either the /32 (IPv4) or the /64 (IPv6) subnet of an IP address * @@ -103,9 +136,16 @@ public function getSubnet(): string { $this->ip, 32 ); - } elseif (substr(strtolower($this->ip), 0, 7) === '::ffff:') { - return '::ffff:' . $this->getIPv4Subnet(substr($this->ip, 7), 32); } + + $ipv4 = $this->getEmbeddedIpv4($this->ip); + if ($ipv4 !== null) { + return $this->getIPv4Subnet( + $ipv4, + 32 + ); + } + return $this->getIPv6Subnet( $this->ip, 64 diff --git a/tests/lib/Security/Normalizer/IpAddressTest.php b/tests/lib/Security/Normalizer/IpAddressTest.php index a3c839adc3bfc..86b03f7d20929 100644 --- a/tests/lib/Security/Normalizer/IpAddressTest.php +++ b/tests/lib/Security/Normalizer/IpAddressTest.php @@ -37,7 +37,15 @@ public function subnetDataProvider() { ], [ '::ffff:192.168.0.123', - '::ffff:192.168.0.123/32', + '192.168.0.123/32', + ], + [ + '0:0:0:0:0:ffff:192.168.0.123', + '192.168.0.123/32', + ], + [ + '0:0:0:0:0:ffff:c0a8:7b', + '192.168.0.123/32', ], [ '2001:0db8:85a3:0000:0000:8a2e:0370:7334', From 264b329aaf2e4b29b496e6e6b711ecda96af5226 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 22 Nov 2021 17:36:26 +0100 Subject: [PATCH 3/3] Type hint in IpAddress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Vincent Petry Co-authored-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com> --- lib/private/Security/Normalizer/IpAddress.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/private/Security/Normalizer/IpAddress.php b/lib/private/Security/Normalizer/IpAddress.php index c442391dc3eb6..c3aa13aecda1b 100644 --- a/lib/private/Security/Normalizer/IpAddress.php +++ b/lib/private/Security/Normalizer/IpAddress.php @@ -96,10 +96,9 @@ private function getIPv6Subnet(string $ip, int $maskBits = 48): string { * Returns the IPv4 address embedded in an IPv6 if applicable. * The detected format is "::ffff:x.x.x.x" using the binary form. * - * @param string $ipv6 IPv6 string - * @return null|string embedded IPv4 string or null if none was found + * @return string|null embedded IPv4 string or null if none was found */ - private function getEmbeddedIpv4($ipv6) { + private function getEmbeddedIpv4(string $ipv6): ?string { $binary = inet_pton($ipv6); if (!$binary) { return null;