public static function get_instance() { if (!self::$instance) { self::$instance = new self(); } return self::$instance; }
/** * Loads core functionality across both admin and frontend. * * Creates all plugin globals, registers activation and related hooks, * loads the text domain and loads all plugin modules * * @since 4.0 * * @access private * * @param string $plugin_file The main plugin file * @param string $plugin_name The plugin name * */ public function init($plugin_file, $plugin_name) { global $itsec_globals, $itsec_logger, $itsec_lockout; $this->plugin_build = 4044; // used to trigger updates $this->plugin_file = $plugin_file; $this->plugin_dir = dirname($plugin_file) . '/'; $this->current_time = current_time('timestamp'); $this->current_time_gmt = current_time('timestamp', true); $this->notices_loaded = false; $this->doing_data_upgrade = false; $this->interactive = false; // Used to distinguish between a user modifying settings and the API modifying // settings (such as from Sync requests). $itsec_globals = array('plugin_name' => sanitize_text_field($plugin_name), 'plugin_dir' => $this->plugin_dir, 'current_time' => $this->current_time, 'current_time_gmt' => $this->current_time_gmt); require $this->plugin_dir . 'core/class-itsec-modules.php'; add_action('itsec-register-modules', array($this, 'register_modules')); ITSEC_Modules::init_modules(); require $this->plugin_dir . 'core/class-itsec-lib.php'; require $this->plugin_dir . 'core/class-itsec-logger.php'; require $this->plugin_dir . 'core/class-itsec-lockout.php'; require $this->plugin_dir . 'core/class-itsec-files.php'; require $this->plugin_dir . 'core/class-itsec-notify.php'; require $this->plugin_dir . 'core/class-itsec-response.php'; require $this->plugin_dir . 'core/lib/class-itsec-lib-user-activity.php'; $this->itsec_files = ITSEC_Files::get_instance(); $this->itsec_notify = new ITSEC_Notify(); $itsec_logger = new ITSEC_Logger(); $itsec_lockout = new ITSEC_Lockout($this); //Determine if we need to run upgrade scripts $plugin_data = get_site_option('itsec_data'); if (false === $plugin_data) { $plugin_data = $this->save_plugin_data(); } $itsec_globals['data'] = $plugin_data; if (isset($plugin_data['build']) && $plugin_data['build'] !== $this->plugin_build) { // We need to upgrade the data. Delay init of the rest of the plugin until the upgrade is complete. $this->doing_data_upgrade = true; // Run the actions early so that the rest of the code can still use the plugins_loaded hook. add_action('plugins_loaded', array($this, 'execute_upgrade'), -100); add_action('plugins_loaded', array($this, 'continue_init'), -90); } else { $this->continue_init(); } }
/** * Inserts an IP address into the htaccess ban list. * * @since 4.0 * * @param $ip * @param null $ban_list * @param null $white_list * * @return void */ public static function insert_ip($ip, $ban_list = null, $white_list = null) { $settings = get_site_option('itsec_ban_users'); $host = sanitize_text_field($ip); if ($ban_list === null) { $ban_list = isset($settings['host_list']) ? $settings['host_list'] : array(); } if ($white_list === null) { $global_settings = get_site_option('itsec_global'); $white_list = isset($global_settings['lockout_white_list']) ? $global_settings['lockout_white_list'] : array(); } if (!in_array($host, $ban_list) && !ITSEC_Ban_Users::is_ip_whitelisted($host, $white_list)) { $ban_list[] = $host; $settings['host_list'] = $ban_list; ITSEC_Files::quick_ban($host); update_site_option('itsec_ban_users', $settings); add_site_option('itsec_rewrites_changed', true); } }
/** * Locks out given user or host * * @since 4.0 * * @param string $type The type of lockout (for user reference) * @param string $reason Reason for lockout, for notifications * @param string $host Host to lock out * @param int $user user id to lockout * @param string $username username to lockout * * @return void */ private function lockout($type, $reason, $host = null, $user = null, $username = null) { global $wpdb, $itsec_logger, $itsec_globals; $itsec_files = ITSEC_Core::get_itsec_files(); $host_expiration = null; $user_expiration = null; $username = sanitize_text_field(trim($username)); if ($itsec_files->get_file_lock('lockout_' . $host . $user . $username)) { //Do we have a good host to lock out or not if (!is_null($host) && ITSEC_Lib::is_ip_whitelisted(sanitize_text_field($host)) === false && ITSEC_Lib_IP_Tools::validate($host)) { $good_host = sanitize_text_field($host); } else { $good_host = false; } //Do we have a valid user to lockout or not if ($user !== null && ITSEC_Lib::user_id_exists(intval($user)) === true) { $good_user = intval($user); } else { $good_user = false; } //Do we have a valid username to lockout or not if ($username !== null && $username != '') { $good_username = $username; } else { $good_username = false; } $blacklist_host = false; //assume we're not permanently blcking the host //Sanitize the data for later $type = sanitize_text_field($type); $reason = sanitize_text_field($reason); //handle a permanent host ban (if needed) if (ITSEC_Modules::get_setting('global', 'blacklist') && $good_host !== false) { //permanent blacklist $blacklist_period = ITSEC_Modules::get_setting('global', 'blacklist_period', 7); $blacklist_seconds = $blacklist_period * DAY_IN_SECONDS; $host_count = 1 + $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_lockouts` WHERE `lockout_expire_gmt` > '%s' AND `lockout_host`='%s';", date('Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - $blacklist_seconds), $host)); if ($host_count >= ITSEC_Modules::get_setting('global', 'blacklist_count') && ITSEC_Files::can_write_to_files()) { $host_expiration = false; $this->blacklist_ip(sanitize_text_field($host)); $blacklist_host = true; //flag it so we don't do a temp ban as well } } //We have temp bans to perform if ($good_host !== false || $good_user !== false || $good_username || $good_username !== false) { if (ITSEC_Lib::is_ip_whitelisted(sanitize_text_field($host))) { $whitelisted = true; $expiration = date('Y-m-d H:i:s', 1); $expiration_gmt = date('Y-m-d H:i:s', 1); } else { $whitelisted = false; $exp_seconds = ITSEC_Modules::get_setting('global', 'lockout_period') * MINUTE_IN_SECONDS; $expiration = date('Y-m-d H:i:s', $itsec_globals['current_time'] + $exp_seconds); $expiration_gmt = date('Y-m-d H:i:s', $itsec_globals['current_time_gmt'] + $exp_seconds); } if ($good_host !== false && $blacklist_host === false) { //temp lockout host $host_expiration = $expiration; $wpdb->insert($wpdb->base_prefix . 'itsec_lockouts', array('lockout_type' => $type, 'lockout_start' => date('Y-m-d H:i:s', $itsec_globals['current_time']), 'lockout_start_gmt' => date('Y-m-d H:i:s', $itsec_globals['current_time_gmt']), 'lockout_expire' => $expiration, 'lockout_expire_gmt' => $expiration_gmt, 'lockout_host' => sanitize_text_field($host))); $itsec_logger->log_event('lockout', 10, array('expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type), sanitize_text_field($host)); } if ($good_user !== false) { //blacklist host and temp lockout user $user_expiration = $expiration; $wpdb->insert($wpdb->base_prefix . 'itsec_lockouts', array('lockout_type' => $type, 'lockout_start' => date('Y-m-d H:i:s', $itsec_globals['current_time']), 'lockout_start_gmt' => date('Y-m-d H:i:s', $itsec_globals['current_time_gmt']), 'lockout_expire' => $expiration, 'lockout_expire_gmt' => $expiration_gmt, 'lockout_host' => '', 'lockout_user' => intval($user))); if ($whitelisted === false) { $itsec_logger->log_event('lockout', 10, array('expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type), '', '', intval($user)); } else { $itsec_logger->log_event('lockout', 10, array(__('White Listed', 'better-wp-security'), 'type' => $type), '', '', intval($user)); } } if ($good_username !== false) { //blacklist host and temp lockout username $user_expiration = $expiration; $wpdb->insert($wpdb->base_prefix . 'itsec_lockouts', array('lockout_type' => $type, 'lockout_start' => date('Y-m-d H:i:s', $itsec_globals['current_time']), 'lockout_start_gmt' => date('Y-m-d H:i:s', $itsec_globals['current_time_gmt']), 'lockout_expire' => $expiration, 'lockout_expire_gmt' => $expiration_gmt, 'lockout_host' => '', 'lockout_username' => $username)); if ($whitelisted === false) { $itsec_logger->log_event('lockout', 10, array('expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type), '', '', $username); } else { $itsec_logger->log_event('lockout', 10, array(__('White Listed', 'better-wp-security'), 'type' => $type), '', '', $username); } } if ($whitelisted === false) { if (ITSEC_Modules::get_setting('global', 'email_notifications')) { //send email notifications $this->send_lockout_email($good_host, $good_user, $good_username, $host_expiration, $user_expiration, $reason); } if ($good_host !== false) { $itsec_files->release_file_lock('lockout_' . $host . $user . $username); $this->execute_lock(); } else { $itsec_files->release_file_lock('lockout_' . $host . $user . $username); $this->execute_lock(true); } } } $itsec_files->release_file_lock('lockout_' . $host . $user . $username); } }
/** * Update modifications in the supplied configuration file. * * If a blank $contents argument is supplied, all modifications will be removed. * * @since 1.15.0 * @access protected * * @param string $file Config file to update. * @param string $type The type of config file. Valid options are apache, nginx, and * wp-config. * @param string $modification The contents to add or update the file with. If an empty string is * supplied, all iThemes Security modifications will be removed. * @param bool $clear_existing_modifications Optional. Whether or not existing modifications should be removed * first. Defaults to true. * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise. */ protected static function update($file, $type, $modification, $clear_existing_modifications = true) { // Check to make sure that the settings give permission to write files. if (!ITSEC_Files::can_write_to_files()) { $display_file = str_replace('\\', '/', $file); $abspath = str_replace('\\', '/', ABSPATH); $display_file = preg_replace('/^' . preg_quote($abspath, '/') . '/', '', $display_file); $display_file = ltrim($display_file, '/'); return new WP_Error('itsec-config-file-update-writes-files-disabled', sprintf(__('The "Write to Files" setting is disabled. Manual configuration for the <code>%s</code> file can be found on the Security > Settings page in the Advanced section.', 'better-wp-security'), $display_file)); } if ($clear_existing_modifications) { $contents = self::get_file_contents_without_modification($file, $type); } else { $contents = self::get_file_contents($file); } if (is_wp_error($contents)) { return $contents; } $modification = ltrim($modification, "\v\r\n"); $modification = rtrim($modification, " \t\v\r\n"); if (empty($modification)) { // If there isn't a new modification, write the content without any modification and return the result. if (empty($contents)) { $contents = PHP_EOL; } return ITSEC_Lib_File::write($file, $contents); } $placeholder = self::get_placeholder(); // Ensure that the generated placeholder can be uniquely identified in the contents. while (false !== strpos($contents, $placeholder)) { $placeholder = self::get_placeholder(); } if ('wp-config' === $type) { // Put the placeholder at the beginning of the file, after the <?php tag. $contents = preg_replace('/^(.*?<\\?(?:php)?)\\s*(?:\\r\\r\\n|\\r\\n|\\r|\\n)/', "\${1}{$placeholder}", $contents, 1); if (false === strpos($contents, $placeholder)) { $contents = preg_replace('/^(.*?<\\?(?:php)?)\\s*(.+(?:\\r\\r\\n|\\r\\n|\\r|\\n))/', "\${1}{$placeholder}\$2", $contents, 1); } if (false === strpos($contents, $placeholder)) { $contents = "<?php{$placeholder}?" . ">{$contents}"; } } else { // Apache and nginx server config files. $contents = "{$placeholder}{$contents}"; } // Pad away from existing sections when adding iThemes Security modifications. $line_ending = self::get_line_ending($contents); while (!preg_match("/(?:^|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)){$placeholder}/", $contents)) { $contents = preg_replace("/{$placeholder}/", "{$line_ending}{$placeholder}", $contents); } while (!preg_match("/{$placeholder}(?:\$|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n))/", $contents)) { $contents = preg_replace("/{$placeholder}/", "{$placeholder}{$line_ending}", $contents); } // Ensure that the file ends in a newline if the placeholder is at the end. $contents = preg_replace("/{$placeholder}\$/", "{$placeholder}{$line_ending}", $contents); if (!empty($modification)) { // Normalize line endings of the modification to match the file's line endings. $modification = ITSEC_Lib_Utility::normalize_line_endings($modification, $line_ending); // Exchange the placeholder with the modification. $contents = preg_replace("/{$placeholder}/", $modification, $contents); } // Write the new contents to the file and return the results. return ITSEC_Lib_File::write($file, $contents); }
public static function maybe_regenerate_server_config() { $self = self::get_instance(); if ($self->regenerate_server_config) { ITSEC_Files::regenerate_server_config(); $self->regenerate_server_config = false; } }