2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 * This library is used with the server IP allow/deny host authentication
11 * Matches for IPv4 or IPv6 addresses
13 * @param string $testRange string of IP range to match
14 * @param string $ipToTest string of IP to test against range
16 * @return boolean whether the IP mask matches
20 function PMA_ipMaskTest($testRange, $ipToTest)
22 if (mb_strpos($testRange, ':') > -1
23 ||
mb_strpos($ipToTest, ':') > -1
26 $result = PMA_ipv6MaskTest($testRange, $ipToTest);
28 $result = PMA_ipv4MaskTest($testRange, $ipToTest);
32 } // end of the "PMA_ipMaskTest()" function
36 * Based on IP Pattern Matcher
37 * Originally by J.Adams <jna@retina.net>
38 * Found on <https://secure.php.net/manual/en/function.ip2long.php>
39 * Modified for phpMyAdmin
42 * xxx.xxx.xxx.xxx (exact)
43 * xxx.xxx.xxx.[yyy-zzz] (range)
44 * xxx.xxx.xxx.xxx/nn (CIDR)
47 * xxx.xxx.xxx.xx[yyy-zzz] (range, partial octets not supported)
49 * @param string $testRange string of IP range to match
50 * @param string $ipToTest string of IP to test against range
52 * @return boolean whether the IP mask matches
56 function PMA_ipv4MaskTest($testRange, $ipToTest)
60 '|([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/([0-9]+)|',
65 // performs a mask match
66 $ipl = ip2long($ipToTest);
68 $regs[1] . '.' . $regs[2] . '.' . $regs[3] . '.' . $regs[4]
73 for ($i = 0; $i < 31; $i++
) {
74 if ($i < $regs[5] - 1) {
75 $maskl = $maskl +
pow(2, (30 - $i));
79 if (($maskl & $rangel) == ($maskl & $ipl)) {
87 $maskocts = explode('.', $testRange);
88 $ipocts = explode('.', $ipToTest);
90 // perform a range match
91 for ($i = 0; $i < 4; $i++
) {
92 if (preg_match('|\[([0-9]+)\-([0-9]+)\]|', $maskocts[$i], $regs)) {
93 if (($ipocts[$i] > $regs[2]) ||
($ipocts[$i] < $regs[1])) {
97 if ($maskocts[$i] <> $ipocts[$i]) {
104 } // end of the "PMA_ipv4MaskTest()" function
109 * CIDR section taken from https://stackoverflow.com/a/10086404
110 * Modified for phpMyAdmin
113 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
115 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:[yyyy-zzzz]
116 * (range, only at end of IP - no subnets)
117 * xxxx:xxxx:xxxx:xxxx/nn
121 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xx[yyy-zzz]
122 * (range, partial octets not supported)
124 * @param string $test_range string of IP range to match
125 * @param string $ip_to_test string of IP to test against range
127 * @return boolean whether the IP mask matches
131 function PMA_ipv6MaskTest($test_range, $ip_to_test)
135 // convert to lowercase for easier comparison
136 $test_range = mb_strtolower($test_range);
137 $ip_to_test = mb_strtolower($ip_to_test);
139 $is_cidr = mb_strpos($test_range, '/') > -1;
140 $is_range = mb_strpos($test_range, '[') > -1;
141 $is_single = ! $is_cidr && ! $is_range;
143 $ip_hex = bin2hex(inet_pton($ip_to_test));
146 $range_hex = bin2hex(inet_pton($test_range));
147 $result = hash_equals($ip_hex, $range_hex);
152 // what range do we operate on?
153 $range_match = array();
155 '/\[([0-9a-f]+)\-([0-9a-f]+)\]/', $test_range, $range_match
158 $range_start = $range_match[1];
159 $range_end = $range_match[2];
161 // get the first and last allowed IPs
162 $first_ip = str_replace($range_match[0], $range_start, $test_range);
163 $first_hex = bin2hex(inet_pton($first_ip));
164 $last_ip = str_replace($range_match[0], $range_end, $test_range);
165 $last_hex = bin2hex(inet_pton($last_ip));
167 // check if the IP to test is within the range
168 $result = ($ip_hex >= $first_hex && $ip_hex <= $last_hex);
174 // Split in address and prefix length
175 list($first_ip, $subnet) = explode('/', $test_range);
177 // Parse the address into a binary string
178 $first_bin = inet_pton($first_ip);
179 $first_hex = bin2hex($first_bin);
181 $flexbits = 128 - $subnet;
183 // Build the hexadecimal string of the last address
184 $last_hex = $first_hex;
187 while ($flexbits > 0) {
188 // Get the character at this position
189 $orig = mb_substr($last_hex, $pos, 1);
191 // Convert it to an integer
192 $origval = hexdec($orig);
194 // OR it with (2^flexbits)-1, with flexbits limited to 4 at a time
195 $newval = $origval |
(pow(2, min(4, $flexbits)) - 1);
197 // Convert it back to a hexadecimal character
198 $new = dechex($newval);
200 // And put that character back in the string
201 $last_hex = substr_replace($last_hex, $new, $pos, 1);
203 // We processed one nibble, move to previous position
208 // check if the IP to test is within the range
209 $result = ($ip_hex >= $first_hex && $ip_hex <= $last_hex);
213 } // end of the "PMA_ipv6MaskTest()" function
217 * Runs through IP Allow/Deny rules the use of it below for more information
219 * @param string $type 'allow' | 'deny' type of rule to match
221 * @return bool Whether rule has matched
227 function PMA_allowDeny($type)
231 // Grabs true IP of the user and returns if it can't be found
232 $remote_ip = PMA_getIp();
233 if (empty($remote_ip)) {
238 $username = $cfg['Server']['user'];
240 // copy rule database
241 if (isset($cfg['Server']['AllowDeny']['rules'])) {
242 $rules = $cfg['Server']['AllowDeny']['rules'];
243 if (! is_array($rules)) {
250 // lookup table for some name shortcuts
252 'all' => '0.0.0.0/0',
253 'localhost' => '127.0.0.1/8'
256 // Provide some useful shortcuts if server gives us address:
257 if (PMA_getenv('SERVER_ADDR')) {
258 $shortcuts['localnetA'] = PMA_getenv('SERVER_ADDR') . '/8';
259 $shortcuts['localnetB'] = PMA_getenv('SERVER_ADDR') . '/16';
260 $shortcuts['localnetC'] = PMA_getenv('SERVER_ADDR') . '/24';
263 foreach ($rules as $rule) {
265 $rule_data = explode(' ', $rule);
267 // check for rule type
268 if ($rule_data[0] != $type) {
272 // check for username
273 if (($rule_data[1] != '%') //wildcarded first
274 && (! hash_equals($rule_data[1], $username))
279 // check if the config file has the full string with an extra
280 // 'from' in it and if it does, just discard it
281 if ($rule_data[2] == 'from') {
282 $rule_data[2] = $rule_data[3];
285 // Handle shortcuts with above array
286 if (isset($shortcuts[$rule_data[2]])) {
287 $rule_data[2] = $shortcuts[$rule_data[2]];
290 // Add code for host lookups here
291 // Excluded for the moment
293 // Do the actual matching now
294 if (PMA_ipMaskTest($rule_data[2], $remote_ip)) {
300 } // end of the "PMA_AllowDeny()" function