1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/phpmyadmin_ynh.git synced 2024-09-03 19:56:46 +02:00
phpmyadmin_ynh/sources/libraries/ip_allow_deny.lib.php
2014-01-14 12:28:06 +01:00

334 lines
9.2 KiB
PHP

<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* This library is used with the server IP allow/deny host authentication
* feature
*
* @package PhpMyAdmin
*/
if (! defined('PHPMYADMIN')) {
exit;
}
/**
* Gets the "true" IP address of the current user
*
* @return string the ip of the user
*
* @access private
*/
function PMA_getIp()
{
/* Get the address of user */
if (!empty($_SERVER['REMOTE_ADDR'])) {
$direct_ip = $_SERVER['REMOTE_ADDR'];
} else {
/* We do not know remote IP */
return false;
}
/* Do we trust this IP as a proxy? If yes we will use it's header. */
if (isset($GLOBALS['cfg']['TrustedProxies'][$direct_ip])) {
$trusted_header_value
= PMA_getenv($GLOBALS['cfg']['TrustedProxies'][$direct_ip]);
$matches = array();
// the $ checks that the header contains only one IP address,
// ?: makes sure the () don't capture
$is_ip = preg_match(
'|^(?:[0-9]{1,3}\.){3,3}[0-9]{1,3}$|',
$trusted_header_value, $matches
);
if ($is_ip && (count($matches) == 1)) {
// True IP behind a proxy
return $matches[0];
}
}
/* Return true IP */
return $direct_ip;
} // end of the 'PMA_getIp()' function
/**
* Matches for IPv4 or IPv6 addresses
*
* @param string $testRange string of IP range to match
* @param string $ipToTest string of IP to test against range
*
* @return boolean whether the IP mask matches
*
* @access public
*/
function PMA_ipMaskTest($testRange, $ipToTest)
{
$result = true;
if (strpos($testRange, ':') > -1 || strpos($ipToTest, ':') > -1) {
// assume IPv6
$result = PMA_ipv6MaskTest($testRange, $ipToTest);
} else {
$result = PMA_ipv4MaskTest($testRange, $ipToTest);
}
return $result;
} // end of the "PMA_ipMaskTest()" function
/**
* Based on IP Pattern Matcher
* Originally by J.Adams <jna@retina.net>
* Found on <http://www.php.net/manual/en/function.ip2long.php>
* Modified for phpMyAdmin
*
* Matches:
* xxx.xxx.xxx.xxx (exact)
* xxx.xxx.xxx.[yyy-zzz] (range)
* xxx.xxx.xxx.xxx/nn (CIDR)
*
* Does not match:
* xxx.xxx.xxx.xx[yyy-zzz] (range, partial octets not supported)
*
* @param string $testRange string of IP range to match
* @param string $ipToTest string of IP to test against range
*
* @return boolean whether the IP mask matches
*
* @access public
*/
function PMA_ipv4MaskTest($testRange, $ipToTest)
{
$result = true;
$match = preg_match(
'|([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/([0-9]+)|',
$testRange,
$regs
);
if ($match) {
// performs a mask match
$ipl = ip2long($ipToTest);
$rangel = ip2long(
$regs[1] . '.' . $regs[2] . '.' . $regs[3] . '.' . $regs[4]
);
$maskl = 0;
for ($i = 0; $i < 31; $i++) {
if ($i < $regs[5] - 1) {
$maskl = $maskl + PMA_Util::pow(2, (30 - $i));
} // end if
} // end for
if (($maskl & $rangel) == ($maskl & $ipl)) {
return true;
} else {
return false;
}
} else {
// range based
$maskocts = explode('.', $testRange);
$ipocts = explode('.', $ipToTest);
// perform a range match
for ($i = 0; $i < 4; $i++) {
if (preg_match('|\[([0-9]+)\-([0-9]+)\]|', $maskocts[$i], $regs)) {
if (($ipocts[$i] > $regs[2]) || ($ipocts[$i] < $regs[1])) {
$result = false;
} // end if
} else {
if ($maskocts[$i] <> $ipocts[$i]) {
$result = false;
} // end if
} // end if/else
} //end for
} //end if/else
return $result;
} // end of the "PMA_ipv4MaskTest()" function
/**
* IPv6 matcher
* CIDR section taken from http://stackoverflow.com/a/10086404
* Modified for phpMyAdmin
*
* Matches:
* xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
* (exact)
* xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:[yyyy-zzzz]
* (range, only at end of IP - no subnets)
* xxxx:xxxx:xxxx:xxxx/nn
* (CIDR)
*
* Does not match:
* xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xx[yyy-zzz]
* (range, partial octets not supported)
*
* @param string $test_range string of IP range to match
* @param string $ip_to_test string of IP to test against range
*
* @return boolean whether the IP mask matches
*
* @access public
*/
function PMA_ipv6MaskTest($test_range, $ip_to_test)
{
$result = true;
// convert to lowercase for easier comparison
$test_range = strtolower($test_range);
$ip_to_test = strtolower($ip_to_test);
$is_cidr = strpos($test_range, '/') > -1;
$is_range = strpos($test_range, '[') > -1;
$is_single = ! $is_cidr && ! $is_range;
$ip_hex = bin2hex(inet_pton($ip_to_test));
if ($is_single) {
$range_hex = bin2hex(inet_pton($test_range));
$result = $ip_hex === $range_hex;
} elseif ($is_range) {
// what range do we operate on?
$range_match = array();
$match = preg_match(
'/\[([0-9a-f]+)\-([0-9a-f]+)\]/', $test_range, $range_match
);
if ($match) {
$range_start = $range_match[1];
$range_end = $range_match[2];
// get the first and last allowed IPs
$first_ip = str_replace($range_match[0], $range_start, $test_range);
$first_hex = bin2hex(inet_pton($first_ip));
$last_ip = str_replace($range_match[0], $range_end, $test_range);
$last_hex = bin2hex(inet_pton($last_ip));
// check if the IP to test is within the range
$result = ($ip_hex >= $first_hex && $ip_hex <= $last_hex);
}
} elseif ($is_cidr) {
// Split in address and prefix length
list($first_ip, $subnet) = explode('/', $test_range);
// Parse the address into a binary string
$first_bin = inet_pton($first_ip);
$first_hex = bin2hex($first_bin);
// Overwriting first address string to make sure notation is optimal
$first_ip = inet_ntop($first_bin);
$flexbits = 128 - $subnet;
// Build the hexadecimal string of the last address
$last_hex = $first_hex;
$pos = 31;
while ($flexbits > 0) {
// Get the character at this position
$orig = substr($last_hex, $pos, 1);
// Convert it to an integer
$origval = hexdec($orig);
// OR it with (2^flexbits)-1, with flexbits limited to 4 at a time
$newval = $origval | (pow(2, min(4, $flexbits)) - 1);
// Convert it back to a hexadecimal character
$new = dechex($newval);
// And put that character back in the string
$last_hex = substr_replace($last_hex, $new, $pos, 1);
// We processed one nibble, move to previous position
$flexbits -= 4;
$pos -= 1;
}
// check if the IP to test is within the range
$result = ($ip_hex >= $first_hex && $ip_hex <= $last_hex);
}
return $result;
} // end of the "PMA_ipv6MaskTest()" function
/**
* Runs through IP Allow/Deny rules the use of it below for more information
*
* @param string $type 'allow' | 'deny' type of rule to match
*
* @return bool Matched a rule ?
*
* @access public
*
* @see PMA_getIp()
*/
function PMA_allowDeny($type)
{
global $cfg;
// Grabs true IP of the user and returns if it can't be found
$remote_ip = PMA_getIp();
if (empty($remote_ip)) {
return false;
}
// copy username
$username = $cfg['Server']['user'];
// copy rule database
$rules = $cfg['Server']['AllowDeny']['rules'];
// lookup table for some name shortcuts
$shortcuts = array(
'all' => '0.0.0.0/0',
'localhost' => '127.0.0.1/8'
);
// Provide some useful shortcuts if server gives us address:
if (PMA_getenv('SERVER_ADDR')) {
$shortcuts['localnetA'] = PMA_getenv('SERVER_ADDR') . '/8';
$shortcuts['localnetB'] = PMA_getenv('SERVER_ADDR') . '/16';
$shortcuts['localnetC'] = PMA_getenv('SERVER_ADDR') . '/24';
}
foreach ($rules as $rule) {
// extract rule data
$rule_data = explode(' ', $rule);
// check for rule type
if ($rule_data[0] != $type) {
continue;
}
// check for username
if (($rule_data[1] != '%') //wildcarded first
&& ($rule_data[1] != $username)
) {
continue;
}
// check if the config file has the full string with an extra
// 'from' in it and if it does, just discard it
if ($rule_data[2] == 'from') {
$rule_data[2] = $rule_data[3];
}
// Handle shortcuts with above array
if (isset($shortcuts[$rule_data[2]])) {
$rule_data[2] = $shortcuts[$rule_data[2]];
}
// Add code for host lookups here
// Excluded for the moment
// Do the actual matching now
if (PMA_ipMaskTest($rule_data[2], $remote_ip)) {
return true;
}
} // end while
return false;
} // end of the "PMA_AllowDeny()" function
?>