PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 10.1.1
Jetpack – WP Security, Backup, Speed, & Growth v10.1.1
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / modules / protect / shared-functions.php
shared-functions.php
311 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * These functions are shared by the Protect module and its related json-endpoints
4 */
5 /**
6 * Returns an array of IP objects that will never be blocked by the Protect module
7 *
8 * The array is segmented into a local whitelist which applies only to the current site
9 * and a global whitelist which, for multisite installs, applies to the entire networko
10 *
11 * @return array
12 */
13 function jetpack_protect_format_whitelist() {
14 $local_whitelist = jetpack_protect_get_local_whitelist();
15 $formatted = array(
16 'local' => array(),
17 );
18 foreach ( $local_whitelist as $item ) {
19 if ( $item->range ) {
20 $formatted['local'][] = $item->range_low . ' - ' . $item->range_high;
21 } else {
22 $formatted['local'][] = $item->ip_address;
23 }
24 }
25 if ( is_multisite() && current_user_can( 'manage_network' ) ) {
26 $formatted['global'] = array();
27 $global_whitelist = jetpack_protect_get_global_whitelist();
28 if ( false === $global_whitelist ) {
29 // If the global whitelist has never been set, check for a legacy option set prior to 3.6.
30 $global_whitelist = get_site_option( 'jetpack_protect_whitelist', array() );
31 }
32 foreach ( $global_whitelist as $item ) {
33 if ( $item->range ) {
34 $formatted['global'][] = $item->range_low . ' - ' . $item->range_high;
35 } else {
36 $formatted['global'][] = $item->ip_address;
37 }
38 }
39 }
40 return $formatted;
41 }
42 /**
43 * Gets the local Protect whitelist
44 *
45 * The 'local' part of the whitelist only really applies to multisite installs,
46 * which can have a network wide whitelist, as well as a local list that applies
47 * only to the current site. On single site installs, there will only be a local
48 * whitelist.
49 *
50 * @return array A list of IP Address objects or an empty array
51 */
52 function jetpack_protect_get_local_whitelist() {
53 $whitelist = Jetpack_Options::get_option( 'protect_whitelist' );
54 if ( false === $whitelist ) {
55 // The local whitelist has never been set.
56 if ( is_multisite() ) {
57 // On a multisite, we can check for a legacy site_option that existed prior to v 3.6, or default to an empty array.
58 $whitelist = get_site_option( 'jetpack_protect_whitelist', array() );
59 } else {
60 // On a single site, we can just use an empty array.
61 $whitelist = array();
62 }
63 }
64 return $whitelist;
65 }
66
67 /**
68 * Get the global, network-wide whitelist
69 *
70 * It will revert to the legacy site_option if jetpack_protect_global_whitelist has never been set.
71 *
72 * @return array
73 */
74 function jetpack_protect_get_global_whitelist() {
75 $whitelist = get_site_option( 'jetpack_protect_global_whitelist' );
76 if ( false === $whitelist ) {
77 // The global whitelist has never been set. Check for legacy site_option, or default to an empty array.
78 $whitelist = get_site_option( 'jetpack_protect_whitelist', array() );
79 }
80 return $whitelist;
81 }
82
83 /**
84 * Jetpack Protect Save Whitelist.
85 *
86 * @access public
87 * @param mixed $whitelist Whitelist.
88 * @param bool $global (default: false) Global.
89 * @return Bool.
90 */
91 function jetpack_protect_save_whitelist( $whitelist, $global = false ) {
92 $whitelist_error = false;
93 $new_items = array();
94 if ( ! is_array( $whitelist ) ) {
95 return new WP_Error( 'invalid_parameters', __( 'Expecting an array', 'jetpack' ) );
96 }
97 if ( $global && ! is_multisite() ) {
98 return new WP_Error( 'invalid_parameters', __( 'Cannot use global flag on non-multisites', 'jetpack' ) );
99 }
100 if ( $global && ! current_user_can( 'manage_network' ) ) {
101 return new WP_Error( 'permission_denied', __( 'Only super admins can edit the global whitelist', 'jetpack' ) );
102 }
103 // Validate each item.
104 foreach ( $whitelist as $item ) {
105 $item = trim( $item );
106 if ( empty( $item ) ) {
107 continue;
108 }
109 $range = false;
110 if ( strpos( $item, '-' ) ) {
111 $item = explode( '-', $item );
112 $range = true;
113 }
114 $new_item = new stdClass();
115 $new_item->range = $range;
116 if ( ! empty( $range ) ) {
117 $low = trim( $item[0] );
118 $high = trim( $item[1] );
119 if ( ! filter_var( $low, FILTER_VALIDATE_IP ) || ! filter_var( $high, FILTER_VALIDATE_IP ) ) {
120 $whitelist_error = true;
121 break;
122 }
123 if ( ! jetpack_convert_ip_address( $low ) || ! jetpack_convert_ip_address( $high ) ) {
124 $whitelist_error = true;
125 break;
126 }
127 $new_item->range_low = $low;
128 $new_item->range_high = $high;
129 } else {
130 if ( ! filter_var( $item, FILTER_VALIDATE_IP ) ) {
131 $whitelist_error = true;
132 break;
133 }
134 if ( ! jetpack_convert_ip_address( $item ) ) {
135 $whitelist_error = true;
136 break;
137 }
138 $new_item->ip_address = $item;
139 }
140 $new_items[] = $new_item;
141 } // End item loop.
142 if ( ! empty( $whitelist_error ) ) {
143 return new WP_Error( 'invalid_ip', __( 'One of your IP addresses was not valid.', 'jetpack' ) );
144 }
145 if ( $global ) {
146 update_site_option( 'jetpack_protect_global_whitelist', $new_items );
147 // Once a user has saved their global whitelist, we can permanently remove the legacy option.
148 delete_site_option( 'jetpack_protect_whitelist' );
149 } else {
150 Jetpack_Options::update_option( 'protect_whitelist', $new_items );
151 }
152 return true;
153 }
154
155 /**
156 * Jetpack Protect Get IP.
157 *
158 * @access public
159 * @return string|false IP.
160 */
161 function jetpack_protect_get_ip() {
162 $trusted_header_data = get_site_option( 'trusted_ip_header' );
163 if ( isset( $trusted_header_data->trusted_header ) && isset( $_SERVER[ $trusted_header_data->trusted_header ] ) ) {
164 $ip = $_SERVER[ $trusted_header_data->trusted_header ];
165 $segments = $trusted_header_data->segments;
166 $reverse_order = $trusted_header_data->reverse;
167 } else {
168 $ip = $_SERVER['REMOTE_ADDR'];
169 }
170
171 if ( ! $ip ) {
172 return false;
173 }
174
175 $ips = explode( ',', $ip );
176 if ( ! isset( $segments ) || ! $segments ) {
177 $segments = 1;
178 }
179 if ( isset( $reverse_order ) && $reverse_order ) {
180 $ips = array_reverse( $ips );
181 }
182 $ip_count = count( $ips );
183 if ( 1 === $ip_count ) {
184 return jetpack_clean_ip( $ips[0] );
185 } elseif ( $ip_count >= $segments ) {
186 $the_one = $ip_count - $segments;
187 return jetpack_clean_ip( $ips[ $the_one ] );
188 } else {
189 return jetpack_clean_ip( $_SERVER['REMOTE_ADDR'] );
190 }
191 }
192
193 /**
194 * Jetpack Clean IP.
195 *
196 * @access public
197 * @param string $ip IP.
198 * @return string|false IP.
199 */
200 function jetpack_clean_ip( $ip ) {
201
202 // Some misconfigured servers give back extra info, which comes after "unless".
203 $ips = explode( ' unless ', $ip );
204 $ip = $ips[0];
205
206 $ip = strtolower( trim( $ip ) );
207
208 // Check for IPv4 with port.
209 if ( preg_match( '/^(\d+\.\d+\.\d+\.\d+):\d+$/', $ip, $matches ) ) {
210 $ip = $matches[1];
211 }
212
213 // Check for IPv6 (or IPvFuture) with brackets and optional port.
214 if ( preg_match( '/^\[([a-z0-9\-._~!$&\'()*+,;=:]+)\](?::\d+)?$/', $ip, $matches ) ) {
215 $ip = $matches[1];
216 }
217
218 // Check for IPv4 IP cast as IPv6.
219 if ( preg_match( '/^::ffff:(\d+\.\d+\.\d+\.\d+)$/', $ip, $matches ) ) {
220 $ip = $matches[1];
221 }
222
223 // Validate and return.
224 return filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : false;
225 }
226
227 /**
228 * Checks an IP to see if it is within a private range.
229 *
230 * @param int $ip IP.
231 * @return bool
232 */
233 function jetpack_protect_ip_is_private( $ip ) {
234 // We are dealing with ipv6, so we can simply rely on filter_var.
235 if ( false === strpos( $ip, '.' ) ) {
236 return ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE );
237 }
238 // We are dealing with ipv4.
239 $private_ip4_addresses = array(
240 '10.0.0.0|10.255.255.255', // Single class A network.
241 '172.16.0.0|172.31.255.255', // 16 contiguous class B network.
242 '192.168.0.0|192.168.255.255', // 256 contiguous class C network.
243 '169.254.0.0|169.254.255.255', // Link-local address also referred to as Automatic Private IP Addressing.
244 '127.0.0.0|127.255.255.255', // localhost.
245 );
246 $long_ip = ip2long( $ip );
247 if ( -1 !== $long_ip ) {
248 foreach ( $private_ip4_addresses as $pri_addr ) {
249 list ( $start, $end ) = explode( '|', $pri_addr );
250 if ( $long_ip >= ip2long( $start ) && $long_ip <= ip2long( $end ) ) {
251 return true;
252 }
253 }
254 }
255 return false;
256 }
257
258 /**
259 * Uses inet_pton if available to convert an IP address to a binary string.
260 * If inet_pton is not available, ip2long will convert the address to an integer.
261 * Returns false if an invalid IP address is given.
262 *
263 * NOTE: ip2long will return false for any ipv6 address. servers that do not support
264 * inet_pton will not support ipv6
265 *
266 * @access public
267 * @param mixed $ip IP.
268 * @return int|string|bool
269 */
270 function jetpack_convert_ip_address( $ip ) {
271 if ( function_exists( 'inet_pton' ) ) {
272 return inet_pton( $ip );
273 }
274 return ip2long( $ip );
275 }
276
277 /**
278 * Checks that a given IP address is within a given low - high range.
279 * Servers that support inet_pton will use that function to convert the ip to number,
280 * while other servers will use ip2long.
281 *
282 * NOTE: servers that do not support inet_pton cannot support ipv6.
283 *
284 * @access public
285 * @param mixed $ip IP.
286 * @param mixed $range_low Range Low.
287 * @param mixed $range_high Range High.
288 * @return Bool.
289 */
290 function jetpack_protect_ip_address_is_in_range( $ip, $range_low, $range_high ) {
291 // The inet_pton will give us binary string of an ipv4 or ipv6.
292 // We can then use strcmp to see if the address is in range.
293 if ( function_exists( 'inet_pton' ) ) {
294 $ip_num = inet_pton( $ip );
295 $ip_low = inet_pton( $range_low );
296 $ip_high = inet_pton( $range_high );
297 if ( $ip_num && $ip_low && $ip_high && strcmp( $ip_num, $ip_low ) >= 0 && strcmp( $ip_num, $ip_high ) <= 0 ) {
298 return true;
299 }
300 // The ip2long will give us an integer of an ipv4 address only. it will produce FALSE for ipv6.
301 } else {
302 $ip_num = ip2long( $ip );
303 $ip_low = ip2long( $range_low );
304 $ip_high = ip2long( $range_high );
305 if ( $ip_num && $ip_low && $ip_high && $ip_num >= $ip_low && $ip_num <= $ip_high ) {
306 return true;
307 }
308 }
309 return false;
310 }
311