Diff
11 years ago
.htaccess
14 years ago
Diff.php
14 years ago
GeoIP.dat
9 years ago
GeoIPv6.dat
9 years ago
IPTraf.php
9 years ago
compat.php
10 years ago
conntest.php
11 years ago
cronview.php
10 years ago
dashboard.php
9 years ago
dbview.php
11 years ago
diffResult.php
11 years ago
email_genericAlert.php
9 years ago
email_newIssues.php
9 years ago
email_passwdChanged.php
10 years ago
email_pleaseChangePasswd.php
10 years ago
email_unlockRequest.php
11 years ago
menuHeader.php
12 years ago
menu_activity.php
9 years ago
menu_blockedIPs.php
9 years ago
menu_countryBlocking.php
9 years ago
menu_diagnostic.php
9 years ago
menu_options.php
9 years ago
menu_passwd.php
9 years ago
menu_rangeBlocking.php
9 years ago
menu_scan.php
9 years ago
menu_scanSchedule.php
9 years ago
menu_sitePerf.php
11 years ago
menu_sitePerfStats.php
9 years ago
menu_twoFactor.php
9 years ago
menu_waf.php
9 years ago
menu_whois.php
11 years ago
pageTitle.php
11 years ago
schedWeekEntry.php
12 years ago
sysinfo.php
10 years ago
unknownFiles.php
11 years ago
viewFullActivityLog.php
10 years ago
wf503.php
10 years ago
wfAPI.php
9 years ago
wfAction.php
14 years ago
wfActivityReport.php
10 years ago
wfArray.php
13 years ago
wfBrowscap.php
12 years ago
wfBrowscapCache.php
10 years ago
wfBulkCountries.php
13 years ago
wfCache.php
9 years ago
wfConfig.php
9 years ago
wfCountryMap.php
13 years ago
wfCrawl.php
10 years ago
wfCrypt.php
11 years ago
wfDB.php
10 years ago
wfDiagnostic.php
10 years ago
wfDict.php
14 years ago
wfDirectoryIterator.php
11 years ago
wfGeoIP.php
9 years ago
wfHelperBin.php
11 years ago
wfHelperString.php
11 years ago
wfIPWhitelist.php
9 years ago
wfIssues.php
9 years ago
wfLockedOut.php
13 years ago
wfLog.php
9 years ago
wfRate.php
10 years ago
wfScan.php
9 years ago
wfScanEngine.php
9 years ago
wfSchema.php
10 years ago
wfUnlockMsg.php
10 years ago
wfUpdateCheck.php
9 years ago
wfUtils.php
9 years ago
wfView.php
10 years ago
wfViewResult.php
11 years ago
wordfenceClass.php
9 years ago
wordfenceConstants.php
9 years ago
wordfenceHash.php
9 years ago
wordfenceScanner.php
9 years ago
wordfenceURLHoover.php
9 years ago
wfConfig.php
725 lines
| 1 | <?php |
| 2 | class wfConfig { |
| 3 | const AUTOLOAD = 'yes'; |
| 4 | const DONT_AUTOLOAD = 'no'; |
| 5 | |
| 6 | public static $diskCache = array(); |
| 7 | private static $diskCacheDisabled = false; //enables if we detect a write fail so we don't keep calling stat() |
| 8 | private static $cacheDisableCheckDone = false; |
| 9 | private static $table = false; |
| 10 | private static $tableExists = true; |
| 11 | private static $cache = array(); |
| 12 | private static $DB = false; |
| 13 | private static $tmpFileHeader = "<?php\n/* Wordfence temporary file security header */\necho \"Nothing to see here!\\n\"; exit(0);\n?>"; |
| 14 | private static $tmpDirCache = false; |
| 15 | public static $defaultConfig = array( |
| 16 | "checkboxes" => array( |
| 17 | "alertOn_critical" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 18 | "alertOn_update" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 19 | "alertOn_warnings" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 20 | "alertOn_throttle" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 21 | "alertOn_block" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 22 | "alertOn_loginLockout" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 23 | "alertOn_lostPasswdForm" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 24 | "alertOn_adminLogin" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 25 | "alertOn_nonAdminLogin" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 26 | "liveTrafficEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 27 | "scansEnabled_checkReadableConfig" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 28 | "advancedCommentScanning" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 29 | "checkSpamIP" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 30 | "spamvertizeCheck" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 31 | "liveTraf_ignorePublishers" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 32 | //"perfLoggingEnabled" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 33 | "scheduledScansEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 34 | "scansEnabled_public" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 35 | "scansEnabled_heartbleed" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 36 | "scansEnabled_core" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 37 | "scansEnabled_themes" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 38 | "scansEnabled_plugins" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 39 | "scansEnabled_coreUnknown" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 40 | "scansEnabled_malware" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 41 | "scansEnabled_fileContents" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 42 | "scansEnabled_posts" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 43 | "scansEnabled_comments" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 44 | "scansEnabled_passwds" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 45 | "scansEnabled_diskSpace" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 46 | "scansEnabled_options" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 47 | "scansEnabled_wpscan_fullPathDisclosure" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 48 | "scansEnabled_wpscan_directoryListingEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 49 | "scansEnabled_dns" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 50 | "scansEnabled_scanImages" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 51 | "scansEnabled_highSense" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 52 | "scansEnabled_oldVersions" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 53 | "scansEnabled_suspiciousAdminUsers" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 54 | "firewallEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 55 | "blockFakeBots" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 56 | "autoBlockScanners" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 57 | "loginSecurityEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 58 | "loginSec_lockInvalidUsers" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 59 | "loginSec_maskLoginErrors" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 60 | "loginSec_blockAdminReg" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 61 | "loginSec_disableAuthorScan" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 62 | "loginSec_disableOEmbedAuthor" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 63 | "other_hideWPVersion" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 64 | "other_noAnonMemberComments" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 65 | "other_blockBadPOST" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 66 | "other_scanComments" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 67 | "other_pwStrengthOnUpdate" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 68 | "other_WFNet" => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 69 | "other_scanOutside" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 70 | "deleteTablesOnDeact" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 71 | "autoUpdate" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 72 | "disableCookies" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 73 | "startScansRemotely" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 74 | "disableConfigCaching" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 75 | "addCacheComment" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 76 | "disableCodeExecutionUploads" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 77 | "allowHTTPSCaching" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 78 | "debugOn" => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 79 | 'email_summary_enabled' => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 80 | 'email_summary_dashboard_widget_enabled' => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 81 | 'ssl_verify' => array('value' => true, 'autoload' => self::AUTOLOAD), |
| 82 | 'ajaxWatcherDisabled_front' => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 83 | 'ajaxWatcherDisabled_admin' => array('value' => false, 'autoload' => self::AUTOLOAD), |
| 84 | ), |
| 85 | "otherParams" => array( |
| 86 | "scan_include_extra" => "", |
| 87 | // 'securityLevel' => '2', |
| 88 | "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '', |
| 89 | 'liveTraf_maxRows' => 2000, |
| 90 | "neverBlockBG" => "neverBlockVerified", |
| 91 | "loginSec_countFailMins" => "240", |
| 92 | "loginSec_lockoutMins" => "240", |
| 93 | 'loginSec_strongPasswds' => 'pubs', |
| 94 | 'loginSec_maxFailures' => "20", |
| 95 | 'loginSec_maxForgotPasswd' => "20", |
| 96 | 'maxGlobalRequests' => "DISABLED", |
| 97 | 'maxGlobalRequests_action' => "throttle", |
| 98 | 'maxRequestsCrawlers' => "DISABLED", |
| 99 | 'maxRequestsCrawlers_action' => "throttle", |
| 100 | 'maxRequestsHumans' => "DISABLED", |
| 101 | 'maxRequestsHumans_action' => "throttle", |
| 102 | 'max404Crawlers' => "DISABLED", |
| 103 | 'max404Crawlers_action' => "throttle", |
| 104 | 'max404Humans' => "DISABLED", |
| 105 | 'max404Humans_action' => "throttle", |
| 106 | 'maxScanHits' => "DISABLED", |
| 107 | 'maxScanHits_action' => "throttle", |
| 108 | 'blockedTime' => "300", |
| 109 | 'email_summary_interval' => 'biweekly', |
| 110 | 'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wfcache,wp-content/plugins/wordfence/tmp', |
| 111 | 'allowed404s' => "/favicon.ico\n/apple-touch-icon*.png\n/*@2x.png", |
| 112 | ) |
| 113 | ); |
| 114 | public static $serializedOptions = array('lastAdminLogin', 'scanSched', 'emailedIssuesList', 'wf_summaryItems', 'adminUserList', 'twoFactorUsers', 'alertFreqTrack', 'wfStatusStartMsgs'); |
| 115 | public static function setDefaults() { |
| 116 | foreach (self::$defaultConfig['checkboxes'] as $key => $config) { |
| 117 | $val = $config['value']; |
| 118 | $autoload = $config['autoload']; |
| 119 | if (self::get($key) === false) { |
| 120 | self::set($key, $val ? '1' : '0', $autoload); |
| 121 | } |
| 122 | } |
| 123 | foreach (self::$defaultConfig['otherParams'] as $key => $val) { |
| 124 | if (self::get($key) === false) { |
| 125 | self::set($key, $val); |
| 126 | } |
| 127 | } |
| 128 | self::set('encKey', substr(wfUtils::bigRandomHex(), 0, 16)); |
| 129 | if (self::get('maxMem', false) === false) { |
| 130 | self::set('maxMem', '256'); |
| 131 | } |
| 132 | if (self::get('other_scanOutside', false) === false) { |
| 133 | self::set('other_scanOutside', 0); |
| 134 | } |
| 135 | |
| 136 | if (self::get('email_summary_enabled')) { |
| 137 | wfActivityReport::scheduleCronJob(); |
| 138 | } else { |
| 139 | wfActivityReport::disableCronJob(); |
| 140 | } |
| 141 | } |
| 142 | public static function loadAllOptions() { |
| 143 | global $wpdb; |
| 144 | |
| 145 | $options = wp_cache_get('alloptions', 'wordfence'); |
| 146 | if (!$options) { |
| 147 | $table = self::table(); |
| 148 | self::updateTableExists(); |
| 149 | $suppress = $wpdb->suppress_errors(); |
| 150 | if (!($rawOptions = $wpdb->get_results("SELECT name, val FROM {$table} WHERE autoload = 'yes'"))) { |
| 151 | $rawOptions = $wpdb->get_results("SELECT name, val FROM {$table}"); |
| 152 | } |
| 153 | $wpdb->suppress_errors($suppress); |
| 154 | $options = array(); |
| 155 | foreach ((array) $rawOptions as $o) { |
| 156 | if (in_array($o->name, self::$serializedOptions)) { |
| 157 | $val = maybe_unserialize($o->val); |
| 158 | if ($val) { |
| 159 | $options[$o->name] = $val; |
| 160 | } |
| 161 | } |
| 162 | else { |
| 163 | $options[$o->name] = $o->val; |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | wp_cache_add_non_persistent_groups('wordfence'); |
| 168 | wp_cache_add('alloptions', $options, 'wordfence'); |
| 169 | } |
| 170 | |
| 171 | return $options; |
| 172 | } |
| 173 | public static function updateTableExists() { |
| 174 | $table = self::table(); |
| 175 | self::$tableExists = (strtolower(self::getDB()->querySingle("SHOW TABLES LIKE '%s'", $table)) == strtolower($table)); |
| 176 | } |
| 177 | private static function updateCachedOption($name, $val) { |
| 178 | $options = self::loadAllOptions(); |
| 179 | $options[$name] = $val; |
| 180 | wp_cache_set('alloptions', $options, 'wordfence'); |
| 181 | } |
| 182 | private static function removeCachedOption($name) { |
| 183 | $options = self::loadAllOptions(); |
| 184 | if (isset($options[$name])) { |
| 185 | unset($options[$name]); |
| 186 | wp_cache_set('alloptions', $options, 'wordfence'); |
| 187 | } |
| 188 | } |
| 189 | private static function getCachedOption($name) { |
| 190 | $options = self::loadAllOptions(); |
| 191 | if (isset($options[$name])) { |
| 192 | return $options[$name]; |
| 193 | } |
| 194 | |
| 195 | $table = self::table(); |
| 196 | $val = self::getDB()->querySingle("SELECT val FROM {$table} WHERE name='%s'", $name); |
| 197 | if ($val !== null) { |
| 198 | $options[$name] = $val; |
| 199 | wp_cache_set('alloptions', $options, 'wordfence'); |
| 200 | } |
| 201 | return $val; |
| 202 | } |
| 203 | private static function hasCachedOption($name) { |
| 204 | $options = self::loadAllOptions(); |
| 205 | return isset($options[$name]); |
| 206 | } |
| 207 | public static function getExportableOptionsKeys(){ |
| 208 | $ret = array(); |
| 209 | foreach(self::$defaultConfig['checkboxes'] as $key => $val){ |
| 210 | $ret[] = $key; |
| 211 | } |
| 212 | foreach(self::$defaultConfig['otherParams'] as $key => $val){ |
| 213 | if($key != 'apiKey'){ |
| 214 | $ret[] = $key; |
| 215 | } |
| 216 | } |
| 217 | foreach(array('cbl_action', 'cbl_countries', 'cbl_redirURL', 'cbl_loggedInBlocked', 'cbl_loginFormBlocked', 'cbl_restOfSiteBlocked', 'cbl_bypassRedirURL', 'cbl_bypassRedirDest', 'cbl_bypassViewURL') as $key){ |
| 218 | $ret[] = $key; |
| 219 | } |
| 220 | return $ret; |
| 221 | } |
| 222 | public static function parseOptions(){ |
| 223 | $ret = array(); |
| 224 | foreach(self::$defaultConfig['checkboxes'] as $key => $val){ //value is not used. We just need the keys for validation |
| 225 | $ret[$key] = isset($_POST[$key]) ? '1' : '0'; |
| 226 | } |
| 227 | foreach(self::$defaultConfig['otherParams'] as $key => $val){ |
| 228 | if(isset($_POST[$key])){ |
| 229 | $ret[$key] = stripslashes($_POST[$key]); |
| 230 | } else { |
| 231 | error_log("Missing options param \"$key\" when parsing parameters."); |
| 232 | } |
| 233 | } |
| 234 | /* for debugging only: |
| 235 | foreach($_POST as $key => $val){ |
| 236 | if($key != 'action' && $key != 'nonce' && (! array_key_exists($key, self::$checkboxes)) && (! array_key_exists($key, self::$otherParams)) ){ |
| 237 | error_log("Unrecognized option: $key"); |
| 238 | } |
| 239 | } |
| 240 | */ |
| 241 | return $ret; |
| 242 | } |
| 243 | public static function setArray($arr){ |
| 244 | foreach($arr as $key => $val){ |
| 245 | self::set($key, $val); |
| 246 | } |
| 247 | } |
| 248 | public static function getHTML($key){ |
| 249 | return esc_html(self::get($key)); |
| 250 | } |
| 251 | public static function inc($key){ |
| 252 | $val = self::get($key, false); |
| 253 | if(! $val){ |
| 254 | $val = 0; |
| 255 | } |
| 256 | self::set($key, $val + 1); |
| 257 | } |
| 258 | public static function set($key, $val, $autoload = self::AUTOLOAD) { |
| 259 | global $wpdb; |
| 260 | |
| 261 | if (is_array($val)) { |
| 262 | $msg = "wfConfig::set() got an array as second param with key: $key and value: " . var_export($val, true); |
| 263 | wordfence::status(1, 'error', $msg); |
| 264 | return; |
| 265 | } |
| 266 | |
| 267 | if (($key == 'apiKey' || $key == 'isPaid') && wfWAF::getInstance() && !WFWAF_SUBDIRECTORY_INSTALL) { |
| 268 | try { |
| 269 | wfWAF::getInstance()->getStorageEngine()->setConfig($key, $val); |
| 270 | } catch (wfWAFStorageFileException $e) { |
| 271 | error_log($e->getMessage()); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | if (!self::$tableExists) { |
| 276 | return; |
| 277 | } |
| 278 | |
| 279 | $table = self::table(); |
| 280 | if ($wpdb->query($wpdb->prepare("INSERT INTO {$table} (name, val, autoload) values (%s, %s, %s) ON DUPLICATE KEY UPDATE val = %s, autoload = %s", $key, $val, $autoload, $val, $autoload)) !== false && $autoload != self::DONT_AUTOLOAD) { |
| 281 | self::updateCachedOption($key, $val); |
| 282 | } |
| 283 | } |
| 284 | public static function get($key, $default = false) { |
| 285 | global $wpdb; |
| 286 | |
| 287 | if (self::hasCachedOption($key)) { |
| 288 | return self::getCachedOption($key); |
| 289 | } |
| 290 | |
| 291 | if (!self::$tableExists) { |
| 292 | return $default; |
| 293 | } |
| 294 | |
| 295 | $table = self::table(); |
| 296 | if (!($option = $wpdb->get_row($wpdb->prepare("SELECT name, val, autoload FROM {$table} WHERE name = %s", $key)))) { |
| 297 | return $default; |
| 298 | } |
| 299 | |
| 300 | if ($option->autoload != self::DONT_AUTOLOAD) { |
| 301 | self::updateCachedOption($key, $option->val); |
| 302 | } |
| 303 | return $option->val; |
| 304 | } |
| 305 | |
| 306 | private static function canCompressValue() { |
| 307 | if (!function_exists('gzencode') || !function_exists('gzdecode')) { |
| 308 | return false; |
| 309 | } |
| 310 | $disabled = explode(',', ini_get('disable_functions')); |
| 311 | if (in_array('gzencode', $disabled) || in_array('gzdecode', $disabled)) { |
| 312 | return false; |
| 313 | } |
| 314 | return true; |
| 315 | } |
| 316 | |
| 317 | private static function isCompressedValue($data) { |
| 318 | //Based on http://www.ietf.org/rfc/rfc1952.txt |
| 319 | if (strlen($data) < 2) { |
| 320 | return false; |
| 321 | } |
| 322 | |
| 323 | $magicBytes = substr($data, 0, 2); |
| 324 | if ($magicBytes !== (chr(0x1f) . chr(0x8b))) { |
| 325 | return false; |
| 326 | } |
| 327 | |
| 328 | //Small chance of false positives here -- can check the header CRC if it turns out it's needed |
| 329 | return true; |
| 330 | } |
| 331 | |
| 332 | private static function ser_chunked_key($key) { |
| 333 | return 'wordfence_chunked_' . $key . '_'; |
| 334 | } |
| 335 | |
| 336 | public static function get_ser($key, $default, $cache = true) { |
| 337 | if (self::hasCachedOption($key)) { |
| 338 | return self::getCachedOption($key); |
| 339 | } |
| 340 | |
| 341 | if (!self::$tableExists) { |
| 342 | return $default; |
| 343 | } |
| 344 | |
| 345 | //Check for a chunked value first |
| 346 | $chunkedValueKey = self::ser_chunked_key($key); |
| 347 | $header = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $chunkedValueKey . 'header'); |
| 348 | if ($header) { |
| 349 | $header = unserialize($header); |
| 350 | $count = $header['count']; |
| 351 | $path = tempnam(sys_get_temp_dir(), $key); //Writing to a file like this saves some of PHP's in-memory copying when just appending each chunk to a string |
| 352 | $fh = fopen($path, 'r+'); |
| 353 | $length = 0; |
| 354 | for ($i = 0; $i < $count; $i++) { |
| 355 | $chunk = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $chunkedValueKey . $i); |
| 356 | self::getDB()->flush(); //clear cache |
| 357 | if (!$chunk) { |
| 358 | wordfence::status(2, 'error', "Error reassembling value for {$key}"); |
| 359 | return $default; |
| 360 | } |
| 361 | fwrite($fh, $chunk); |
| 362 | $length += strlen($chunk); |
| 363 | unset($chunk); |
| 364 | } |
| 365 | |
| 366 | fseek($fh, 0); |
| 367 | $serialized = fread($fh, $length); |
| 368 | fclose($fh); |
| 369 | unlink($path); |
| 370 | |
| 371 | if (self::canCompressValue() && self::isCompressedValue($serialized)) { |
| 372 | $inflated = @gzdecode($serialized); |
| 373 | if ($inflated !== false) { |
| 374 | unset($serialized); |
| 375 | if ($cache) { |
| 376 | self::updateCachedOption($key, unserialize($inflated)); |
| 377 | return self::getCachedOption($key); |
| 378 | } |
| 379 | return unserialize($inflated); |
| 380 | } |
| 381 | } |
| 382 | if ($cache) { |
| 383 | self::updateCachedOption($key, unserialize($serialized)); |
| 384 | return self::getCachedOption($key); |
| 385 | } |
| 386 | return unserialize($serialized); |
| 387 | } |
| 388 | else { |
| 389 | $serialized = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $key); |
| 390 | self::getDB()->flush(); //clear cache |
| 391 | if ($serialized) { |
| 392 | if (self::canCompressValue() && self::isCompressedValue($serialized)) { |
| 393 | $inflated = @gzdecode($serialized); |
| 394 | if ($inflated !== false) { |
| 395 | unset($serialized); |
| 396 | return unserialize($inflated); |
| 397 | } |
| 398 | } |
| 399 | if ($cache) { |
| 400 | self::updateCachedOption($key, unserialize($serialized)); |
| 401 | return self::getCachedOption($key); |
| 402 | } |
| 403 | return unserialize($serialized); |
| 404 | } |
| 405 | } |
| 406 | |
| 407 | return $default; |
| 408 | } |
| 409 | |
| 410 | public static function set_ser($key, $val, $allowCompression = false, $autoload = self::AUTOLOAD) { |
| 411 | /* |
| 412 | * Because of the small default value for `max_allowed_packet` and `max_long_data_size`, we're stuck splitting |
| 413 | * large values into multiple chunks. To minimize memory use, the MySQLi driver is used directly when possible. |
| 414 | */ |
| 415 | |
| 416 | global $wpdb; |
| 417 | $dbh = $wpdb->dbh; |
| 418 | |
| 419 | if (!self::$tableExists) { |
| 420 | return; |
| 421 | } |
| 422 | |
| 423 | self::delete_ser_chunked($key); //Ensure any old values for a chunked value are deleted first |
| 424 | |
| 425 | if (self::canCompressValue() && $allowCompression) { |
| 426 | $data = gzencode(serialize($val)); |
| 427 | } |
| 428 | else { |
| 429 | $data = serialize($val); |
| 430 | } |
| 431 | |
| 432 | if (!$wpdb->use_mysqli) { |
| 433 | $data = bin2hex($data); |
| 434 | } |
| 435 | |
| 436 | $dataLength = strlen($data); |
| 437 | $chunkSize = intval((self::getDB()->getMaxAllowedPacketBytes() - 50) / 1.2); //Based on max_allowed_packet + 20% for escaping and SQL |
| 438 | $chunkSize = $chunkSize - ($chunkSize % 2); //Ensure it's even |
| 439 | $chunkedValueKey = self::ser_chunked_key($key); |
| 440 | if ($dataLength > $chunkSize) { |
| 441 | $chunks = 0; |
| 442 | while (($chunks * $chunkSize) < $dataLength) { |
| 443 | $dataChunk = substr($data, $chunks * $chunkSize, $chunkSize); |
| 444 | if ($wpdb->use_mysqli) { |
| 445 | $chunkKey = $chunkedValueKey . $chunks; |
| 446 | $stmt = $dbh->prepare("INSERT IGNORE INTO " . self::table() . " (name, val, autoload) VALUES (?, ?, 'no')"); |
| 447 | $null = NULL; |
| 448 | $stmt->bind_param("sb", $chunkKey, $null); |
| 449 | |
| 450 | if (!$stmt->send_long_data(1, $dataChunk)) { |
| 451 | wordfence::status(2, 'error', "Error writing value chunk for {$key} (MySQLi error: [{$dbh->errno}] {$dbh->error})"); |
| 452 | return false; |
| 453 | } |
| 454 | |
| 455 | if (!$stmt->execute()) { |
| 456 | wordfence::status(2, 'error', "Error finishing writing value for {$key} (MySQLi error: [{$dbh->errno}] {$dbh->error})"); |
| 457 | return false; |
| 458 | } |
| 459 | } |
| 460 | else { |
| 461 | if (!self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val, autoload) values (%%s, X'%s', 'no')", $dataChunk), $chunkedValueKey . $chunks)) { |
| 462 | $errno = mysql_errno($wpdb->dbh); |
| 463 | wordfence::status(2, 'error', "Error writing value chunk for {$key} (MySQL error: [$errno] {$wpdb->last_error})"); |
| 464 | return false; |
| 465 | } |
| 466 | } |
| 467 | $chunks++; |
| 468 | } |
| 469 | |
| 470 | if (!self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val, autoload) values (%%s, X'%s', 'no')", bin2hex(serialize(array('count' => $chunks)))), $chunkedValueKey . 'header')) { |
| 471 | wordfence::status(2, 'error', "Error writing value header for {$key}"); |
| 472 | return false; |
| 473 | } |
| 474 | } |
| 475 | else { |
| 476 | $exists = self::getDB()->querySingle("select name from " . self::table() . " where name='%s'", $key); |
| 477 | |
| 478 | if ($wpdb->use_mysqli) { |
| 479 | if ($exists) { |
| 480 | $stmt = $dbh->prepare("UPDATE " . self::table() . " SET val=? WHERE name=?"); |
| 481 | $null = NULL; |
| 482 | $stmt->bind_param("bs", $null, $key); |
| 483 | } |
| 484 | else { |
| 485 | $stmt = $dbh->prepare("INSERT IGNORE INTO " . self::table() . " (val, name, autoload) VALUES (?, ?, ?)"); |
| 486 | $null = NULL; |
| 487 | $stmt->bind_param("bss", $null, $key, $autoload); |
| 488 | } |
| 489 | |
| 490 | if (!$stmt->send_long_data(0, $data)) { |
| 491 | wordfence::status(2, 'error', "Error writing value for {$key} (MySQLi error: [{$dbh->errno}] {$dbh->error})"); |
| 492 | return false; |
| 493 | } |
| 494 | |
| 495 | if (!$stmt->execute()) { |
| 496 | wordfence::status(2, 'error', "Error finishing writing value for {$key} (MySQLi error: [{$dbh->errno}] {$dbh->error})"); |
| 497 | return false; |
| 498 | } |
| 499 | } |
| 500 | else { |
| 501 | if ($exists) { |
| 502 | self::getDB()->queryWrite(sprintf("update " . self::table() . " set val=X'%s' where name=%%s", $data), $key); |
| 503 | } |
| 504 | else { |
| 505 | self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val, autoload) values (%%s, X'%s', %%s)", $data), $key, $autoload); |
| 506 | } |
| 507 | } |
| 508 | } |
| 509 | self::getDB()->flush(); |
| 510 | |
| 511 | if ($autoload != self::DONT_AUTOLOAD) { |
| 512 | self::updateCachedOption($key, $val); |
| 513 | } |
| 514 | return true; |
| 515 | } |
| 516 | |
| 517 | private static function delete_ser_chunked($key) { |
| 518 | if (!self::$tableExists) { |
| 519 | return; |
| 520 | } |
| 521 | |
| 522 | self::removeCachedOption($key); |
| 523 | |
| 524 | $chunkedValueKey = self::ser_chunked_key($key); |
| 525 | $header = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $chunkedValueKey . 'header'); |
| 526 | if (!$header) { |
| 527 | return; |
| 528 | } |
| 529 | |
| 530 | $header = unserialize($header); |
| 531 | $count = $header['count']; |
| 532 | for ($i = 0; $i < $count; $i++) { |
| 533 | self::getDB()->queryWrite("delete from " . self::table() . " where name='%s'", $chunkedValueKey . $i); |
| 534 | } |
| 535 | self::getDB()->queryWrite("delete from " . self::table() . " where name='%s'", $chunkedValueKey . 'header'); |
| 536 | } |
| 537 | public static function f($key){ |
| 538 | echo esc_attr(self::get($key)); |
| 539 | } |
| 540 | public static function cbp($key){ |
| 541 | if(self::get('isPaid') && self::get($key)){ |
| 542 | echo ' checked '; |
| 543 | } |
| 544 | } |
| 545 | public static function cb($key){ |
| 546 | if(self::get($key)){ |
| 547 | echo ' checked '; |
| 548 | } |
| 549 | } |
| 550 | public static function sel($key, $val, $isDefault = false){ |
| 551 | if((! self::get($key)) && $isDefault){ echo ' selected '; } |
| 552 | if(self::get($key) == $val){ echo ' selected '; } |
| 553 | } |
| 554 | private static function getDB(){ |
| 555 | if(! self::$DB){ |
| 556 | self::$DB = new wfDB(); |
| 557 | } |
| 558 | return self::$DB; |
| 559 | } |
| 560 | private static function table(){ |
| 561 | if(! self::$table){ |
| 562 | global $wpdb; |
| 563 | self::$table = $wpdb->base_prefix . 'wfConfig'; |
| 564 | } |
| 565 | return self::$table; |
| 566 | } |
| 567 | public static function haveAlertEmails(){ |
| 568 | $emails = self::getAlertEmails(); |
| 569 | return sizeof($emails) > 0 ? true : false; |
| 570 | } |
| 571 | public static function getAlertEmails(){ |
| 572 | $dat = explode(',', self::get('alertEmails')); |
| 573 | $emails = array(); |
| 574 | foreach($dat as $email){ |
| 575 | if(preg_match('/\@/', $email)){ |
| 576 | $emails[] = trim($email); |
| 577 | } |
| 578 | } |
| 579 | return $emails; |
| 580 | } |
| 581 | public static function getAlertLevel(){ |
| 582 | if(self::get('alertOn_warnings')){ |
| 583 | return 2; |
| 584 | } else if(self::get('alertOn_critical')){ |
| 585 | return 1; |
| 586 | } else { |
| 587 | return 0; |
| 588 | } |
| 589 | } |
| 590 | public static function liveTrafficEnabled(){ |
| 591 | if( (! self::get('liveTrafficEnabled')) || self::get('cacheType') == 'falcon' || self::get('cacheType') == 'php'){ return false; } |
| 592 | return true; |
| 593 | } |
| 594 | public static function enableAutoUpdate(){ |
| 595 | wfConfig::set('autoUpdate', '1'); |
| 596 | wp_clear_scheduled_hook('wordfence_daily_autoUpdate'); |
| 597 | if (is_main_site()) { |
| 598 | wp_schedule_event(time(), 'daily', 'wordfence_daily_autoUpdate'); |
| 599 | } |
| 600 | } |
| 601 | public static function disableAutoUpdate(){ |
| 602 | wfConfig::set('autoUpdate', '0'); |
| 603 | wp_clear_scheduled_hook('wordfence_daily_autoUpdate'); |
| 604 | } |
| 605 | public static function autoUpdate(){ |
| 606 | try { |
| 607 | if(getenv('noabort') != '1' && stristr($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false){ |
| 608 | $lastEmail = self::get('lastLiteSpdEmail', false); |
| 609 | if( (! $lastEmail) || (time() - (int)$lastEmail > (86400 * 30))){ |
| 610 | self::set('lastLiteSpdEmail', time()); |
| 611 | wordfence::alert("Wordfence Upgrade not run. Please modify your .htaccess", "To preserve the integrity of your website we are not running Wordfence auto-update.\n" . |
| 612 | "You are running the LiteSpeed web server which has been known to cause a problem with Wordfence auto-update.\n" . |
| 613 | "Please go to your website now and make a minor change to your .htaccess to fix this.\n" . |
| 614 | "You can find out how to make this change at:\n" . |
| 615 | "https://docs.wordfence.com/en/LiteSpeed_aborts_Wordfence_scans_and_updates._How_do_I_prevent_that%3F\n" . |
| 616 | "\nAlternatively you can disable auto-update on your website to stop receiving this message and upgrade Wordfence manually.\n", |
| 617 | '127.0.0.1' |
| 618 | ); |
| 619 | } |
| 620 | return; |
| 621 | } |
| 622 | require_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'); |
| 623 | require_once(ABSPATH . 'wp-admin/includes/misc.php'); |
| 624 | /* We were creating show_message here so that WP did not write to STDOUT. This had the strange effect of throwing an error about redeclaring show_message function, but only when a crawler hit the site and triggered the cron job. Not a human. So we're now just require'ing misc.php which does generate output, but that's OK because it is a loopback cron request. |
| 625 | if(! function_exists('show_message')){ |
| 626 | function show_message($msg = 'null'){} |
| 627 | } |
| 628 | */ |
| 629 | if(! defined('FS_METHOD')){ |
| 630 | define('FS_METHOD', 'direct'); //May be defined already and might not be 'direct' so this could cause problems. But we were getting reports of a warning that this is already defined, so this check added. |
| 631 | } |
| 632 | require_once(ABSPATH . 'wp-includes/update.php'); |
| 633 | require_once(ABSPATH . 'wp-admin/includes/file.php'); |
| 634 | wp_update_plugins(); |
| 635 | ob_start(); |
| 636 | $upgrader = new Plugin_Upgrader(); |
| 637 | $upret = $upgrader->upgrade(WORDFENCE_BASENAME); |
| 638 | if($upret){ |
| 639 | $cont = file_get_contents(WORDFENCE_FCPATH); |
| 640 | if(wfConfig::get('alertOn_update') == '1' && preg_match('/Version: (\d+\.\d+\.\d+)/', $cont, $matches) ){ |
| 641 | wordfence::alert("Wordfence Upgraded to version " . $matches[1], "Your Wordfence installation has been upgraded to version " . $matches[1], '127.0.0.1'); |
| 642 | } |
| 643 | } |
| 644 | $output = @ob_get_contents(); |
| 645 | @ob_end_clean(); |
| 646 | } catch(Exception $e){} |
| 647 | } |
| 648 | |
| 649 | /** |
| 650 | * .htaccess file contents to disable all script execution in a given directory. |
| 651 | */ |
| 652 | private static $_disable_scripts_htaccess = '# BEGIN Wordfence code execution protection |
| 653 | <IfModule mod_php5.c> |
| 654 | php_flag engine 0 |
| 655 | </IfModule> |
| 656 | |
| 657 | AddHandler cgi-script .php .phtml .php3 .pl .py .jsp .asp .htm .shtml .sh .cgi |
| 658 | Options -ExecCGI |
| 659 | # END Wordfence code execution protection |
| 660 | '; |
| 661 | |
| 662 | private static function _uploadsHtaccessFilePath() { |
| 663 | $upload_dir = wp_upload_dir(); |
| 664 | return $upload_dir['basedir'] . '/.htaccess'; |
| 665 | } |
| 666 | |
| 667 | /** |
| 668 | * Add/Merge .htaccess file in the uploads directory to prevent code execution. |
| 669 | * |
| 670 | * @return bool |
| 671 | * @throws wfConfigException |
| 672 | */ |
| 673 | public static function disableCodeExecutionForUploads() { |
| 674 | $uploads_htaccess_file_path = self::_uploadsHtaccessFilePath(); |
| 675 | $uploads_htaccess_has_content = false; |
| 676 | if (file_exists($uploads_htaccess_file_path)) { |
| 677 | $htaccess_contents = file_get_contents($uploads_htaccess_file_path); |
| 678 | |
| 679 | // htaccess exists and contains our htaccess code to disable script execution, nothing more to do |
| 680 | if (strpos($htaccess_contents, self::$_disable_scripts_htaccess) !== false) { |
| 681 | return true; |
| 682 | } |
| 683 | $uploads_htaccess_has_content = strlen(trim($htaccess_contents)) > 0; |
| 684 | } |
| 685 | if (@file_put_contents($uploads_htaccess_file_path, ($uploads_htaccess_has_content ? "\n\n" : "") . self::$_disable_scripts_htaccess, FILE_APPEND | LOCK_EX) === false) { |
| 686 | throw new wfConfigException("Unable to save the .htaccess file needed to disable script execution in the uploads directory. Please check your permissions on that directory."); |
| 687 | } |
| 688 | return true; |
| 689 | } |
| 690 | |
| 691 | /** |
| 692 | * Remove script execution protections for our the .htaccess file in the uploads directory. |
| 693 | * |
| 694 | * @return bool |
| 695 | * @throws wfConfigException |
| 696 | */ |
| 697 | public static function removeCodeExecutionProtectionForUploads() { |
| 698 | $uploads_htaccess_file_path = self::_uploadsHtaccessFilePath(); |
| 699 | if (file_exists($uploads_htaccess_file_path)) { |
| 700 | $htaccess_contents = file_get_contents($uploads_htaccess_file_path); |
| 701 | |
| 702 | // Check that it is in the file |
| 703 | if (strpos($htaccess_contents, self::$_disable_scripts_htaccess) !== false) { |
| 704 | $htaccess_contents = str_replace(self::$_disable_scripts_htaccess, '', $htaccess_contents); |
| 705 | |
| 706 | $error_message = "Unable to remove code execution protections applied to the .htaccess file in the uploads directory. Please check your permissions on that file."; |
| 707 | if (strlen(trim($htaccess_contents)) === 0) { |
| 708 | // empty file, remove it |
| 709 | if (!@unlink($uploads_htaccess_file_path)) { |
| 710 | throw new wfConfigException($error_message); |
| 711 | } |
| 712 | |
| 713 | } elseif (@file_put_contents($uploads_htaccess_file_path, $htaccess_contents, LOCK_EX) === false) { |
| 714 | throw new wfConfigException($error_message); |
| 715 | } |
| 716 | } |
| 717 | } |
| 718 | return true; |
| 719 | } |
| 720 | } |
| 721 | |
| 722 | class wfConfigException extends Exception {} |
| 723 | |
| 724 | ?> |
| 725 |