PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.0
Patchstack – WordPress & Plugins Security v2.2.0
2.3.7 trunk 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19 2.1.2 2.1.20 2.1.21 2.1.22 2.1.23 2.1.24 2.1.25 2.1.3 2.1.4 2.1.5 2.1.6 All 49 releases
patchstack / includes / core.php

core.php in Patchstack – WordPress & Plugins Security 2.2.0, at includes/core.php

429 lines 9.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Do not allow the file to be called directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * The core class is used as a base class for all the other classes.
10 * This will allow us to declare certain global methods/variables.
11 */
12 class P_Core {
13
14 /**
15 * This will allow us to communicate between classes.
16 *
17 * @var Patchstack
18 */
19 public $plugin;
20
21 /**
22 * Whether or not the site is a multisite.
23 *
24 * @var boolean
25 */
26 private $is_multi_site = false;
27
28 /**
29 * Allowed HTML for the wp_kses function used to render certain paragraphs of texts.
30 *
31 * @var array
32 */
33 public $allowed_html = [
34 'a' => [
35 'href' => [],
36 'title' => [],
37 'target' => []
38 ],
39 'p' => [
40 'style' => []
41 ],
42 'span' => [
43 'style' => []
44 ],
45 'br' => [],
46 'strong' => [],
47 'b' => [],
48 'i' => [
49 'style' => []
50 ],
51 'label' => [
52 'for' => [],
53 'style' => []
54 ],
55 'input' => [
56 'type' => [],
57 'class' => [],
58 'name' => [],
59 'id' => [],
60 'value' => [],
61 'checked' => [],
62 'style' => []
63 ],
64 'textarea' => [
65 'rows' => [],
66 'id' => [],
67 'name' => []
68 ],
69 'select' => [
70 'name' => [],
71 'id' => [],
72 'data-selected' => []
73 ],
74 'option' => [
75 'value' => [],
76 'selected' => []
77 ],
78 'table' => [
79 'class' => [],
80 'style' => []
81 ],
82 'thead' => [],
83 'th' => [
84 'style' => []
85 ],
86 'tr' => [],
87 'td' => [],
88 'div' => [
89 'class' => [],
90 'style' => []
91 ]
92 ];
93
94 /**
95 * Some of the IP addresses of Patchstack.
96 *
97 * @var array
98 */
99 public $ips = [
100 '18.221.197.243',
101 '52.15.237.250',
102 '3.19.3.34',
103 '3.18.238.17',
104 '13.58.49.77',
105 '18.222.191.77',
106 '3.131.108.250',
107 '3.23.157.140',
108 '18.220.70.233',
109 '3.140.84.221',
110 '185.212.171.100',
111 '3.133.121.93',
112 '18.219.61.133',
113 '3.14.29.150'
114 ];
115
116 /**
117 * @param Patchstack $plugin
118 * @return void
119 */
120 public function __construct( $plugin ) {
121 $this->plugin = $plugin;
122 $this->is_multi_site = is_multisite();
123 }
124
125 /**
126 * In case of multisite we want to determine if there's a difference between the
127 * network setting and site setting and if so, use the site setting.
128 *
129 * @param string $name
130 * @param mixed $default
131 * @return mixed
132 */
133 public function get_option( $name, $default = false ) {
134 // We always want to return the site option on the default settings management page.
135 if ( isset( $_GET['page'] ) && $_GET['page'] == 'patchstack-multisite-settings' && is_super_admin() ) {
136 return get_site_option( $name, $default );
137 }
138
139 // Get the setting of the current site.
140 $secondary = get_option( $name, $default );
141
142 // Get the setting of the network and in case there's a difference,
143 // return the value of site.
144 $main = get_site_option( $name, $default );
145 return $main != $secondary ? $secondary : $main;
146 }
147
148 /**
149 * In case we need to retrieve the option of a specific site, we can use this.
150 * It will determine if it's on a multisite environment and if so, use get_blog_option.
151 *
152 * @param int $site_id
153 * @param string $name
154 * @param mixed $default
155 * @return mixed
156 */
157 public function get_blog_option( $site_id, $name, $default = false ) {
158 if ( $this->is_multi_site ) {
159 return get_blog_option( $site_id, $name, $default );
160 }
161
162 return get_option( $name, $default );
163 }
164
165 /**
166 * In case we need to update the option of a specific site, we can use this.
167 * It will determine if it's on a multisite environment and if so, use update_blog_option.
168 *
169 * @param int $site_id
170 * @param string $name
171 * @param mixed $value
172 * @return mixed
173 */
174 public function update_blog_option( $site_id, $name, $value ) {
175 if ( $this->is_multi_site ) {
176 return update_blog_option( $site_id, $name, $value );
177 }
178
179 return update_option( $name, $value );
180 }
181
182 /**
183 * Determine if the license is active and not expired.
184 *
185 * @return boolean
186 */
187 public function license_is_active() {
188 if ( get_option( 'patchstack_license_activated', 0 ) ) {
189 return true;
190 }
191
192 $expiry = get_option( 'patchstack_license_expiry', '' );
193 if ( $expiry != '' && ( strtotime( $expiry ) < ( time() + ( 3600 * 24 ) ) ) ) {
194 return true;
195 }
196
197 return false;
198 }
199
200 /**
201 * Convert the subscription class name to its text variant.
202 *
203 * @param int $class
204 * @return string
205 */
206 public function get_subscription_name( $class ) {
207 switch ( $class ) {
208 case 0:
209 return 'Community';
210 case 1:
211 case 6:
212 return 'Developer';
213 case 7:
214 return 'Business';
215 default:
216 return 'Unknown';
217 }
218 }
219
220 /**
221 * Determine if the user is a community user.
222 *
223 * @return boolean
224 */
225 public function is_community() {
226 $class = get_option( 'patchstack_subscription_class', '');
227 return $class != '' && (int) $class === 0;
228 }
229
230 /**
231 * Determine if the plugin is connected to the API.
232 *
233 * @return boolean
234 */
235 public function is_connected() {
236 // Determine if the API client id is set.
237 if ( $this->plugin->client_id == 'PATCHSTACK_CLIENT_ID' && get_option( 'patchstack_clientid', false ) === false ) {
238 return false;
239 }
240
241 // Determine if we have an API token.
242 if ( get_option( 'patchstack_api_token', '' ) == '' ) {
243 return false;
244 }
245
246 // Determine if we have a last license check set.
247 $last_license_check = get_option( 'patchstack_last_license_check', 0 );
248 if ( !empty( $last_license_check ) && time() - $last_license_check >= 604800 ) {
249 return false;
250 }
251
252 return true;
253 }
254
255 /**
256 * Determine if the plugin provides protection.
257 *
258 * @return boolean
259 */
260 public function is_protected() {
261 return get_option( 'patchstack_license_free', false) == 0;
262 }
263
264 /**
265 * Grab the IP address of the user. Give the override IP header priority.
266 * If this does not exist, we should always default to REMOTE_ADDR.
267 *
268 * @return string
269 */
270 public function get_ip() {
271 $override = get_site_option( 'patchstack_firewall_ip_header', '' );
272 if ( $override != '' && isset( $_SERVER[ $override ] ) ) {
273 return $_SERVER[ $override ];
274 }
275
276 return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
277 }
278
279 /**
280 * Grab the secret key used for API communication.
281 *
282 * @param string $custom
283 * @return string
284 */
285 public function get_secret_key( $custom = '' ) {
286 if ( $custom != '' ) {
287 return $this->encrypt( $custom );
288 }
289
290 $secret = get_option( 'patchstack_secretkey', '' );
291 if ( ! $secret ) {
292 return '';
293 }
294
295 if ( strlen( $secret ) === 40 ) {
296 $enc = $this->encrypt( $secret );
297
298 update_option( 'patchstack_secretkey', $enc['cipher'] );
299 update_option( 'patchstack_secretkey_nonce', $enc['nonce'] );
300
301 return $secret;
302 }
303
304 $nonce = get_option( 'patchstack_secretkey_nonce' );
305 return $this->decrypt( $secret, $nonce );
306 }
307
308 /**
309 * Set the secret key used for API communication.
310 *
311 * @param string $secret
312 * @return void
313 */
314 public function set_secret_key( $secret ) {
315 $enc = $this->encrypt( $secret );
316
317 update_option( 'patchstack_secretkey', $enc['cipher'] );
318 update_option( 'patchstack_secretkey_nonce', $enc['nonce'] );
319 }
320
321 /**
322 * Determine which encryption dependency we can use.
323 *
324 * @return string
325 */
326 public function get_enc_type() {
327 if ( function_exists('sodium_crypto_generichash') ) {
328 return 'native';
329 }
330
331 return 'compat';
332 }
333
334 /**
335 * Get the unique nonce that is used for the secretbox.
336 *
337 * @return string
338 */
339 public function get_enc_nonce() {
340 if ( function_exists('random_bytes') ) {
341 return random_bytes( 24 );
342 }
343
344 require_once dirname( __FILE__ ) . '/2fa/polyfill/lib/random.php';
345 return random_bytes( 24 );
346 }
347
348 /**
349 * Encrypt a string.
350 *
351 * @param string $message
352 * @return array
353 */
354 public function encrypt( $message ) {
355 if ( is_null( $message ) || ! defined( 'AUTH_KEY' ) ) {
356 return [
357 'cipher' => $message,
358 'nonce' => ''
359 ];
360 }
361
362 $enc_type = $this->get_enc_type();
363 $nonce = $this->get_enc_nonce();
364
365 try {
366 // Use the PHP native encryption functions.
367 if ( $enc_type == 'native' ) {
368 $key = sodium_crypto_generichash( AUTH_KEY );
369
370 return [
371 'cipher' => sodium_bin2hex( sodium_crypto_secretbox( $message, $nonce, $key ) ),
372 'nonce' => sodium_bin2hex( $nonce )
373 ];
374 }
375
376 // Use the Sodium polyfill library part of WordPress core.
377 require_once ABSPATH . WPINC . '/sodium_compat/autoload.php';
378 $key = \Sodium\crypto_generichash( AUTH_KEY );
379
380 return [
381 'cipher' => \Sodium\bin2hex( \Sodium\crypto_secretbox( $message, $nonce, $key ) ),
382 'nonce' => \Sodium\bin2hex( $nonce )
383 ];
384 } catch ( Exception $e ) {
385 return [
386 'cipher' => $message,
387 'nonce' => ''
388 ];
389 }
390 }
391
392 /**
393 * Decrypt a cipher to plain-text.
394 *
395 * @param string $cipher
396 * @param string $nonce
397 * @return string
398 */
399 public function decrypt( $cipher, $nonce ) {
400 $enc_type = $this->get_enc_type();
401
402 // If we received an empty nonce, we assume it was never properly encrypted to begin with.
403 if ( $nonce == '' || ! defined( 'AUTH_KEY' ) ) {
404 return $cipher;
405 }
406
407 try {
408 // Determine if we should use native or polyfill functions.
409 if ( $enc_type == 'native' ) {
410 $key = sodium_crypto_generichash( AUTH_KEY );
411 $dec = sodium_crypto_secretbox_open( sodium_hex2bin( $cipher ), sodium_hex2bin( $nonce ), $key );
412 } else {
413 require_once ABSPATH . WPINC . '/sodium_compat/autoload.php';
414 $key = \Sodium\crypto_generichash( AUTH_KEY );
415 $dec = \Sodium\crypto_secretbox_open( sodium_hex2bin( $cipher ), sodium_hex2bin( $nonce ), $key );
416 }
417 } catch ( Exception $e ) {
418 return $cipher;
419 }
420
421 // In case decryption failed, return null.
422 if ( ! $dec ) {
423 return null;
424 }
425
426 return $dec;
427 }
428 }
429