PluginProbe
Patchstack – WordPress & Plugins Security / 2.3.6
Patchstack – WordPress & Plugins Security v2.3.6
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.3.6, at includes/core.php

377 lines 9.0 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 public $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 * @param Patchstack $plugin
96 * @return void
97 */
98 public function __construct( $plugin ) {
99 $this->plugin = $plugin;
100 $this->is_multi_site = is_multisite();
101 }
102
103 /**
104 * In case of multisite we want to determine if there's a difference between the
105 * network setting and site setting and if so, use the site setting.
106 *
107 * @param string $name
108 * @param mixed $default
109 * @return mixed
110 */
111 public function get_option( $name, $default = false ) {
112 // We always want to return the site option on the default settings management page.
113 if ( isset( $_GET['page'] ) && $_GET['page'] == 'patchstack-multisite-settings' && function_exists( 'wp_get_current_user' ) && is_super_admin() ) {
114 return get_site_option( $name, $default );
115 }
116
117 // Get the setting of the current site.
118 $secondary = get_option( $name, $default );
119
120 // Get the setting of the network and in case there's a difference,
121 // return the value of site.
122 $main = get_site_option( $name, $default );
123 return $main != $secondary ? $secondary : $main;
124 }
125
126 /**
127 * In case we need to retrieve the option of a specific site, we can use this.
128 * It will determine if it's on a multisite environment and if so, use get_blog_option.
129 *
130 * @param int $site_id
131 * @param string $name
132 * @param mixed $default
133 * @return mixed
134 */
135 public function get_blog_option( $site_id, $name, $default = false ) {
136 if ( $this->is_multi_site ) {
137 return get_blog_option( $site_id, $name, $default );
138 }
139
140 return get_option( $name, $default );
141 }
142
143 /**
144 * In case we need to update the option of a specific site, we can use this.
145 * It will determine if it's on a multisite environment and if so, use update_blog_option.
146 *
147 * @param int $site_id
148 * @param string $name
149 * @param mixed $value
150 * @return mixed
151 */
152 public function update_blog_option( $site_id, $name, $value ) {
153 if ( $this->is_multi_site ) {
154 return update_blog_option( $site_id, $name, $value );
155 }
156
157 return update_option( $name, $value );
158 }
159
160 /**
161 * Determine if the license is active and not expired.
162 *
163 * @return boolean
164 */
165 public function license_is_active() {
166 if ( get_option( 'patchstack_license_activated', 0 ) ) {
167 return true;
168 }
169
170 $expiry = get_option( 'patchstack_license_expiry', '' );
171 if ( $expiry != '' && ( strtotime( $expiry ) < ( time() + ( 3600 * 24 ) ) ) ) {
172 return true;
173 }
174
175 return false;
176 }
177
178 /**
179 * Determine if the plugin is connected to the API.
180 *
181 * @return boolean
182 */
183 public function is_connected() {
184 // Determine if the API client id is set.
185 if ( $this->plugin->client_id == 'PATCHSTACK_CLIENT_ID' && get_option( 'patchstack_clientid', false ) === false ) {
186 return false;
187 }
188
189 // Determine if we have an API token.
190 if ( get_option( 'patchstack_api_token', '' ) == '' ) {
191 return false;
192 }
193
194 // Determine if we have a last license check set.
195 $last_license_check = get_option( 'patchstack_last_license_check', 0 );
196 if ( !empty( $last_license_check ) && time() - $last_license_check >= 604800 ) {
197 return false;
198 }
199
200 return true;
201 }
202
203 /**
204 * Determine if the plugin provides protection.
205 *
206 * @return boolean
207 */
208 public function is_protected() {
209 return get_option( 'patchstack_license_free', false) == 0;
210 }
211
212 /**
213 * Grab the IP address of the user. Give the override IP header priority.
214 * If this does not exist, we should always default to REMOTE_ADDR.
215 *
216 * @return string
217 */
218 public function get_ip() {
219 $override = get_option( 'patchstack_firewall_ip_header', '' );
220 if ( $override != '' && isset( $_SERVER[ $override ] ) ) {
221 return $_SERVER[ $override ];
222 }
223
224 return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
225 }
226
227 /**
228 * Grab the secret key used for API communication.
229 *
230 * @param string $custom
231 * @return string
232 */
233 public function get_secret_key( $custom = '' ) {
234 if ( $custom != '' ) {
235 return $this->encrypt( $custom );
236 }
237
238 $secret = get_option( 'patchstack_secretkey', '' );
239 if ( ! $secret ) {
240 return '';
241 }
242
243 if ( strlen( $secret ) === 40 ) {
244 $enc = $this->encrypt( $secret );
245
246 update_option( 'patchstack_secretkey', $enc['cipher'] );
247 update_option( 'patchstack_secretkey_nonce', $enc['nonce'] );
248
249 return $secret;
250 }
251
252 $nonce = get_option( 'patchstack_secretkey_nonce' );
253 return $this->decrypt( $secret, $nonce );
254 }
255
256 /**
257 * Set the secret key used for API communication.
258 *
259 * @param string $secret
260 * @return void
261 */
262 public function set_secret_key( $secret ) {
263 $enc = $this->encrypt( $secret );
264
265 update_option( 'patchstack_secretkey', $enc['cipher'] );
266 update_option( 'patchstack_secretkey_nonce', $enc['nonce'] );
267 }
268
269 /**
270 * Determine which encryption dependency we can use.
271 *
272 * @return string
273 */
274 public function get_enc_type() {
275 if ( function_exists('sodium_crypto_generichash') ) {
276 return 'native';
277 }
278
279 return 'compat';
280 }
281
282 /**
283 * Get the unique nonce that is used for the secretbox.
284 *
285 * @return string
286 */
287 public function get_enc_nonce() {
288 if ( function_exists('random_bytes') ) {
289 return random_bytes( 24 );
290 }
291
292 require_once dirname( __FILE__ ) . '/2fa/polyfill/lib/random.php';
293 return random_bytes( 24 );
294 }
295
296 /**
297 * Encrypt a string.
298 *
299 * @param string $message
300 * @return array
301 */
302 public function encrypt( $message ) {
303 if ( is_null( $message ) || ! defined( 'AUTH_KEY' ) ) {
304 return [
305 'cipher' => $message,
306 'nonce' => ''
307 ];
308 }
309
310 $enc_type = $this->get_enc_type();
311 $nonce = $this->get_enc_nonce();
312
313 try {
314 // Use the PHP native encryption functions.
315 if ( $enc_type == 'native' ) {
316 $key = sodium_crypto_generichash( AUTH_KEY );
317
318 return [
319 'cipher' => sodium_bin2hex( sodium_crypto_secretbox( $message, $nonce, $key ) ),
320 'nonce' => sodium_bin2hex( $nonce )
321 ];
322 }
323
324 // Use the Sodium polyfill library part of WordPress core.
325 require_once ABSPATH . WPINC . '/sodium_compat/autoload.php';
326 $key = \Sodium\crypto_generichash( AUTH_KEY );
327
328 return [
329 'cipher' => \Sodium\bin2hex( \Sodium\crypto_secretbox( $message, $nonce, $key ) ),
330 'nonce' => \Sodium\bin2hex( $nonce )
331 ];
332 } catch ( Exception $e ) {
333 return [
334 'cipher' => $message,
335 'nonce' => ''
336 ];
337 }
338 }
339
340 /**
341 * Decrypt a cipher to plain-text.
342 *
343 * @param string $cipher
344 * @param string $nonce
345 * @return string
346 */
347 public function decrypt( $cipher, $nonce ) {
348 $enc_type = $this->get_enc_type();
349
350 // If we received an empty nonce, we assume it was never properly encrypted to begin with.
351 if ( $nonce == '' || ! defined( 'AUTH_KEY' ) ) {
352 return $cipher;
353 }
354
355 try {
356 // Determine if we should use native or polyfill functions.
357 if ( $enc_type == 'native' ) {
358 $key = sodium_crypto_generichash( AUTH_KEY );
359 $dec = sodium_crypto_secretbox_open( sodium_hex2bin( $cipher ), sodium_hex2bin( $nonce ), $key );
360 } else {
361 require_once ABSPATH . WPINC . '/sodium_compat/autoload.php';
362 $key = \Sodium\crypto_generichash( AUTH_KEY );
363 $dec = \Sodium\crypto_secretbox_open( sodium_hex2bin( $cipher ), sodium_hex2bin( $nonce ), $key );
364 }
365 } catch ( Exception $e ) {
366 return $cipher;
367 }
368
369 // In case decryption failed, return null.
370 if ( ! $dec ) {
371 return null;
372 }
373
374 return $dec;
375 }
376 }
377