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