PluginProbe
RSFirewall! / trunk
RSFirewall! vtrunk
rsfirewall / models / fix.php

fix.php in RSFirewall! trunk, at models/fix.php

482 lines 11.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package RSFirewall!
4 * @copyright (c) 2018 RSJoomla!
5 * @link https://www.rsjoomla.com
6 * @license GNU General Public License http://www.gnu.org/licenses/gpl-3.0.en.html
7 */
8
9 if ( ! defined( 'WPINC' ) ) {
10 die;
11 }
12
13 class RSFirewall_Model_Fix extends RSFirewall_Model {
14 const DS = DIRECTORY_SEPARATOR;
15
16 /**
17 * Changes usernames
18 *
19 * @param $args string username
20 *
21 * @since 1.0.0
22 * @throws Exception
23 *
24 * @return array
25 */
26 public function admin_username_fix( $args ) {
27 $errors = array();
28 $arguments = array(
29 'search' => $args['new_user'],
30 );
31
32 $common = array(
33 'admin',
34 'administrator',
35 'wp-admin'
36 );
37
38 $users = get_users( $arguments );
39
40 if ( ! empty( $users ) ) {
41 $error = esc_html__( 'An identical username already exists.', 'rsfirewall' );
42 foreach ( $users as $user ) {
43 if ( $args['new_user'] === $user->user_login ) {
44 $errors[] = $error;
45 }
46 }
47 }
48
49 if ( $args['old_user'] == $args['new_user'] ) {
50 $errors[] = esc_html__( 'Username should not be identical.', 'rsfirewall' );
51 }
52
53 if ( $args['new_user'] == '' ) {
54 $errors[] = esc_html__( 'Username can\'t be an empty string.', 'rsfirewall' );
55 }
56
57 if ( strlen( $args['new_user'] ) < 5 ) {
58 $errors[] = esc_html__( 'Your username is too short. We recommend at least 5 characters.', 'rsfirewall' );
59 }
60
61 if ( in_array( $args['new_user'], $common ) ) {
62 $errors[] = esc_html__( 'The username is too common', 'rsfirewall' );
63 }
64
65
66 // Do not add by mistake an unwanted admin username
67 $unwanted = RSFirewall_Core_Vulnerabilities::$unwanted_admins;
68 foreach ($unwanted as $u_admin) {
69 if (stripos($args['new_user'], $u_admin) !== false) {
70 $errors[] = esc_html__( 'The username is reported as dangerous! Please reconsider.', 'rsfirewall' );
71 break;
72 }
73 }
74
75
76 if ( count( $errors ) > 0 ) {
77 $return['weak_username'] = true;
78 $return['message'] = esc_html__( 'We recommend the following adjustments to the username:', 'rsfirewall' );
79 $return['details'] = implode( '<br />', $errors );
80
81 return $return;
82 }
83
84 $return = array(
85 'weak_username' => false,
86 'message' => esc_html__( 'The username was changed succesfully!', 'rsfirewall' )
87 );
88
89 global $wpdb;
90 $result = $wpdb->update(
91 $wpdb->prefix . 'users',
92 array( 'user_login' => $args['new_user'] ),
93 array( 'user_login' => $args['old_user'] ),
94 $format = NULL,
95 $where_format = NULL
96 );
97
98 if ( false === $result ) {
99 $return['message'] = esc_html__( 'Username could not be changed, please try again.', 'rsfirewall' );
100 }
101
102 return $return;
103
104 }
105
106 /**
107 * Deletes admin users
108 *
109 * @param $args int user_id
110 *
111 * @since 1.0.0
112 * @throws Exception
113 */
114 public function delete_admin_user($args) {
115 if (!isset($args['user_id']) || empty($args['user_id'])) {
116 throw new Exception(esc_html__( 'No User ID is specified.', 'rsfirewall' ));
117 }
118
119 // make sure the id is an int
120 $user_id = (int) $args['user_id'];
121
122 // get current logged in user - cannot delete own user
123 $current_user = wp_get_current_user();
124
125 if ($user_id == $current_user->ID) {
126 throw new Exception(esc_html__( 'Cannot delete own user.', 'rsfirewall' ));
127 }
128
129 // check if the user actually exist
130 $user = get_user_by('ID', $user_id);
131
132 if (!$user) {
133 throw new Exception(esc_html__( 'This user does not exist.', 'rsfirewall' ));
134 }
135
136 // if somehow the function is triggered with an ID that is not an administrator
137 if ($user->roles[0] != 'administrator') {
138 throw new Exception(esc_html__( 'This user is not an administrator.', 'rsfirewall' ));
139 }
140
141 // Delete the user if all above passes
142 if (!wp_delete_user($user->ID)) {
143 throw new Exception(esc_html__( 'Something went wrong.', 'rsfirewall' ));
144 }
145 }
146
147 /**
148 * @param $name
149 *
150 * @return string
151 */
152 public function getINI( $name ) {
153 return ini_get( $name );
154 }
155
156 /**
157 * @param $name
158 * @param string $against
159 *
160 * @return bool
161 */
162 public function compareINI( $name, $against = '1' ) {
163 return $this->getINI( $name ) == $against;
164 }
165
166 /**
167 * Helper function to build the PHP ini file
168 *
169 * @return string
170 */
171 public function build_php_ini() {
172 $isPHP54 = version_compare( phpversion(), '5.4.0', '>=' );
173 $isWin = substr( PHP_OS, 0, 3 ) == 'WIN';
174
175 $contents = array(
176 'allow_url_include=Off',
177 'disable_functions=system, shell_exec, passthru, exec, phpinfo, popen, proc_open'
178 );
179
180 if ( ! $isPHP54 ) {
181 $contents[] = 'register_globals=Off';
182 $contents[] = 'safe_mode=Off';
183 }
184
185 if ( $this->compareINI( 'open_basedir', '' ) ) {
186 $paths = array();
187 $delimiter = $isWin ? ';' : ':';
188
189 // add the path to the WP installation
190 if ( get_home_path() ) {
191 $paths[] = get_home_path();
192 }
193
194 // try to add the path for the server temporary folder
195 if ( $path = $this->getINI( 'upload_tmp_dir' ) ) {
196 $paths[] = $path;
197 }
198 if ( $temp_dir = sys_get_temp_dir() ) {
199 $paths[] = $temp_dir;
200 }
201 // try to add the path for the server session folder
202 if ( $path = $this->getINI( 'session.save_path' ) ) {
203 $paths[] = $path;
204 }
205
206 $paths = array_filter( $paths );
207
208 $contents[] = 'open_basedir=' . implode( $delimiter, array_unique( $paths ) );
209 } else {
210 $contents[] = 'open_basedir=' . $this->getINI( 'open_basedir' );
211 }
212
213 return implode( "\r\n", $contents );
214 }
215
216 /**
217 * Function to save the php ini
218 *
219 * @param $content
220 *
221 * @since 1.0.0
222 * @return bool
223 */
224 public function save_php_ini( $content ) {
225
226 $access_type = get_filesystem_method();
227 if ( $access_type === 'direct' ) {
228 /* you can safely run request_filesystem_credentials() without any issues and don't need to worry about passing in a URL */
229 $creds = request_filesystem_credentials( site_url() . '/wp-admin/', '', false, false, array() );
230
231 /* initialize the API */
232 if ( ! WP_Filesystem( $creds ) ) {
233 /* any problems and we exit */
234 return false;
235 }
236
237 global $wp_filesystem;
238 /* do our file manipulations below */
239 $wp_filesystem->put_contents(
240 get_home_path() . '/php.ini',
241 $content,
242 FS_CHMOD_FILE // predefined mode settings for WP files
243 );
244
245 return true;
246 }
247
248 return false;
249
250 }
251
252 /**
253 * Attempts to fix the php directives by creating a local php ini file
254 *
255 * @return array
256 * @since 1.0.0
257 */
258 public function php_configuration_fix() {
259 $result = array(
260 'success' => true,
261 'message' => esc_html__( 'RSFirewall! succesfully created the php.ini file', 'rsfirewall' )
262 );
263
264 $contents = $this->build_php_ini();
265 // using @ because file_put_contents outputs a warning when unsuccessful
266 if ( ! @$this->save_php_ini( $contents ) ) {
267 $error = error_get_last();
268 $result['result'] = false;
269 $result['message'] = $error['message'];
270 $result['details'] = $contents;
271 }
272
273 return $result;
274 }
275
276 /**
277 * Function that handles the ignoring of hashes/files
278 *
279 * @param $args
280 * @param $type
281 * @param $table
282 *
283 * @return array
284 * @throws Exception
285 *
286 * @since 1.0.0
287 */
288 public function ignore_stuff( $args, $type, $table ) {
289 global $wpdb, $wp_version;
290 $wpdb->show_errors();
291 $table = $wpdb->prefix . $table;
292 $result = array();
293 $added = 0;
294
295 switch ( $type ) {
296 case 'files':
297 foreach ( $args['files'] as $file ) {
298 $query = $wpdb->insert(
299 $table,
300 array(
301 'path' => RSFIREWALL_SITE . self::DS . stripslashes( $file ),
302 'type' => 'ignore_file',
303 )
304 );
305
306 if ( false === $query ) {
307 $result['result'] = false;
308 throw new Exception ( esc_html__( 'Could not add the files to the ignored table. Please try again.', 'rsfirewall' ) );
309 }
310
311 $result['result'] = true;
312 $added++;
313 }
314 break;
315 case 'hashes':
316 foreach ( $args['data'] as $file ) {
317 // set the proper flag
318 $flag = (isset($file['type']) && $file['type'] == 'missing') ? 'M' : '';
319
320 // check if the file is the 'protect' type.
321 $is_protect = false;
322 if (isset($file['version']) && $file['version'] == 'protect') {
323 $is_protect = true;
324 }
325
326 // full path to the file
327 $file_path = $is_protect ? $file['file'] : RSFIREWALL_SITE . '/' . $file['file'];
328
329 if (file_exists($file_path) && !is_readable($file_path)) {
330 $result['result'] = false;
331 throw new Exception ( sprintf(esc_html__( 'Could not open %s for reading. Please make sure you have read permissions.', 'rsfirewall' ), $file_path) );
332 }
333
334 if ($flag == 'M') {
335 // no hash for a missing file
336 $file_hash = '';
337 } else {
338 // this check is done only when the file exists and has the wrong hash
339 if (!is_file($file_path)) {
340 $result['result'] = false;
341 throw new Exception ( sprintf(esc_html__( '%s: file not found.', 'rsfirewall' ), $file_path) );
342 }
343 // calculate the hash
344 $file_hash = md5_file($file_path);
345 }
346
347 if ($is_protect) {
348 // Rebuild the protected files hashes
349 $id = $wpdb->get_var("SELECT `id` FROM $table WHERE `file`='".$file['file']."' AND `type`='protect' LIMIT 1");
350 } else {
351 // Rebuild or add core files hashes
352 $id = $wpdb->get_var("SELECT `id` FROM $table WHERE `file`='" . $file['file'] . "' AND (`type`='ignore' OR `type`='" . $wp_version . "') LIMIT 1");
353 }
354
355 // The update is available for all file types
356 if (!is_null($id)) {
357 $query = $wpdb->update(
358 $table,
359 array(
360 'hash' => $file_hash,
361 'flag' => $flag,
362 'date' => current_time('mysql'),
363 ),
364 array(
365 'id' => $id,
366 )
367 );
368 } else if(!$is_protect) {
369 $query = $wpdb->insert(
370 $table,
371 array(
372 'file' => $file['file'],
373 'hash' => $file_hash,
374 'type' => 'ignore',
375 'flag' => $flag,
376 'date' => current_time('mysql'),
377 )
378 );
379 }
380
381 if (!$is_protect && false === $query ) {
382 $result['result'] = false;
383 throw new Exception ( esc_html__( 'Could not add the files to the hashes table. Please try again.', 'rsfirewall' ) );
384 }
385
386 $result['result'] = true;
387 $added++;
388 }
389 break;
390 }
391
392 if ($added)
393 {
394 if ($type == 'files')
395 {
396 $result['message'] = sprintf( esc_html__( '%d file(s) was added to the ignored table.', 'rsfirewall' ), $added );
397 }
398 else if ($type == 'hashes')
399 {
400 $result['message'] = sprintf( esc_html__( '%d file(s) was added to the hashes table.', 'rsfirewall' ), $added );
401 }
402 }
403
404 return $result;
405 }
406
407 /**
408 * Function that sets the file/folder permissions
409 *
410 * @param $paths
411 * @param $perms
412 *
413 * @return array
414 *
415 * @since 1.0.0
416 */
417 public function set_permissions( $paths, $perms ) {
418 $success = array();
419 if ( ! is_array( $paths ) ) {
420 $paths = (array) $paths;
421 }
422
423 foreach ( $paths as $path ) {
424 $success[] = (int) @chmod( RSFIREWALL_SITE . self::DS . $path, octdec( $perms ) );
425 }
426
427 // the result will be an array with the same length as the input array
428 // 0 for failure, 1 for success
429 return $success;
430 }
431
432 /**
433 * Function to delete the post revisions from the database
434 *
435 * @return array
436 *
437 * @since 1.0.0
438 */
439 public function fix_delete_revisions() {
440 global $wpdb;
441 $table = $wpdb->prefix . 'posts';
442 $result = $wpdb->delete( $table, array( 'post_type' => 'revision' ) );
443
444 if ( $result ) {
445 return array(
446 'result' => true,
447 'message' => sprintf( esc_html__( 'We managed to delete %d revisions from the database', 'rsfirewall' ), $result ),
448 );
449 }
450
451 return array(
452 'result' => false,
453 'message' => esc_html__( 'No posts have been deleted. Please try again.', 'rsfirewall' )
454 );
455 }
456
457 /**
458 * Remove the ip from the blocklist
459 *
460 * @param args
461 *
462 * @since 1.0.0
463 *
464 * @return array
465 */
466 public function remove_ip( $args ) {
467 $result = wp_delete_post( $args, true );
468
469 if ( ! $result ) {
470 return array(
471 'result' => false,
472 'message' => esc_html__( 'The entry wasn\'t be deleted from the database. Please try again.', 'rsfirewall' )
473 );
474 }
475
476 return array(
477 'result' => true,
478 'message' => esc_html__( 'This entry was deleted from the database.', 'rsfirewall' ),
479 );
480
481 }
482 }