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

358 lines 8.6 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 = array(
34 'a' => array(
35 'href' => array(),
36 'title' => array(),
37 'target' => array()
38 ),
39 'p' => array(
40 'style' => array()
41 ),
42 'span' => array(
43 'style' => array()
44 ),
45 'br' => array(),
46 'strong' => array(),
47 'b' => array(),
48 'i' => array(
49 'style' => array()
50 ),
51 'label' => array(
52 'for' => array(),
53 'style' => array()
54 ),
55 'input' => array(
56 'type' => array(),
57 'class' => array(),
58 'name' => array(),
59 'id' => array(),
60 'value' => array(),
61 'checked' => array(),
62 'style' => array()
63 ),
64 'textarea' => array(
65 'rows' => array(),
66 'id' => array(),
67 'name' => array()
68 ),
69 'select' => array(
70 'name' => array(),
71 'id' => array(),
72 'data-selected' => array()
73 ),
74 'option' => array(
75 'value' => array(),
76 'selected' => array()
77 ),
78 'table' => array(
79 'class' => array(),
80 'style' => array()
81 ),
82 'thead' => array(),
83 'th' => array(
84 'style' => array()
85 ),
86 'tr' => array(),
87 'td' => array(),
88 'div' => array(
89 'class' => array(),
90 'style' => array()
91 )
92 );
93
94 /**
95 * Some of the IP addresses of Patchstack.
96 *
97 * @var array
98 */
99 public $ips = array(
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 * Grab the IP address of the user. Give the override IP header priority.
202 * If this does not exist, we should always default to REMOTE_ADDR.
203 *
204 * @return string
205 */
206 public function get_ip() {
207 $override = get_site_option( 'patchstack_firewall_ip_header', '' );
208 if ( $override != '' && isset( $_SERVER[ $override ] ) ) {
209 return $_SERVER[ $override ];
210 }
211
212 return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
213 }
214
215 /**
216 * Grab the secret key used for API communication.
217 *
218 * @param string $custom
219 * @return string
220 */
221 public function get_secret_key( $custom = '' ) {
222 if ( $custom != '' ) {
223 return $this->encrypt( $custom );
224 }
225
226 $secret = get_option( 'patchstack_secretkey', '' );
227 if ( ! $secret ) {
228 return '';
229 }
230
231 if ( strlen( $secret ) === 40 ) {
232 $enc = $this->encrypt( $secret );
233
234 update_option( 'patchstack_secretkey', $enc['cipher'] );
235 update_option( 'patchstack_secretkey_nonce', $enc['nonce'] );
236
237 return $secret;
238 }
239
240 $nonce = get_option( 'patchstack_secretkey_nonce' );
241 return $this->decrypt( $secret, $nonce );
242 }
243
244 /**
245 * Set the secret key used for API communication.
246 *
247 * @param string $secret
248 * @return void
249 */
250 public function set_secret_key( $secret ) {
251 $enc = $this->encrypt( $secret );
252
253 update_option( 'patchstack_secretkey', $enc['cipher'] );
254 update_option( 'patchstack_secretkey_nonce', $enc['nonce'] );
255 }
256
257 /**
258 * Determine which encryption dependency we can use.
259 *
260 * @return string
261 */
262 public function get_enc_type() {
263 if ( function_exists('sodium_crypto_generichash') ) {
264 return 'native';
265 }
266
267 return 'compat';
268 }
269
270 /**
271 * Get the unique nonce that is used for the secretbox.
272 *
273 * @return string
274 */
275 public function get_enc_nonce() {
276 if ( function_exists('random_bytes') ) {
277 return random_bytes( 24 );
278 }
279
280 require_once dirname( __FILE__ ) . '/2fa/polyfill/lib/random.php';
281 return random_bytes( 24 );
282 }
283
284 /**
285 * Encrypt a string.
286 *
287 * @param string $message
288 * @return array
289 */
290 public function encrypt( $message ) {
291 $enc_type = $this->get_enc_type();
292 $nonce = $this->get_enc_nonce();
293
294 try {
295 // Use the PHP native encryption functions.
296 if ( $enc_type == 'native' ) {
297 $key = sodium_crypto_generichash( AUTH_KEY );
298
299 return [
300 'cipher' => sodium_bin2hex( sodium_crypto_secretbox( $message, $nonce, $key ) ),
301 'nonce' => sodium_bin2hex( $nonce )
302 ];
303 }
304
305 // Use the Sodium polyfill library part of WordPress core.
306 require_once ABSPATH . WPINC . '/sodium_compat/autoload.php';
307 $key = \Sodium\crypto_generichash( AUTH_KEY );
308
309 return [
310 'cipher' => \Sodium\bin2hex( \Sodium\crypto_secretbox( $message, $nonce, $key ) ),
311 'nonce' => \Sodium\bin2hex( $nonce )
312 ];
313 } catch ( Exception $e ) {
314 return [
315 'cipher' => $message,
316 'nonce' => ''
317 ];
318 }
319 }
320
321 /**
322 * Decrypt a cipher to plain-text.
323 *
324 * @param string $cipher
325 * @param string $nonce
326 * @return string
327 */
328 public function decrypt( $cipher, $nonce ) {
329 $enc_type = $this->get_enc_type();
330
331 // If we received an empty nonce, we assume it was never properly encrypted to begin with.
332 if ( $nonce == '' ) {
333 return $cipher;
334 }
335
336 try {
337 // Determine if we should use native or polyfill functions.
338 if ( $enc_type == 'native' ) {
339 $key = sodium_crypto_generichash( AUTH_KEY );
340 $dec = sodium_crypto_secretbox_open( sodium_hex2bin( $cipher ), sodium_hex2bin( $nonce ), $key );
341 } else {
342 require_once ABSPATH . WPINC . '/sodium_compat/autoload.php';
343 $key = \Sodium\crypto_generichash( AUTH_KEY );
344 $dec = \Sodium\crypto_secretbox_open( sodium_hex2bin( $cipher ), sodium_hex2bin( $nonce ), $key );
345 }
346 } catch ( Exception $e ) {
347 return $cipher;
348 }
349
350 // In case decryption failed, return null.
351 if ( ! $dec ) {
352 return null;
353 }
354
355 return $dec;
356 }
357 }
358