From f0527d162c94a834c443dd87ae2ed9abf27f718d Mon Sep 17 00:00:00 2001 From: Mladen B <mladen074@gmail.com> Date: Tue, 8 Feb 2022 20:37:29 +0100 Subject: [PATCH] Add IPv6 support for admin IP whitelist feature. --- interface/web/login/index.php | 51 +++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/interface/web/login/index.php b/interface/web/login/index.php index 2bcb380d95..d59d24efc8 100644 --- a/interface/web/login/index.php +++ b/interface/web/login/index.php @@ -190,7 +190,7 @@ function is_admin_ip_whitelisted($ip, $conf) // exclude empty lines and comments if ($line === '' || $line[0] === '#') return false; - return ip_matches_cidr($ip, $line); + return ipv6_matches_cidr($ip, $line) || ipv4_matches_cidr($ip, $line); }); return count($matches) > 0; @@ -198,24 +198,59 @@ function is_admin_ip_whitelisted($ip, $conf) // based on https://www.php.net/manual/en/ref.network.php (comments) /** - * Checks if the given IP address matches the given CIDR. - * @param $ip - * @param $cidr - + * Checks if the given IPv4 address matches the given CIDR. + * @param string $ip The IPv4 address. + * @param string $cidr The CIDR in the IPv4 format. * @return bool */ -function ip_matches_cidr ($ip, $cidr) { +function ipv4_matches_cidr ($ip, $cidr) +{ + if (strpos($ip, '.') === false) return false; + list ($net, $mask) = explode ('/', $cidr); if (!$mask) $mask = 32; $ip_net = ip2long ($net); - $ip_mask = ~((1 << (32 - $mask)) - 1); - $ip_ip = ip2long ($ip); + $ip_mask = ~((1 << (32 - $mask)) - 1); return (($ip_ip & $ip_mask) == ($ip_net & $ip_mask)); } +// based on https://stackoverflow.com/a/7951507/2428861 +/** + * Checks if the given IPv6 address matches the given CIDR. + * @param string $ip The IPv6 address. + * @param string $cidr The CIDR in the IPv6 format. + * @return bool + */ +function ipv6_matches_cidr($ip, $cidr) +{ + if (strpos($ip, ':') === false) return false; + + list ($net, $mask) = explode('/', $cidr); + if (!$mask) $mask = 128; + + $ip_net = in_addr_to_bitstring(inet_pton($net)); + $ip_ip = in_addr_to_bitstring(inet_pton($ip)); + + return substr($ip_ip, 0, $mask) === substr($ip_net, 0, $mask); +} + +/** + * Converts the output of {@see inet_pton()} to string of bits. + * @param string $in_addr The in_addr representation of the IP address. + * @return string String of bits representing given in_addr representation of the IP address. + */ +function in_addr_to_bitstring($in_addr) +{ + $result = ''; + foreach (str_split($in_addr) as $c) { + $result .= str_pad(decbin(ord($c)), 8, '0', STR_PAD_LEFT); + } + return $result; +} + /** * Validates user credentials and fetches the user if validation succeeded * @param app $app -- GitLab