/** * 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); }