Diff
14 years ago
.htaccess
14 years ago
Diff.php
14 years ago
GeoIP.dat
13 years ago
IPTraf.php
13 years ago
diffResult.php
14 years ago
email_genericAlert.php
14 years ago
email_newIssues.php
14 years ago
email_unlockRequest.php
14 years ago
menu_activity.php
13 years ago
menu_blockedIPs.php
13 years ago
menu_countryBlocking.php
13 years ago
menu_options.php
13 years ago
menu_scan.php
13 years ago
menu_scanSchedule.php
13 years ago
schedWeekEntry.php
13 years ago
sysinfo.php
14 years ago
unknownFiles.php
13 years ago
viewFullActivityLog.php
13 years ago
wf503.php
13 years ago
wfAPI.php
13 years ago
wfAction.php
14 years ago
wfArray.php
13 years ago
wfBrowscap.php
14 years ago
wfBrowscapCache.php
14 years ago
wfBulkCountries.php
13 years ago
wfConfig.php
13 years ago
wfCountryMap.php
13 years ago
wfCrawl.php
13 years ago
wfDB.php
13 years ago
wfDict.php
14 years ago
wfGeoIP.php
13 years ago
wfIssues.php
13 years ago
wfLockedOut.php
14 years ago
wfLog.php
13 years ago
wfModTracker.php
14 years ago
wfRate.php
14 years ago
wfScanEngine.php
13 years ago
wfSchema.php
13 years ago
wfUnlockMsg.php
14 years ago
wfUtils.php
13 years ago
wfViewResult.php
14 years ago
wordfenceClass.php
13 years ago
wordfenceConstants.php
13 years ago
wordfenceHash.php
13 years ago
wordfenceScanner.php
13 years ago
wordfenceURLHoover.php
13 years ago
wordfenceClass.php
1529 lines
| 1 | <?php |
| 2 | require_once('wordfenceConstants.php'); |
| 3 | require_once('wfScanEngine.php'); |
| 4 | require_once('wfCrawl.php'); |
| 5 | require_once 'Diff.php'; |
| 6 | require_once 'Diff/Renderer/Html/SideBySide.php'; |
| 7 | require_once 'wfAPI.php'; |
| 8 | require_once 'wfIssues.php'; |
| 9 | require_once('wfDB.php'); |
| 10 | require_once('wfUtils.php'); |
| 11 | require_once('wfLog.php'); |
| 12 | require_once('wfConfig.php'); |
| 13 | require_once('wfSchema.php'); |
| 14 | class wordfence { |
| 15 | public static $printStatus = false; |
| 16 | protected static $lastURLError = false; |
| 17 | protected static $curlContent = ""; |
| 18 | protected static $curlDataWritten = 0; |
| 19 | protected static $hasher = ''; |
| 20 | protected static $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; |
| 21 | protected static $ignoreList = false; |
| 22 | public static $newVisit = false; |
| 23 | private static $wfLog = false; |
| 24 | private static $hitID = 0; |
| 25 | private static $statusStartMsgs = array(); |
| 26 | private static $debugOn = null; |
| 27 | public static function installPlugin(){ |
| 28 | self::runInstall(); |
| 29 | //Used by MU code below |
| 30 | update_option('wordfenceActivated', 1); |
| 31 | } |
| 32 | public static function uninstallPlugin(){ |
| 33 | //Used by MU code below |
| 34 | update_option('wordfenceActivated', 0); |
| 35 | wp_clear_scheduled_hook('wordfence_daily_cron'); |
| 36 | wp_clear_scheduled_hook('wordfence_hourly_cron'); |
| 37 | |
| 38 | //Remove old legacy cron job if it exists |
| 39 | wp_clear_scheduled_hook('wordfence_scheduled_scan'); |
| 40 | |
| 41 | //Remove all scheduled scans. |
| 42 | self::unscheduleAllScans(); |
| 43 | |
| 44 | if(wfConfig::get('deleteTablesOnDeact')){ |
| 45 | $schema = new wfSchema(); |
| 46 | $schema->dropAll(); |
| 47 | foreach(array('wordfence_version', 'wordfenceActivated') as $opt){ |
| 48 | delete_option($opt); |
| 49 | } |
| 50 | } |
| 51 | } |
| 52 | public static function hourlyCron(){ |
| 53 | global $wpdb; $p = $wpdb->base_prefix; |
| 54 | $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion()); |
| 55 | |
| 56 | |
| 57 | if(wfConfig::get('other_WFNet')){ |
| 58 | $wfdb = new wfDB(); |
| 59 | $q1 = $wfdb->query("select URI from $p"."wfNet404s where ctime > unix_timestamp() - 3600 limit 1000"); |
| 60 | $URIs = array(); |
| 61 | while($rec = mysql_fetch_assoc($q1)){ |
| 62 | array_push($URIs, $rec['URI']); |
| 63 | } |
| 64 | $wfdb->truncate($p . "wfNet404s"); |
| 65 | if(sizeof($URIs) > 0){ |
| 66 | try { |
| 67 | $api->call('send_net_404s', array(), array( 'URIs' => json_encode($URIs) )); |
| 68 | } catch(Exception $e){ |
| 69 | //Ignore |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | $q2 = $wfdb->query("select INET_NTOA(IP) as IP from $p"."wfVulnScanners where ctime > unix_timestamp() - 3600"); |
| 74 | $scanCont = ""; |
| 75 | while($rec = mysql_fetch_assoc($q2)){ |
| 76 | $scanCont .= pack('N', ip2long($rec['IP'])); |
| 77 | } |
| 78 | $wfdb->truncate($p . "wfVulnScanners"); |
| 79 | |
| 80 | $q3 = $wfdb->query("select INET_NTOA(IP) as IP from $p"."wfLockedOut where blockedTime > unix_timestamp() - 3600"); |
| 81 | $lockCont = ""; |
| 82 | while($rec = mysql_fetch_assoc($q3)){ |
| 83 | $lockCont .= pack('N', ip2long($rec['IP'])); |
| 84 | } |
| 85 | if(strlen($lockCont) > 0 || strlen($scanCont) > 0){ |
| 86 | $cont = pack('N', strlen($lockCont) / 4) . $lockCont . pack('N', strlen($scanCont) / 4) . $scanCont; |
| 87 | try { |
| 88 | $resp = $api->binCall('get_net_bad_ips', $cont); |
| 89 | if($resp['code'] == 200){ |
| 90 | $len = strlen($resp['data']); |
| 91 | $reason = "WFSN: Blocked by Wordfence Security Network"; |
| 92 | $wfdb->query("delete from $p"."wfBlocks where wfsn=1 and permanent=0"); |
| 93 | if($len > 0 && $len % 4 == 0){ |
| 94 | for($i = 0; $i < $len; $i += 4){ |
| 95 | list($ipLong) = array_values(unpack('N', substr($resp['data'], $i, 4))); |
| 96 | $IPStr = long2ip($ipLong); |
| 97 | if(! self::getLog()->isWhitelisted($IPStr)){ |
| 98 | self::getLog()->blockIP($IPStr, $reason, true); |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | } catch(Exception $e){ |
| 104 | //Ignore |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | public static function dailyCron(){ |
| 110 | $wfdb = new wfDB(); |
| 111 | global $wpdb; $p = $wpdb->base_prefix; |
| 112 | $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion()); |
| 113 | try { |
| 114 | $patData = $api->call('get_known_vuln_pattern'); |
| 115 | if(is_array($patData) && $patData['pat']){ |
| 116 | if(@preg_match($patData['pat'], 'wordfence_test_vuln_match')){ |
| 117 | wfConfig::set('vulnRegex', $patData['pat']); |
| 118 | } |
| 119 | } |
| 120 | } catch(Exception $e){ |
| 121 | wordfence::status(4, 'error', "Could not fetch vulnerability patterns in scheduled job: " . $e->getMessage()); |
| 122 | } |
| 123 | |
| 124 | $wfdb->query("delete from $p"."wfLocs where ctime < unix_timestamp() - %d", WORDFENCE_MAX_IPLOC_AGE); |
| 125 | $wfdb->truncate($p . "wfBadLeechers"); //only uses date that's less than 1 minute old |
| 126 | $wfdb->query("delete from $p"."wfBlocks where (blockedTime + %s < unix_timestamp()) and permanent=0", wfConfig::get('blockedTime')); |
| 127 | $wfdb->query("delete from $p"."wfCrawlers where lastUpdate < unix_timestamp() - (86400 * 7)"); |
| 128 | |
| 129 | $count = $wfdb->querySingle("select count(*) as cnt from $p"."wfHits"); |
| 130 | if($count > 20000){ |
| 131 | $wfdb->query("delete from $p"."wfHits order by id asc limit " . ($count - 20000)); |
| 132 | } |
| 133 | $maxRows = 1000; //affects stuff further down too |
| 134 | foreach(array('wfLeechers', 'wfScanners') as $table){ |
| 135 | //This is time based per IP so shouldn't get too big |
| 136 | $wfdb->query("delete from $p"."$table where eMin < ((unix_timestamp() - (86400 * 2)) / 60)"); |
| 137 | } |
| 138 | $wfdb->query("delete from $p"."wfLockedOut where blockedTime + %s < unix_timestamp()", wfConfig::get('loginSec_lockoutMins') * 60); |
| 139 | $count2 = $wfdb->querySingle("select count(*) as cnt from $p"."wfLogins"); |
| 140 | if($count2 > 20000){ |
| 141 | $wfdb->truncate($p . "wfLogins"); //in case of Dos |
| 142 | } else if($count2 > $maxRows){ |
| 143 | $wfdb->query("delete from $p"."wfLogins order by ctime asc limit %d", ($count2 - $maxRows)); |
| 144 | } |
| 145 | $wfdb->query("delete from $p"."wfReverseCache where unix_timestamp() - lastUpdate > 86400"); |
| 146 | $count3 = $wfdb->querySingle("select count(*) as cnt from $p"."wfThrottleLog"); |
| 147 | if($count3 > 20000){ |
| 148 | $wfdb->truncate($p . "wfThrottleLog"); //in case of DoS |
| 149 | } else if($count3 > $maxRows){ |
| 150 | $wfdb->query("delete from $p"."wfThrottleLog order by endTime asc limit %d", ($count3 - $maxRows)); |
| 151 | } |
| 152 | $count4 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus"); |
| 153 | if($count4 > 100000){ |
| 154 | $wfdb->truncate($p . "wfStatus"); |
| 155 | } else if($count4 > 1000){ //max status events we keep. This determines how much gets emailed to us when users sends us a debug report. |
| 156 | $wfdb->query("delete from $p"."wfStatus where level != 10 order by ctime asc limit %d", ($count4 - 1000)); |
| 157 | $count5 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus where level=10"); |
| 158 | if($count5 > 100){ |
| 159 | $wfdb->query("delete from $p"."wfStatus where level = 10 order by ctime asc limit %d", ($count5 - 100) ); |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | } |
| 164 | public static function runInstall(){ |
| 165 | update_option('wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install. |
| 166 | //EVERYTHING HERE MUST BE IDEMPOTENT |
| 167 | |
| 168 | //Remove old legacy cron job if exists |
| 169 | wp_clear_scheduled_hook('wordfence_scheduled_scan'); |
| 170 | |
| 171 | //Install new schedule. If schedule config is blank it will install the default 'auto' schedule. |
| 172 | wordfence::scheduleScans(); |
| 173 | |
| 174 | $schema = new wfSchema(); |
| 175 | $schema->createAll(); //if not exists |
| 176 | wfConfig::setDefaults(); //If not set |
| 177 | |
| 178 | if(! wfConfig::get('apiKey')){ |
| 179 | $api = new wfAPI('', wfUtils::getWPVersion()); |
| 180 | try { |
| 181 | $keyData = $api->call('get_anon_api_key'); |
| 182 | if($keyData['ok'] && $keyData['apiKey']){ |
| 183 | wfConfig::set('apiKey', $keyData['apiKey']); |
| 184 | } else { |
| 185 | throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key."); |
| 186 | } |
| 187 | } catch(Exception $e){ |
| 188 | error_log("Could not fetch free API key from Wordfence: " . $e->getMessage()); |
| 189 | return; |
| 190 | } |
| 191 | } |
| 192 | wp_clear_scheduled_hook('wordfence_daily_cron'); |
| 193 | wp_clear_scheduled_hook('wordfence_hourly_cron'); |
| 194 | wp_schedule_event(time(), 'daily', 'wordfence_daily_cron'); |
| 195 | wp_schedule_event(time(), 'hourly', 'wordfence_hourly_cron'); |
| 196 | $db = new wfDB(); |
| 197 | |
| 198 | if($db->columnExists('wfHits', 'HTTPHeaders')){ //Upgrade from 3.0.4 |
| 199 | global $wpdb; |
| 200 | $prefix = $wpdb->base_prefix; |
| 201 | $count = $db->querySingle("select count(*) as cnt from $prefix"."wfHits"); |
| 202 | if($count > 20000){ |
| 203 | $db->query("delete from $prefix"."wfHits order by id asc limit " . ($count - 20000)); |
| 204 | } |
| 205 | $db->dropColumn('wfHits', 'HTTPHeaders'); |
| 206 | } |
| 207 | |
| 208 | //Upgrading from 1.5.6 or earlier needs: |
| 209 | $db->createKeyIfNotExists('wfStatus', 'level', 'k2'); |
| 210 | if(wfConfig::get('isPaid') == 'free'){ |
| 211 | wfConfig::set('isPaid', ''); |
| 212 | } |
| 213 | //End upgrade from 1.5.6 |
| 214 | |
| 215 | //Show an alert that user needs to enter an email address if user has not seen it before |
| 216 | if(! wfConfig::get('alertEmailMsgCount')){ |
| 217 | wfConfig::set('alertEmailMsgCount', 0); |
| 218 | } |
| 219 | |
| 220 | @chmod(dirname(__FILE__) . '/../wfscan.php', 0755); |
| 221 | @chmod(dirname(__FILE__) . '/../visitor.php', 0755); |
| 222 | |
| 223 | global $wpdb; |
| 224 | $prefix = $wpdb->base_prefix; |
| 225 | $db->queryIgnoreError("alter table $prefix"."wfConfig modify column val longblob"); |
| 226 | $db->queryIgnoreError("alter table $prefix"."wfBlocks add column permanent tinyint UNSIGNED default 0"); |
| 227 | $db->queryIgnoreError("alter table $prefix"."wfStatus modify column msg varchar(1000) NOT NULL"); |
| 228 | //3.1.2 to 3.1.4 |
| 229 | $db->queryIgnoreError("alter table $prefix"."wfBlocks modify column blockedTime bigint signed NOT NULL"); |
| 230 | //3.2.1 to 3.2.2 |
| 231 | $db->queryIgnoreError("alter table $prefix"."wfLockedOut modify column blockedTime bigint signed NOT NULL"); |
| 232 | |
| 233 | //Must be the final line |
| 234 | } |
| 235 | public static function install_actions(){ |
| 236 | $versionInOptions = get_option('wordfence_version', false); |
| 237 | if( (! $versionInOptions) || version_compare(WORDFENCE_VERSION, $versionInOptions, '>')){ |
| 238 | //Either there is no version in options or the version in options is greater and we need to run the upgrade |
| 239 | self::runInstall(); |
| 240 | } |
| 241 | if(defined('MULTISITE') && MULTISITE === true){ |
| 242 | global $blog_id; |
| 243 | if($blog_id == 1 && get_option('wordfenceActivated') != 1){ return; } //Because the plugin is active once installed, even before it's network activated, for site 1 (WordPress team, why?!) |
| 244 | } |
| 245 | add_action('wordfence_start_scheduled_scan', 'wordfence::wordfenceStartScheduledScan'); |
| 246 | add_action('wordfence_daily_cron', 'wordfence::dailyCron'); |
| 247 | add_action('wordfence_hourly_cron', 'wordfence::hourlyCron'); |
| 248 | add_action('plugins_loaded', 'wordfence::veryFirstAction'); |
| 249 | add_action('init', 'wordfence::initAction'); |
| 250 | add_action('template_redirect', 'wordfence::templateRedir'); |
| 251 | add_action('shutdown', 'wordfence::shutdownAction'); |
| 252 | add_action('wp_authenticate','wordfence::authAction'); |
| 253 | add_action('login_init','wordfence::loginInitAction'); |
| 254 | add_action('wp_login','wordfence::loginAction'); |
| 255 | add_action('wp_logout','wordfence::logoutAction'); |
| 256 | add_action('profile_update', 'wordfence::profileUpdateAction', '99', 2); |
| 257 | add_action('lostpassword_post', 'wordfence::lostPasswordPost', '1'); |
| 258 | //add_filter('cron_schedules', 'wordfence::moreCronReccurences'); |
| 259 | add_filter('pre_comment_approved', 'wordfence::preCommentApprovedFilter', '99', 2); |
| 260 | add_filter('authenticate', 'wordfence::authenticateFilter', 99, 3); |
| 261 | //html|xhtml|atom|rss2|rdf|comment|export |
| 262 | add_filter('get_the_generator_html', 'wordfence::genFilter', 99, 2); |
| 263 | add_filter('get_the_generator_xhtml', 'wordfence::genFilter', 99, 2); |
| 264 | add_filter('get_the_generator_atom', 'wordfence::genFilter', 99, 2); |
| 265 | add_filter('get_the_generator_rss2', 'wordfence::genFilter', 99, 2); |
| 266 | add_filter('get_the_generator_rdf', 'wordfence::genFilter', 99, 2); |
| 267 | add_filter('get_the_generator_comment', 'wordfence::genFilter', 99, 2); |
| 268 | add_filter('get_the_generator_export', 'wordfence::genFilter', 99, 2); |
| 269 | if(is_admin()){ |
| 270 | add_action('admin_init', 'wordfence::admin_init'); |
| 271 | if(is_multisite()){ |
| 272 | if(wfUtils::isAdminPageMU()){ |
| 273 | add_action('network_admin_menu', 'wordfence::admin_menus'); |
| 274 | } //else don't show menu |
| 275 | } else { |
| 276 | add_action('admin_menu', 'wordfence::admin_menus'); |
| 277 | } |
| 278 | } |
| 279 | } |
| 280 | public static function ajaxReceiver(){ |
| 281 | if(! wfUtils::isAdmin()){ |
| 282 | die(json_encode(array('errorMsg' => "You appear to have logged out or you are not an admin. Please sign-out and sign-in again."))); |
| 283 | } |
| 284 | $func = $_POST['action']; |
| 285 | $nonce = $_POST['nonce']; |
| 286 | if(! wp_verify_nonce($nonce, 'wp-ajax')){ |
| 287 | die(json_encode(array('errorMsg' => "Your browser sent an invalid security token to Wordfence. Please try reloading this page or signing out and in again."))); |
| 288 | } |
| 289 | //func is e.g. wordfence_ticker so need to munge it |
| 290 | $func = str_replace('wordfence_', '', $func); |
| 291 | $returnArr = call_user_func('wordfence::ajax_' . $func . '_callback'); |
| 292 | if($returnArr === false){ |
| 293 | $returnArr = array('errorMsg' => "Wordfence encountered an internal error executing that request."); |
| 294 | } |
| 295 | |
| 296 | if(! is_array($returnArr)){ |
| 297 | error_log("Function $func did not return an array and did not generate an error."); |
| 298 | $returnArr = array(); |
| 299 | } |
| 300 | if(isset($returnARr['nonce'])){ |
| 301 | error_log("Wordfence ajax function return an array with 'nonce' already set. This could be a bug."); |
| 302 | } |
| 303 | $returnArr['nonce'] = wp_create_nonce('wp-ajax'); |
| 304 | die(json_encode($returnArr)); |
| 305 | } |
| 306 | public static function lostPasswordPost(){ |
| 307 | $IP = wfUtils::getIP(); |
| 308 | if(self::getLog()->isWhitelisted($IP)){ |
| 309 | return; |
| 310 | } |
| 311 | if(self::isLockedOut($IP)){ |
| 312 | require('wfLockedOut.php'); |
| 313 | } |
| 314 | $email = $_POST['user_login']; |
| 315 | if(empty($email)){ return; } |
| 316 | $user = get_user_by('email', $_POST['user_login']); |
| 317 | if($user){ |
| 318 | if(wfConfig::get('alertOn_lostPasswdForm')){ |
| 319 | wordfence::alert("Password recovery attempted", "Someone tried to recover the password for user with email address: $email", $IP); |
| 320 | } |
| 321 | } |
| 322 | if(wfConfig::get('loginSecurityEnabled')){ |
| 323 | $tKey = 'wffgt_' . wfUtils::inet_aton($IP); |
| 324 | $forgotAttempts = get_transient($tKey); |
| 325 | if($forgotAttempts){ |
| 326 | $forgotAttempts++; |
| 327 | } else { |
| 328 | $forgotAttempts = 1; |
| 329 | } |
| 330 | if($forgotAttempts >= wfConfig::get('loginSec_maxForgotPasswd')){ |
| 331 | self::lockOutIP($IP, "Exceeded the maximum number of tries to recover their password which is set at: " . wfConfig::get('loginSec_maxForgotPasswd')); |
| 332 | require('wfLockedOut.php'); |
| 333 | } |
| 334 | set_transient($tKey, $forgotAttempts, wfConfig::get('loginSec_countFailMins') * 60); |
| 335 | } |
| 336 | } |
| 337 | public static function lockOutIP($IP, $reason){ |
| 338 | if(wfConfig::get('alertOn_loginLockout')){ |
| 339 | wordfence::alert("User locked out from signing in", "A user with IP address $IP has been locked out from the signing in or using the password recovery form for the following reason: $reason", $IP); |
| 340 | } |
| 341 | self::getLog()->lockOutIP(wfUtils::getIP(), $reason); |
| 342 | } |
| 343 | public static function isLockedOut($IP){ |
| 344 | return self::getLog()->isIPLockedOut($IP); |
| 345 | } |
| 346 | public static function veryFirstAction(){ |
| 347 | $wfFunc = @$_GET['_wfsf']; |
| 348 | if($wfFunc == 'unlockEmail'){ |
| 349 | $email = trim($_POST['email']); |
| 350 | global $wpdb; |
| 351 | $ws = $wpdb->get_results("SELECT ID, user_login FROM $wpdb->users"); |
| 352 | $users = array(); |
| 353 | foreach($ws as $user){ |
| 354 | $userDat = get_userdata($user->ID); |
| 355 | if($userDat->user_level > 7){ |
| 356 | if($email == $userDat->user_email){ |
| 357 | $found = true; |
| 358 | break; |
| 359 | } |
| 360 | } |
| 361 | } |
| 362 | if(! $found){ |
| 363 | foreach(wfConfig::getAlertEmails() as $alertEmail){ |
| 364 | if($alertEmail == $email){ |
| 365 | $found = true; |
| 366 | break; |
| 367 | } |
| 368 | } |
| 369 | } |
| 370 | if($found){ |
| 371 | $key = wfUtils::bigRandomHex(); |
| 372 | $IP = wfUtils::getIP(); |
| 373 | set_transient('wfunlock_' . $key, $IP, 1800); |
| 374 | $content = wfUtils::tmpl('email_unlockRequest.php', array( |
| 375 | 'siteName' => get_bloginfo('name', 'raw'), |
| 376 | 'siteURL' => wfUtils::getSiteBaseURL(), |
| 377 | 'unlockHref' => wfUtils::getSiteBaseURL() . '?_wfsf=unlockAccess&key=' . $key, |
| 378 | 'key' => $key, |
| 379 | 'IP' => $IP |
| 380 | )); |
| 381 | wp_mail($email, "Unlock email requested", $content, "Content-Type: text/html"); |
| 382 | } |
| 383 | echo "<html><body><h1>Your request was received</h1><p>We received a request to email \"$email\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, then they have been emailed instructions on how to regain access to this sytem. The instructions we sent will expire 30 minutes from now.</body></html>"; |
| 384 | exit(); |
| 385 | } else if($wfFunc == 'unlockAccess'){ |
| 386 | if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', get_transient('wfunlock_' . $_GET['key']))){ |
| 387 | echo "Invalid key provided for authentication."; |
| 388 | exit(); |
| 389 | } |
| 390 | /* You can enable this for paranoid security leve. |
| 391 | if(get_transient('wfunlock_' . $_GET['key']) != wfUtils::getIP()){ |
| 392 | echo "You can only use this link from the IP address you used to generate the unlock email."; |
| 393 | exit(); |
| 394 | } |
| 395 | */ |
| 396 | $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion()); |
| 397 | if($_GET['func'] == 'unlockMyIP'){ |
| 398 | $wfLog->unblockIP(wfUtils::getIP()); |
| 399 | $wfLog->unlockOutIP(wfUtils::getIP()); |
| 400 | header('Location: ' . wp_login_url()); |
| 401 | exit(); |
| 402 | } else if($_GET['func'] == 'unlockAllIPs'){ |
| 403 | wordfence::status(1, 'info', "Request received via unlock email link to unblock all IP's."); |
| 404 | $wfLog->unblockAllIPs(); |
| 405 | $wfLog->unlockAllIPs(); |
| 406 | header('Location: ' . wp_login_url()); |
| 407 | exit(); |
| 408 | } else if($_GET['func'] == 'disableRules'){ |
| 409 | wfConfig::set('firewallEnabled', 0); |
| 410 | wfConfig::set('loginSecurityEnabled', 0); |
| 411 | wordfence::status(1, 'info', "Request received via unlock email link to unblock all IP's via disabling firewall rules."); |
| 412 | $wfLog->unblockAllIPs(); |
| 413 | $wfLog->unlockAllIPs(); |
| 414 | wfConfig::set('cbl_countries', ''); //unblock all countries |
| 415 | header('Location: ' . wp_login_url()); |
| 416 | exit(); |
| 417 | } else { |
| 418 | echo "Invalid function specified. Please check the link we emailed you and make sure it was not cut-off by your email reader."; |
| 419 | exit(); |
| 420 | } |
| 421 | } |
| 422 | |
| 423 | $wfLog = self::getLog(); |
| 424 | $wfLog->firewallBadIPs(); |
| 425 | } |
| 426 | public static function loginAction($username){ |
| 427 | if(sizeof($_POST) < 1){ return; } //only execute if login form is posted |
| 428 | if(! $username){ return; } |
| 429 | $user = get_user_by('login', $username); |
| 430 | $userID = $user ? $user->ID : 0; |
| 431 | self::getLog()->logLogin('loginOK', 0, $username); |
| 432 | if(user_can($userID, 'update_core')){ |
| 433 | if(wfConfig::get('alertOn_adminLogin')){ |
| 434 | wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.", wfUtils::getIP()); |
| 435 | } |
| 436 | } else { |
| 437 | if(wfConfig::get('alertOn_nonAdminLogin')){ |
| 438 | wordfence::alert("User login", "A non-admin user with username \"$username\" signed in to your WordPress site.", wfUtils::getIP()); |
| 439 | } |
| 440 | } |
| 441 | } |
| 442 | public static function authenticateFilter($authResult){ |
| 443 | $IP = wfUtils::getIP(); |
| 444 | if(self::getLog()->isWhitelisted($IP)){ |
| 445 | return $authResult; |
| 446 | } |
| 447 | if(wfConfig::get('loginSecurityEnabled')){ |
| 448 | if(is_wp_error($authResult) && $authResult->get_error_code() == 'invalid_username' && wfConfig::get('loginSec_lockInvalidUsers')){ |
| 449 | self::lockOutIP($IP, "Used an invalid username to try to sign in."); |
| 450 | require('wfLockedOut.php'); |
| 451 | } |
| 452 | $tKey = 'wflginfl_' . wfUtils::inet_aton($IP); |
| 453 | if(is_wp_error($authResult) && ($authResult->get_error_code() == 'invalid_username' || $authResult->get_error_code() == 'incorrect_password') ){ |
| 454 | $tries = get_transient($tKey); |
| 455 | if($tries){ |
| 456 | $tries++; |
| 457 | } else { |
| 458 | $tries = 1; |
| 459 | } |
| 460 | if($tries >= wfConfig::get('loginSec_maxFailures')){ |
| 461 | self::lockOutIP($IP, "Exceeded the maximum number of login failures which is: " . wfConfig::get('loginSec_maxFailures')); |
| 462 | require('wfLockedOut.php'); |
| 463 | } |
| 464 | set_transient($tKey, $tries, wfConfig::get('loginSec_countFailMins') * 60); |
| 465 | } else if(get_class($authResult) == 'WP_User'){ |
| 466 | delete_transient($tKey); //reset counter on success |
| 467 | } |
| 468 | } |
| 469 | if(is_wp_error($authResult) && ($authResult->get_error_code() == 'invalid_username' || $authResult->get_error_code() == 'incorrect_password') && wfConfig::get('loginSec_maskLoginErrors')){ |
| 470 | return new WP_Error( 'incorrect_password', sprintf( __( '<strong>ERROR</strong>: The username or password you entered is incorrect. <a href="%2$s" title="Password Lost and Found">Lost your password</a>?' ), $username, wp_lostpassword_url() ) ); |
| 471 | } |
| 472 | return $authResult; |
| 473 | } |
| 474 | public static function logoutAction(){ |
| 475 | $userID = get_current_user_id(); |
| 476 | $userDat = get_user_by('id', $userID); |
| 477 | self::getLog()->logLogin('logout', 0, $userDat->user_login); |
| 478 | } |
| 479 | public static function loginInitAction(){ |
| 480 | if(self::isLockedOut(wfUtils::getIP())){ |
| 481 | require('wfLockedOut.php'); |
| 482 | } |
| 483 | } |
| 484 | public static function authAction($username){ |
| 485 | if(self::isLockedOut(wfUtils::getIP())){ |
| 486 | require('wfLockedOut.php'); |
| 487 | } |
| 488 | if(! $username){ return; } |
| 489 | $userDat = get_user_by('login', $username); |
| 490 | if($userDat){ |
| 491 | require_once( ABSPATH . 'wp-includes/class-phpass.php'); |
| 492 | $hasher = new PasswordHash(8, TRUE); |
| 493 | if(! $hasher->CheckPassword($_POST['pwd'], $userDat->user_pass)){ |
| 494 | self::getLog()->logLogin('loginFailValidUsername', 1, $username); |
| 495 | } |
| 496 | } else { |
| 497 | self::getLog()->logLogin('loginFailInvalidUsername', 1, $username); |
| 498 | } |
| 499 | } |
| 500 | public static function getWPFileContent($file, $cType, $cName, $cVersion){ |
| 501 | if($cType == 'plugin'){ |
| 502 | if(preg_match('#^/?wp-content/plugins/[^/]+/#', $file)){ |
| 503 | $file = preg_replace('#^/?wp-content/plugins/[^/]+/#', '', $file); |
| 504 | } else { |
| 505 | //If user is using non-standard wp-content dir, then use /plugins/ in pattern to figure out what to strip off |
| 506 | $file = preg_replace('#^.*[^/]+/plugins/[^/]+/#', '', $file); |
| 507 | } |
| 508 | } else if($cType == 'theme'){ |
| 509 | if(preg_match('#/?wp-content/themes/[^/]+/#', $file)){ |
| 510 | $file = preg_replace('#/?wp-content/themes/[^/]+/#', '', $file); |
| 511 | } else { |
| 512 | $file = preg_replace('#^.*[^/]+/themes/[^/]+/#', '', $file); |
| 513 | } |
| 514 | } else if($cType == 'core'){ |
| 515 | |
| 516 | } else { |
| 517 | return array('errorMsg' => "An invalid type was specified to get file."); |
| 518 | } |
| 519 | $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion()); |
| 520 | try { |
| 521 | $contResult = $api->binCall('get_wp_file_content', array( |
| 522 | 'v' => wfUtils::getWPVersion(), |
| 523 | 'file' => $file, |
| 524 | 'cType' => $cType, |
| 525 | 'cName' => $cName, |
| 526 | 'cVersion' => $cVersion |
| 527 | )); |
| 528 | if($contResult['data']){ |
| 529 | return array('fileContent' => $contResult['data']); |
| 530 | } else { |
| 531 | throw new Exception("We could not fetch a core WordPress file from the Wordfence API."); |
| 532 | } |
| 533 | } catch (Exception $e){ |
| 534 | return array('errorMsg' => $e->getMessage()); |
| 535 | } |
| 536 | } |
| 537 | public static function ajax_saveScanSchedule_callback(){ |
| 538 | if(! wfConfig::get('isPaid')){ |
| 539 | return array('errorMsg' => "Sorry but this feature is only available for paid customers."); |
| 540 | } |
| 541 | $schedDays = explode('|', $_POST['schedTxt']); |
| 542 | $schedule = array(); |
| 543 | for($day = 0; $day <= 6; $day++){ |
| 544 | $schedule[$day] = explode(',', $schedDays[$day]); |
| 545 | } |
| 546 | $schedMode = $_POST['schedMode']; |
| 547 | wfConfig::set_ser('scanSched', $schedule); |
| 548 | wfConfig::set('schedMode', $schedMode); |
| 549 | wordfence::scheduleScans(); |
| 550 | $nextTime = self::getNextScanStartTime(); |
| 551 | return array( |
| 552 | 'ok' => 1, |
| 553 | 'nextStart' => ($nextTime ? $nextTime : '') |
| 554 | ); |
| 555 | } |
| 556 | public static function getNextScanStartTime(){ |
| 557 | $nextTime = false; |
| 558 | $cron = _get_cron_array(); |
| 559 | foreach($cron as $key => $val){ |
| 560 | if(isset($val['wordfence_start_scheduled_scan'])){ |
| 561 | $nextTime = $key; |
| 562 | break; |
| 563 | } |
| 564 | } |
| 565 | return ($nextTime ? date('l jS \of F Y H:i:s A', $nextTime + (3600 * get_option('gmt_offset'))) : ''); |
| 566 | } |
| 567 | public static function wordfenceStartScheduledScan(){ |
| 568 | //This prevents scheduled scans from piling up on low traffic blogs and all being run at once. |
| 569 | //Only one scheduled scan runs within a given 60 min window. Won't run if another scan has run within 30 mins. |
| 570 | $lastScanStart = wfConfig::get('lastScheduledScanStart', 0); |
| 571 | if($lastScanStart && (time() - $lastScanStart) < 1800){ |
| 572 | //A scheduled scan was started in the last 30 mins, so skip this one. |
| 573 | return; |
| 574 | } |
| 575 | wfConfig::set('lastScheduledScanStart', time()); |
| 576 | wordfence::status(1, 'info', "Scheduled Wordfence scan starting at " . date('l jS \of F Y h:i:s A', current_time('timestamp')) ); |
| 577 | |
| 578 | //We call this before the scan actually starts to advance the schedule for the next week. |
| 579 | //This ensures that if the scan crashes for some reason, the schedule will hold. |
| 580 | wordfence::scheduleScans(); |
| 581 | |
| 582 | wfScanEngine::startScan(); |
| 583 | } |
| 584 | public static function scheduleScans(){ //Idempotent. Deschedules everything and schedules the following week. |
| 585 | self::unscheduleAllScans(); |
| 586 | $sched = wfConfig::get_ser('scanSched', array()); |
| 587 | $mode = wfConfig::get('schedMode'); |
| 588 | if($mode == 'manual' && is_array($sched) && is_array($sched[0]) ){ |
| 589 | //Use sched as it is |
| 590 | } else { //Default to setting scans to run once a day at a randomly selected time. |
| 591 | $sched = array(); |
| 592 | $runAt = rand(0,23); |
| 593 | for($day = 0; $day <= 6; $day++){ |
| 594 | $sched[$day] = array(); |
| 595 | for($hour = 0; $hour <= 23; $hour++){ |
| 596 | if($hour == $runAt){ |
| 597 | $sched[$day][$hour] = 1; |
| 598 | } else { |
| 599 | $sched[$day][$hour] = 0; |
| 600 | } |
| 601 | } |
| 602 | } |
| 603 | } |
| 604 | for($scheduledDay = 0; $scheduledDay <= 6; $scheduledDay++){ |
| 605 | //0 is sunday |
| 606 | //6 is Saturday |
| 607 | for($scheduledHour = 0; $scheduledHour <= 23; $scheduledHour++){ |
| 608 | if($sched[$scheduledDay][$scheduledHour]){ |
| 609 | $wpTime = current_time('timestamp'); |
| 610 | $currentDayOfWeek = date('w', $wpTime); |
| 611 | $daysInFuture = $scheduledDay - $currentDayOfWeek; //It's monday and scheduledDay is Wed (3) then result is 2 days in future. It's Wed and sched day is monday, then result is 3 - 1 = -2 |
| 612 | if($daysInFuture < 0){ $daysInFuture += 7; } //Turns -2 into 5 days in future |
| 613 | $currentHour = date('G', $wpTime); |
| 614 | $secsOffset = ($scheduledHour - $currentHour) * 3600; //Offset from current hour, can be negative |
| 615 | $secondsInFuture = ($daysInFuture * 86400) + $secsOffset; //Can be negative, so we schedule those 1 week ahead |
| 616 | if($secondsInFuture < 1){ |
| 617 | $secondsInFuture += (86400 * 7); //Add a week |
| 618 | } |
| 619 | $futureTime = time() - (time() % 3600) + $secondsInFuture; //Modulo rounds down to top of the hour |
| 620 | wordfence::status(4, 'info', "Scheduled time for day $scheduledDay hour $scheduledHour is: " . date('l jS \of F Y h:i:s A', $futureTime)); |
| 621 | self::scheduleSingleScan($futureTime); |
| 622 | } |
| 623 | } |
| 624 | } |
| 625 | } |
| 626 | private static function scheduleSingleScan($futureTime){ |
| 627 | wp_schedule_single_event($futureTime, 'wordfence_start_scheduled_scan', array($futureTime)); |
| 628 | $schedArgs = wfConfig::get_ser('schedScanArgs', array()); |
| 629 | if(! is_array($schedArgs)){ //paranoia |
| 630 | $schedArgs = array(); |
| 631 | } |
| 632 | $schedArgs[] = $futureTime; |
| 633 | wfConfig::set_ser('schedScanArgs', $schedArgs); |
| 634 | } |
| 635 | private static function unscheduleAllScans(){ |
| 636 | wp_clear_scheduled_hook('wordfence_start_scheduled_scan'); //Unschedule legacy scans without args |
| 637 | |
| 638 | $schedArgs = wfConfig::get_ser('schedScanArgs', array()); |
| 639 | foreach($schedArgs as $futureTime){ |
| 640 | wp_clear_scheduled_hook('wordfence_start_scheduled_scan', array($futureTime)); |
| 641 | } |
| 642 | wfConfig::set_ser('schedScanArgs', array()); |
| 643 | } |
| 644 | public static function ajax_saveCountryBlocking_callback(){ |
| 645 | if(! wfConfig::get('isPaid')){ |
| 646 | return array('errorMsg' => "Sorry but this feature is only available for paid customers."); |
| 647 | } |
| 648 | wfConfig::set('cbl_action', $_POST['blockAction']); |
| 649 | wfConfig::set('cbl_countries', $_POST['codes']); |
| 650 | wfConfig::set('cbl_redirURL', $_POST['redirURL']); |
| 651 | wfConfig::set('cbl_loggedInBlocked', $_POST['loggedInBlocked']); |
| 652 | wfConfig::set('cbl_loginFormBlocked', $_POST['loginFormBlocked']); |
| 653 | return array('ok' => 1); |
| 654 | } |
| 655 | public static function ajax_sendActivityLog_callback(){ |
| 656 | $content = "SITE: " . site_url() . "\nPLUGIN VERSION: " . WORDFENCE_VERSION . "\nWP VERSION: " . wfUtils::getWPVersion() . "\nAPI KEY: " . wfConfig::get('apiKey') . "\nADMIN EMAIL: " . get_option('admin_email') . "\nLOG:\n\n"; |
| 657 | $wfdb = new wfDB(); |
| 658 | global $wpdb; |
| 659 | $p = $wpdb->base_prefix; |
| 660 | $q = $wfdb->query("select ctime, level, type, msg from $p"."wfStatus order by ctime desc limit 10000"); |
| 661 | while($r = mysql_fetch_assoc($q)){ |
| 662 | if($r['type'] == 'error'){ |
| 663 | $content .= "\n"; |
| 664 | } |
| 665 | $content .= date(DATE_RFC822, $r['ctime']) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . $r['msg'] . "\n"; |
| 666 | } |
| 667 | $content .= "\n\n"; |
| 668 | |
| 669 | ob_start(); |
| 670 | phpinfo(); |
| 671 | $phpinfo = ob_get_contents(); |
| 672 | ob_get_clean(); |
| 673 | |
| 674 | $content .= $phpinfo; |
| 675 | |
| 676 | wp_mail($_POST['email'], "Wordfence Activity Log", $content); |
| 677 | return array('ok' => 1); |
| 678 | } |
| 679 | public static function ajax_startTourAgain_callback(){ |
| 680 | wfConfig::set('tourClosed', 0); |
| 681 | return array('ok' => 1); |
| 682 | } |
| 683 | public static function ajax_tourClosed_callback(){ |
| 684 | wfConfig::set('tourClosed', 1); |
| 685 | return array('ok' => 1); |
| 686 | } |
| 687 | public static function ajax_saveConfig_callback(){ |
| 688 | $opts = wfConfig::parseOptions(); |
| 689 | $emails = array(); |
| 690 | foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['alertEmails'])) as $email){ |
| 691 | if(strlen($email) > 0){ |
| 692 | array_push($emails, $email); |
| 693 | } |
| 694 | } |
| 695 | if(sizeof($emails) > 0){ |
| 696 | $badEmails = array(); |
| 697 | foreach($emails as $email){ |
| 698 | if(! preg_match('/^[^@]+@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,8})$/i', $email)){ |
| 699 | array_push($badEmails, $email); |
| 700 | } |
| 701 | } |
| 702 | if(sizeof($badEmails) > 0){ |
| 703 | return array('errorMsg' => "The following emails are invalid: " . implode(', ', $badEmails)); |
| 704 | } |
| 705 | $opts['alertEmails'] = implode(',', $emails); |
| 706 | } else { |
| 707 | $opts['alertEmails'] = ''; |
| 708 | } |
| 709 | $whiteIPs = array(); |
| 710 | foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['whitelisted'])) as $whiteIP){ |
| 711 | if(strlen($whiteIP) > 0){ |
| 712 | array_push($whiteIPs, $whiteIP); |
| 713 | } |
| 714 | } |
| 715 | if(sizeof($whiteIPs) > 0){ |
| 716 | $badWhiteIPs = array(); |
| 717 | foreach($whiteIPs as $whiteIP){ |
| 718 | if(! preg_match('/^[\[\]\-\d]+\.[\[\]\-\d]+\.[\[\]\-\d]+\.[\[\]\-\d]+$/', $whiteIP)){ |
| 719 | array_push($badWhiteIPs, $whiteIP); |
| 720 | } |
| 721 | } |
| 722 | if(sizeof($badWhiteIPs) > 0){ |
| 723 | return array('errorMsg' => "Please make sure you separate your IP addresses with commas. The following whitelisted IP addresses are invalid: " . implode(', ', $badWhiteIPs)); |
| 724 | } |
| 725 | $opts['whitelisted'] = implode(',', $whiteIPs); |
| 726 | } else { |
| 727 | $opts['whitelisted'] = ''; |
| 728 | } |
| 729 | $validUsers = array(); |
| 730 | $invalidUsers = array(); |
| 731 | foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['liveTraf_ignoreUsers'])) as $val){ |
| 732 | if(strlen($val) > 0){ |
| 733 | if(get_user_by('login', $val)){ |
| 734 | array_push($validUsers, $val); |
| 735 | } else { |
| 736 | array_push($invalidUsers, $val); |
| 737 | } |
| 738 | } |
| 739 | } |
| 740 | $opts['apiKey'] = trim($opts['apiKey']); |
| 741 | if($opts['apiKey'] && (! preg_match('/^[a-fA-F0-9]+$/', $opts['apiKey'])) ){ //User entered something but it's garbage. |
| 742 | return array('errorMsg' => "You entered an API key but it is not in a valid format. It must consist only of characters A to F and 0 to 9."); |
| 743 | } |
| 744 | |
| 745 | if(sizeof($invalidUsers) > 0){ |
| 746 | return array('errorMsg' => "The following users you selected to ignore in live traffic reports are not valid on this system: " . implode(', ', $invalidUsers)); |
| 747 | } |
| 748 | if(sizeof($validUsers) > 0){ |
| 749 | $opts['liveTraf_ignoreUsers'] = implode(',', $validUsers); |
| 750 | } else { |
| 751 | $opts['liveTraf_ignoreUsers'] = ''; |
| 752 | } |
| 753 | |
| 754 | $validIPs = array(); |
| 755 | $invalidIPs = array(); |
| 756 | foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['liveTraf_ignoreIPs'])) as $val){ |
| 757 | if(strlen($val) > 0){ |
| 758 | if(preg_match('/^\d+\.\d+\.\d+\.\d+$/', $val)){ |
| 759 | array_push($validIPs, $val); |
| 760 | } else { |
| 761 | array_push($invalidIPs, $val); |
| 762 | } |
| 763 | } |
| 764 | } |
| 765 | if(sizeof($invalidIPs) > 0){ |
| 766 | return array('errorMsg' => "The following IPs you selected to ignore in live traffic reports are not valid: " . implode(', ', $invalidIPs)); |
| 767 | } |
| 768 | if(sizeof($validIPs) > 0){ |
| 769 | $opts['liveTraf_ignoreIPs'] = implode(',', $validIPs); |
| 770 | } |
| 771 | |
| 772 | if(preg_match('/[a-zA-Z0-9\d]+/', $opts['liveTraf_ignoreUA'])){ |
| 773 | $opts['liveTraf_ignoreUA'] = trim($opts['liveTraf_ignoreUA']); |
| 774 | } else { |
| 775 | $opts['liveTraf_ignoreUA'] = ''; |
| 776 | } |
| 777 | if(! $opts['other_WFNet']){ |
| 778 | $wfdb = new wfDB(); |
| 779 | global $wpdb; |
| 780 | $p = $wpdb->base_prefix; |
| 781 | $wfdb->query("delete from $p"."wfBlocks where wfsn=1 and permanent=0"); |
| 782 | } |
| 783 | foreach($opts as $key => $val){ |
| 784 | if($key != 'apiKey'){ //Don't save API key yet |
| 785 | wfConfig::set($key, $val); |
| 786 | } |
| 787 | } |
| 788 | |
| 789 | $reload = ''; |
| 790 | $paidKeyMsg = false; |
| 791 | |
| 792 | |
| 793 | if(! $opts['apiKey']){ //Empty API key (after trim above), then try to get one. |
| 794 | $api = new wfAPI('', wfUtils::getWPVersion()); |
| 795 | try { |
| 796 | $keyData = $api->call('get_anon_api_key'); |
| 797 | if($keyData['ok'] && $keyData['apiKey']){ |
| 798 | wfConfig::set('apiKey', $keyData['apiKey']); |
| 799 | wfConfig::set('isPaid', 0); |
| 800 | $reload = 'reload'; |
| 801 | } else { |
| 802 | throw new Exception("We could not understand the Wordfence server's response because it did not contain an 'ok' and 'apiKey' element."); |
| 803 | } |
| 804 | } catch(Exception $e){ |
| 805 | return array('errorMsg' => "Your options have been saved, but we encountered a problem. You left your API key blank, so we tried to get you a free API key from the Wordfence servers. However we encountered a problem fetching the free key: " . $e->getMessage()); |
| 806 | } |
| 807 | } else if($opts['apiKey'] != wfConfig::get('apiKey')){ |
| 808 | $api = new wfAPI($opts['apiKey'], wfUtils::getWPVersion()); |
| 809 | try { |
| 810 | $res = $api->call('check_api_key', array(), array()); |
| 811 | if($res['ok'] && isset($res['isPaid'])){ |
| 812 | wfConfig::set('apiKey', $opts['apiKey']); |
| 813 | $reload = 'reload'; |
| 814 | wfConfig::set('isPaid', $res['isPaid']); //res['isPaid'] is boolean coming back as JSON and turned back into PHP struct. Assuming JSON to PHP handles bools. |
| 815 | if($res['isPaid']){ |
| 816 | $paidKeyMsg = true; |
| 817 | } |
| 818 | } else { |
| 819 | throw new Exception("We could not understand the Wordfence API server reply when updating your API key."); |
| 820 | } |
| 821 | } catch (Exception $e){ |
| 822 | return array('errorMsg' => "Your options have been saved. However we noticed you changed your API key and we tried to verify it with the Wordfence servers and received an error: " . $e->getMessage()); |
| 823 | } |
| 824 | } |
| 825 | |
| 826 | |
| 827 | |
| 828 | //Clears next scan if scans are disabled. Schedules next scan if enabled. |
| 829 | if($err){ |
| 830 | return array('errorMsg' => $err); |
| 831 | } else { |
| 832 | return array('ok' => 1, 'reload' => $reload, 'paidKeyMsg' => $paidKeyMsg ); |
| 833 | } |
| 834 | } |
| 835 | public static function ajax_clearAllBlocked_callback(){ |
| 836 | $op = $_POST['op']; |
| 837 | $wfLog = self::getLog(); |
| 838 | if($op == 'blocked'){ |
| 839 | wordfence::status(1, 'info', "Ajax request received to unblock All IP's including permanent blocks."); |
| 840 | $wfLog->unblockAllIPs(); |
| 841 | } else if($op == 'locked'){ |
| 842 | $wfLog->unlockAllIPs(); |
| 843 | } |
| 844 | return array('ok' => 1); |
| 845 | } |
| 846 | public static function ajax_unlockOutIP_callback(){ |
| 847 | $IP = $_POST['IP']; |
| 848 | self::getLog()->unlockOutIP($IP); |
| 849 | return array('ok' => 1); |
| 850 | } |
| 851 | public static function ajax_unblockIP_callback(){ |
| 852 | $IP = $_POST['IP']; |
| 853 | self::getLog()->unblockIP($IP); |
| 854 | return array('ok' => 1); |
| 855 | } |
| 856 | public static function ajax_permBlockIP_callback(){ |
| 857 | $IP = $_POST['IP']; |
| 858 | self::getLog()->blockIP($IP, "Manual permanent block by admin", false, true); |
| 859 | return array('ok' => 1); |
| 860 | } |
| 861 | public static function ajax_loadStaticPanel_callback(){ |
| 862 | $mode = $_POST['mode']; |
| 863 | $wfLog = self::getLog(); |
| 864 | if($mode == 'topScanners' || $mode == 'topLeechers'){ |
| 865 | $results = $wfLog->getLeechers($mode); |
| 866 | } else if($mode == 'blockedIPs'){ |
| 867 | $results = $wfLog->getBlockedIPs(); |
| 868 | } else if($mode == 'lockedOutIPs'){ |
| 869 | $results = $wfLog->getLockedOutIPs(); |
| 870 | } else if($mode == 'throttledIPs'){ |
| 871 | $results = $wfLog->getThrottledIPs(); |
| 872 | } |
| 873 | return array('ok' => 1, 'results' => $results); |
| 874 | } |
| 875 | public static function ajax_blockIP_callback(){ |
| 876 | $IP = trim($_POST['IP']); |
| 877 | if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', $IP)){ |
| 878 | return array('err' => 1, 'errorMsg' => "Please enter a valid IP address to block."); |
| 879 | } |
| 880 | if($IP == wfUtils::getIP()){ |
| 881 | return array('err' => 1, 'errorMsg' => "You can't block your own IP address."); |
| 882 | } |
| 883 | if(self::getLog()->isWhitelisted($IP)){ |
| 884 | return array('err' => 1, 'errorMsg' => "The IP address $IP is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page."); |
| 885 | } |
| 886 | if(wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers'){ //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google |
| 887 | if(wfCrawl::verifyCrawlerPTR('/googlebot\.com$/i', $IP)){ |
| 888 | return array('err' => 1, 'errorMsg' => "The IP address you're trying to block belongs to Google. Your options are currently set to not block these crawlers. Change this in Wordfence options if you want to manually block Google."); |
| 889 | } |
| 890 | } |
| 891 | self::getLog()->blockIP($IP, $_POST['reason']); |
| 892 | return array('ok' => 1); |
| 893 | } |
| 894 | public static function ajax_reverseLookup_callback(){ |
| 895 | $ips = explode(',', $_POST['ips']); |
| 896 | $res = array(); |
| 897 | foreach($ips as $ip){ |
| 898 | $res[$ip] = wfUtils::reverseLookup($ip); |
| 899 | } |
| 900 | return array('ok' => 1, 'ips' => $res); |
| 901 | } |
| 902 | public static function ajax_deleteIssue_callback(){ |
| 903 | $wfIssues = new wfIssues(); |
| 904 | $issueID = $_POST['id']; |
| 905 | $wfIssues->deleteIssue($issueID); |
| 906 | return array('ok' => 1); |
| 907 | } |
| 908 | public static function ajax_updateAllIssues_callback(){ |
| 909 | $op = $_POST['op']; |
| 910 | $i = new wfIssues(); |
| 911 | if($op == 'deleteIgnored'){ |
| 912 | $i->deleteIgnored(); |
| 913 | } else if($op == 'deleteNew'){ |
| 914 | $i->deleteNew(); |
| 915 | } else if($op == 'ignoreAllNew'){ |
| 916 | $i->ignoreAllNew(); |
| 917 | } else { |
| 918 | return array('errorMsg' => "An invalid operation was called."); |
| 919 | } |
| 920 | return array('ok' => 1); |
| 921 | } |
| 922 | public static function ajax_updateIssueStatus_callback(){ |
| 923 | $wfIssues = new wfIssues(); |
| 924 | $status = $_POST['status']; |
| 925 | $issueID = $_POST['id']; |
| 926 | if(! preg_match('/^(?:new|delete|ignoreP|ignoreC)$/', $status)){ |
| 927 | return array('errorMsg' => "An invalid status was specified when trying to update that issue."); |
| 928 | } |
| 929 | $wfIssues->updateIssue($issueID, $status); |
| 930 | return array('ok' => 1); |
| 931 | } |
| 932 | public static function ajax_killScan_callback(){ |
| 933 | wordfence::status(1, 'info', "Scan kill request received."); |
| 934 | wordfence::status(10, 'info', "SUM_KILLED:A request was received to kill the previous scan."); |
| 935 | wfUtils::clearScanLock(); //Clear the lock now because there may not be a scan running to pick up the kill request and clear the lock |
| 936 | wfScanEngine::requestKill(); |
| 937 | return array( |
| 938 | 'ok' => 1, |
| 939 | ); |
| 940 | } |
| 941 | public static function ajax_loadIssues_callback(){ |
| 942 | $i = new wfIssues(); |
| 943 | $iss = $i->getIssues(); |
| 944 | return array( |
| 945 | 'issuesLists' => $iss, |
| 946 | 'summary' => $i->getSummaryItems(), |
| 947 | 'lastScanCompleted' => wfConfig::get('lastScanCompleted') |
| 948 | ); |
| 949 | } |
| 950 | public static function ajax_ticker_callback(){ |
| 951 | $wfdb = new wfDB(); |
| 952 | global $wpdb; |
| 953 | $p = $wpdb->base_prefix; |
| 954 | |
| 955 | $serverTime = $wfdb->querySingle("select unix_timestamp()"); |
| 956 | $issues = new wfIssues(); |
| 957 | $jsonData = array( |
| 958 | 'serverTime' => $serverTime, |
| 959 | 'msg' => $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1") |
| 960 | ); |
| 961 | $events = array(); |
| 962 | $alsoGet = $_POST['alsoGet']; |
| 963 | if(preg_match('/^logList_(404|hit|human|ruser|crawler|gCrawler|loginLogout)$/', $alsoGet, $m)){ |
| 964 | $type = $m[1]; |
| 965 | $newestEventTime = $_POST['otherParams']; |
| 966 | $listType = 'hits'; |
| 967 | if($type == 'loginLogout'){ |
| 968 | $listType = 'logins'; |
| 969 | } |
| 970 | $events = self::getLog()->getHits($listType, $type, $newestEventTime); |
| 971 | } |
| 972 | $jsonData['events'] = $events; |
| 973 | $jsonData['alsoGet'] = $alsoGet; //send it back so we don't load data if panel has changed |
| 974 | return $jsonData; |
| 975 | } |
| 976 | public static function ajax_activityLogUpdate_callback(){ |
| 977 | $issues = new wfIssues(); |
| 978 | return array( |
| 979 | 'ok' => 1, |
| 980 | 'items' => self::getLog()->getStatusEvents($_POST['lastctime']), |
| 981 | 'currentScanID' => $issues->getScanTime() |
| 982 | ); |
| 983 | } |
| 984 | public static function ajax_deleteFile_callback(){ |
| 985 | $issueID = $_POST['issueID']; |
| 986 | $wfIssues = new wfIssues(); |
| 987 | $issue = $wfIssues->getIssueByID($issueID); |
| 988 | if(! $issue){ |
| 989 | return array('errorMsg' => "Could not delete file because we could not find that issue."); |
| 990 | } |
| 991 | if(! $issue['data']['file']){ |
| 992 | return array('errorMsg' => "Could not delete file because that issue does not appear to be a file related issue."); |
| 993 | } |
| 994 | $file = $issue['data']['file']; |
| 995 | $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file); |
| 996 | $localFile = realpath($localFile); |
| 997 | if(strpos($localFile, ABSPATH) !== 0){ |
| 998 | return array('errorMsg' => "An invalid file was requested for deletion."); |
| 999 | } |
| 1000 | if(@unlink($localFile)){ |
| 1001 | $wfIssues->updateIssue($issueID, 'delete'); |
| 1002 | return array( |
| 1003 | 'ok' => 1, |
| 1004 | 'localFile' => $localFile, |
| 1005 | 'file' => $file |
| 1006 | ); |
| 1007 | } else { |
| 1008 | $err = error_get_last(); |
| 1009 | return array('errorMsg' => "Could not delete file $file. The error was: " . $err['message']); |
| 1010 | } |
| 1011 | } |
| 1012 | public static function ajax_restoreFile_callback(){ |
| 1013 | $issueID = $_POST['issueID']; |
| 1014 | $wfIssues = new wfIssues(); |
| 1015 | $issue = $wfIssues->getIssueByID($issueID); |
| 1016 | if(! $issue){ |
| 1017 | return array('cerrorMsg' => "We could not find that issue in our database."); |
| 1018 | } |
| 1019 | $dat = $issue['data']; |
| 1020 | $result = self::getWPFileContent($dat['file'], $dat['cType'], $dat['cName'], $dat['cVersion']); |
| 1021 | $file = $dat['file']; |
| 1022 | if($result['cerrorMsg']){ |
| 1023 | return $result; |
| 1024 | } else if(! $result['fileContent']){ |
| 1025 | return array('cerrorMsg' => "We could not get the original file to do a repair."); |
| 1026 | } |
| 1027 | |
| 1028 | if(preg_match('/\.\./', $file)){ |
| 1029 | return array('cerrorMsg' => "An invalid file was specified for repair."); |
| 1030 | } |
| 1031 | $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file); |
| 1032 | $fh = fopen($localFile, 'w'); |
| 1033 | if(! $fh){ |
| 1034 | $err = error_get_last(); |
| 1035 | if(preg_match('/Permission denied/i', $err['message'])){ |
| 1036 | $errMsg = "You don't have permission to repair that file. You need to either fix the file manually using FTP or change the file permissions and ownership so that your web server has write access to repair the file."; |
| 1037 | } else { |
| 1038 | $errMsg = "We could not write to that file. The error was: " . $err['message']; |
| 1039 | } |
| 1040 | return array('cerrorMsg' => $errMsg); |
| 1041 | } |
| 1042 | flock($fh, LOCK_EX); |
| 1043 | $bytes = fwrite($fh, $result['fileContent']); |
| 1044 | flock($fh, LOCK_UN); |
| 1045 | fclose($fh); |
| 1046 | if($bytes < 1){ |
| 1047 | return array('cerrorMsg' => "We could not write to that file. ($bytes bytes written) You may not have permission to modify files on your WordPress server."); |
| 1048 | } |
| 1049 | $wfIssues->updateIssue($issueID, 'delete'); |
| 1050 | return array( |
| 1051 | 'ok' => 1, |
| 1052 | 'file' => $localFile |
| 1053 | ); |
| 1054 | } |
| 1055 | public static function ajax_scan_callback(){ |
| 1056 | self::status(4, 'info', "Ajax request received to start scan."); |
| 1057 | $err = wfScanEngine::startScan(); |
| 1058 | if($err){ |
| 1059 | return array('errorMsg' => $err); |
| 1060 | } else { |
| 1061 | return array("ok" => 1); |
| 1062 | } |
| 1063 | } |
| 1064 | public static function startScan(){ |
| 1065 | wfScanEngine::startScan(); |
| 1066 | } |
| 1067 | public static function templateRedir(){ |
| 1068 | $wfFunc = get_query_var('_wfsf'); |
| 1069 | $wfLog = self::getLog(); |
| 1070 | if($wfLog->logHitOK()){ |
| 1071 | if( (! empty($wfFunc)) && is_404() ){ |
| 1072 | $wfLog->logLeechAndBlock('404'); |
| 1073 | } else { |
| 1074 | $wfLog->logLeechAndBlock('hit'); |
| 1075 | } |
| 1076 | if(wfConfig::get('liveTrafficEnabled')){ |
| 1077 | self::$hitID = $wfLog->logHit(); |
| 1078 | add_action('wp_head', 'wordfence::wp_head'); |
| 1079 | } |
| 1080 | } |
| 1081 | |
| 1082 | if(! ($wfFunc == 'diff' || $wfFunc == 'view' || $wfFunc == 'sysinfo' || $wfFunc == 'unknownFiles' || $wfFunc == 'IPTraf' || $wfFunc == 'viewActivityLog' || $wfFunc == 'testmem' || $wfFunc == 'testtime')){ |
| 1083 | return; |
| 1084 | } |
| 1085 | if(! wfUtils::isAdmin()){ |
| 1086 | return; |
| 1087 | } |
| 1088 | |
| 1089 | $nonce = $_GET['nonce']; |
| 1090 | if(! wp_verify_nonce($nonce, 'wp-ajax')){ |
| 1091 | echo "Bad security token. It may have been more than 12 hours since you reloaded the page you came from. Try reloading the page you came from. If that doesn't work, please sign out and sign-in again."; |
| 1092 | exit(0); |
| 1093 | } |
| 1094 | if($wfFunc == 'diff'){ |
| 1095 | self::wfFunc_diff(); |
| 1096 | } else if($wfFunc == 'view'){ |
| 1097 | self::wfFunc_view(); |
| 1098 | } else if($wfFunc == 'sysinfo'){ |
| 1099 | require('sysinfo.php'); |
| 1100 | } else if($wfFunc == 'unknownFiles'){ |
| 1101 | require('unknownFiles.php'); |
| 1102 | } else if($wfFunc == 'IPTraf'){ |
| 1103 | self::wfFunc_IPTraf(); |
| 1104 | } else if($wfFunc == 'viewActivityLog'){ |
| 1105 | self::wfFunc_viewActivityLog(); |
| 1106 | } else if($wfFunc == 'testmem'){ |
| 1107 | self::wfFunc_testmem(); |
| 1108 | } else if($wfFunc == 'testtime'){ |
| 1109 | self::wfFunc_testtime(); |
| 1110 | } |
| 1111 | exit(0); |
| 1112 | } |
| 1113 | public static function memtest_error_handler($errno, $errstr, $errfile, $errline){ |
| 1114 | echo "Error received: $errstr\n"; |
| 1115 | } |
| 1116 | private static function wfFunc_testtime(){ |
| 1117 | header('Content-Type: text/plain'); |
| 1118 | ini_set('max_execution_time', 1800); //30 mins |
| 1119 | @error_reporting(E_ALL); |
| 1120 | @ini_set('display_errors','On'); |
| 1121 | set_error_handler('wordfence::memtest_error_handler', E_ALL); |
| 1122 | |
| 1123 | echo "Wordfence process duration benchmarking utility version " . WORDFENCE_VERSION . ".\n"; |
| 1124 | echo "This utility tests how long your WordPress host allows a process to run.\n\n--Starting test--\n"; |
| 1125 | echo "Starting timed test. This will take at least three minutes. Seconds elapsed are printed below.\nAn error after this line is not unusual. Read it and the elapsed seconds to determine max process running time on your host.\n"; |
| 1126 | for($i = 1; $i <= 180; $i++){ |
| 1127 | echo "\n$i:"; |
| 1128 | for($j = 0; $j < 1000; $j++){ |
| 1129 | echo '.'; |
| 1130 | } |
| 1131 | flush(); |
| 1132 | sleep(1); |
| 1133 | } |
| 1134 | echo "\n--Test complete.--\n\nCongratulations, your web host allows your PHP processes to run at least 3 minutes.\n"; |
| 1135 | exit(); |
| 1136 | } |
| 1137 | private static function wfFunc_testmem(){ |
| 1138 | header('Content-Type: text/plain'); |
| 1139 | @error_reporting(E_ALL); |
| 1140 | @ini_set('display_errors','On'); |
| 1141 | set_error_handler('wordfence::memtest_error_handler', E_ALL); |
| 1142 | |
| 1143 | echo "Wordfence Memory benchmarking utility version " . WORDFENCE_VERSION . ".\n"; |
| 1144 | echo "This utility tests if your WordPress host respects the maximum memory configured\nin their php.ini file, or if they are using other methods to limit your access to memory.\n\n--Starting test--\n"; |
| 1145 | echo "Current maximum memory configured in php.ini: " . ini_get('memory_limit') . "\n"; |
| 1146 | echo "Current memory usage: " . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . "M\n"; |
| 1147 | echo "Setting max memory to 90M.\n"; |
| 1148 | ini_set('memory_limit', '90M'); |
| 1149 | echo "Starting memory benchmark. Seeing an error after this line is not unusual. Read the error carefully\nto determine how much memory your host allows. We have requested 90 megabytes.\n"; |
| 1150 | if(memory_get_usage(true) < 1){ |
| 1151 | echo "Exiting test because memory_get_usage() returned a negative number\n"; |
| 1152 | } |
| 1153 | if(memory_get_usage(true) > (1024 * 1024 * 1024)){ |
| 1154 | echo "Exiting because current memory usage is greater than a gigabyte.\n"; |
| 1155 | } |
| 1156 | $arr = array(); |
| 1157 | //256 bytes |
| 1158 | $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111222222222222222222233333333333333334444444444444444444444444555555555555666666666666666666"; |
| 1159 | $finalUsage = '0'; |
| 1160 | while(true){ |
| 1161 | if(memory_get_usage(true) > 80 * 1024 * 1024){ |
| 1162 | $finalUsage = sprintf('%.2f', (memory_get_usage(true) / 1024 / 1024)); |
| 1163 | echo "Completing test after benchmarking up to " . $finalUsage . " megabytes.\n"; |
| 1164 | break; |
| 1165 | } |
| 1166 | for($i = 0; $i < 1024; $i++){ //Roughly 1 megabyte if it's 256K and actual array size is 4x data size |
| 1167 | $arr[] = $chars; |
| 1168 | } |
| 1169 | } |
| 1170 | echo "--Test complete.--\n\nCongratulations, your web host allows you to use at least $finalUsage megabytes of memory for each PHP process hosting your WordPress site.\n"; |
| 1171 | exit(); |
| 1172 | } |
| 1173 | public static function wp_head(){ |
| 1174 | echo '<script type="text/javascript">var src="' . wfUtils::getBaseURL() . 'visitor.php?hid=' . wfUtils::encrypt(self::$hitID) . '"; if(window.location.protocol == "https:"){ src = src.replace("http:", "https:"); } var wfHTImg = new Image(); wfHTImg.src=src;</script>'; |
| 1175 | } |
| 1176 | public static function shutdownAction(){ |
| 1177 | } |
| 1178 | public static function wfFunc_viewActivityLog(){ |
| 1179 | require('viewFullActivityLog.php'); |
| 1180 | exit(0); |
| 1181 | } |
| 1182 | public static function wfFunc_IPTraf(){ |
| 1183 | $IP = $_GET['IP']; |
| 1184 | $reverseLookup = wfUtils::reverseLookup($IP); |
| 1185 | if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', $IP)){ |
| 1186 | echo "An invalid IP address was specified."; |
| 1187 | exit(0); |
| 1188 | } |
| 1189 | $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion()); |
| 1190 | $results = array_merge( |
| 1191 | $wfLog->getHits('hits', 'hit', 0, 10000, $IP), |
| 1192 | $wfLog->getHits('hits', '404', 0, 10000, $IP) |
| 1193 | ); |
| 1194 | usort($results, 'wordfence::iptrafsort'); |
| 1195 | for($i = 0; $i < sizeof($results); $i++){ |
| 1196 | if(array_key_exists($i + 1, $results)){ |
| 1197 | $results[$i]['timeSinceLastHit'] = sprintf('%.4f', $results[$i]['ctime'] - $results[$i + 1]['ctime']); |
| 1198 | } else { |
| 1199 | $results[$i]['timeSinceLastHit'] = ''; |
| 1200 | } |
| 1201 | } |
| 1202 | require('IPTraf.php'); |
| 1203 | exit(0); |
| 1204 | } |
| 1205 | public static function iptrafsort($b, $a){ |
| 1206 | if($a['ctime'] == $b['ctime']){ return 0; } |
| 1207 | return ($a['ctime'] < $b['ctime']) ? -1 : 1; |
| 1208 | } |
| 1209 | public static function wfFunc_view(){ |
| 1210 | $localFile = ABSPATH . '/' . preg_replace('/^(?:\.\.|[\/]+)/', '', $_GET['file']); |
| 1211 | if(strpos($localFile, '..') !== false){ |
| 1212 | echo "Invalid file requested. (Relative paths not allowed)"; |
| 1213 | exit(); |
| 1214 | } |
| 1215 | $lang = false; |
| 1216 | $cont = @file_get_contents($localFile); |
| 1217 | $isEmpty = false; |
| 1218 | if(! $cont){ |
| 1219 | if(file_exists($localFile) && filesize($localFile) === 0){ //There's a remote possibility that very large files on 32 bit systems will return 0 here, but it's about 1 in 2 billion |
| 1220 | $isEmpty = true; |
| 1221 | } else { |
| 1222 | $err = error_get_last(); |
| 1223 | echo "We could not open the requested file for reading. The error was: " . $err['message']; |
| 1224 | exit(0); |
| 1225 | } |
| 1226 | } |
| 1227 | $fileMTime = @filemtime($localFile); |
| 1228 | $fileMTime = date('l jS \of F Y h:i:s A', $fileMTime); |
| 1229 | if(wfUtils::fileOver2Gigs($localFile)){ |
| 1230 | $fileSize = "Greater than 2 Gigs"; |
| 1231 | } else { |
| 1232 | $fileSize = @filesize($localFile); //Checked if over 2 gigs above |
| 1233 | $fileSize = number_format($fileSize, 0, '', ',') . ' bytes'; |
| 1234 | } |
| 1235 | |
| 1236 | require 'wfViewResult.php'; |
| 1237 | exit(0); |
| 1238 | } |
| 1239 | public static function wfFunc_diff(){ |
| 1240 | $result = self::getWPFileContent($_GET['file'], $_GET['cType'], $_GET['cName'], $_GET['cVersion']); |
| 1241 | if($result['errorMsg']){ |
| 1242 | echo $result['errorMsg']; |
| 1243 | exit(0); |
| 1244 | } else if(! $result['fileContent']){ |
| 1245 | echo "We could not get the contents of the original file to do a comparison."; |
| 1246 | exit(0); |
| 1247 | } |
| 1248 | |
| 1249 | $localFile = realpath(ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $_GET['file'])); |
| 1250 | $localContents = file_get_contents($localFile); |
| 1251 | if($localContents == $result['fileContent']){ |
| 1252 | $diffResult = ''; |
| 1253 | } else { |
| 1254 | $diff = new Diff( |
| 1255 | //Treat DOS and Unix files the same |
| 1256 | preg_split("/(?:\r\n|\n)/", $result['fileContent']), |
| 1257 | preg_split("/(?:\r\n|\n)/", $localContents), |
| 1258 | array() |
| 1259 | ); |
| 1260 | $renderer = new Diff_Renderer_Html_SideBySide; |
| 1261 | $diffResult = $diff->Render($renderer); |
| 1262 | } |
| 1263 | require 'diffResult.php'; |
| 1264 | exit(0); |
| 1265 | } |
| 1266 | public static function initAction(){ |
| 1267 | global $wp; |
| 1268 | if (!is_object($wp)) return; //Suggested fix for compatability with "Portable phpmyadmin" |
| 1269 | |
| 1270 | $wp->add_query_var('_wfsf'); |
| 1271 | //add_rewrite_rule('wfStaticFunc/([a-zA-Z0-9]+)/?$', 'index.php?wfStaticFunc=' . $matches[1], 'top'); |
| 1272 | $cookieName = 'wfvt_' . crc32(site_url()); |
| 1273 | $c = isset($_COOKIES[$cookieName]) ? isset($_COOKIES[$cookieName]) : false; |
| 1274 | if($c){ |
| 1275 | self::$newVisit = false; |
| 1276 | } else { |
| 1277 | self::$newVisit = true; |
| 1278 | } |
| 1279 | @setcookie($cookieName, uniqid(), time() + 1800, '/'); |
| 1280 | } |
| 1281 | public static function admin_init(){ |
| 1282 | if(! wfUtils::isAdmin()){ return; } |
| 1283 | foreach(array('activate', 'scan', 'sendActivityLog', 'restoreFile', 'deleteFile', 'removeExclusion', 'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues', 'reverseLookup', 'unlockOutIP', 'unblockIP', 'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed', 'startTourAgain') as $func){ |
| 1284 | add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver'); |
| 1285 | } |
| 1286 | |
| 1287 | if(preg_match('/^Wordfence/', @$_GET['page'])){ |
| 1288 | wp_enqueue_style('wp-pointer'); |
| 1289 | wp_enqueue_script('wp-pointer'); |
| 1290 | wp_enqueue_style('wordfence-main-style', wfUtils::getBaseURL() . 'css/main.css', '', WORDFENCE_VERSION); |
| 1291 | wp_enqueue_style('wordfence-colorbox-style', wfUtils::getBaseURL() . 'css/colorbox.css', '', WORDFENCE_VERSION); |
| 1292 | wp_enqueue_style('wordfence-dttable-style', wfUtils::getBaseURL() . 'css/dt_table.css', '', WORDFENCE_VERSION); |
| 1293 | |
| 1294 | wp_enqueue_script('json2'); |
| 1295 | wp_enqueue_script('jquery.tmpl', wfUtils::getBaseURL() . 'js/jquery.tmpl.min.js', array('jquery'), WORDFENCE_VERSION); |
| 1296 | wp_enqueue_script('jquery.colorbox', wfUtils::getBaseURL() . 'js/jquery.colorbox-min.js', array('jquery'), WORDFENCE_VERSION); |
| 1297 | wp_enqueue_script('jquery.dataTables', wfUtils::getBaseURL() . 'js/jquery.dataTables.min.js', array('jquery'), WORDFENCE_VERSION); |
| 1298 | //wp_enqueue_script('jquery.tools', wfUtils::getBaseURL() . 'js/jquery.tools.min.js', array('jquery')); |
| 1299 | wp_enqueue_script('wordfenceAdminjs', wfUtils::getBaseURL() . 'js/admin.js', array('jquery'), WORDFENCE_VERSION); |
| 1300 | self::setupAdminVars(); |
| 1301 | } else { |
| 1302 | wp_enqueue_style('wp-pointer'); |
| 1303 | wp_enqueue_script('wp-pointer'); |
| 1304 | wp_enqueue_script('wordfenceAdminjs', wfUtils::getBaseURL() . 'js/tourTip.js', array('jquery'), WORDFENCE_VERSION); |
| 1305 | self::setupAdminVars(); |
| 1306 | } |
| 1307 | |
| 1308 | } |
| 1309 | private static function setupAdminVars(){ |
| 1310 | wp_localize_script('wordfenceAdminjs', 'WordfenceAdminVars', array( |
| 1311 | 'ajaxURL' => admin_url('admin-ajax.php'), |
| 1312 | 'firstNonce' => wp_create_nonce('wp-ajax'), |
| 1313 | 'siteBaseURL' => wfUtils::getSiteBaseURL(), |
| 1314 | 'debugOn' => wfConfig::get('debugOn', 0), |
| 1315 | 'tourClosed' => wfConfig::get('tourClosed', 0) |
| 1316 | )); |
| 1317 | } |
| 1318 | public static function configure_warning(){ |
| 1319 | if(! preg_match('/WordfenceSecOpt/', $_SERVER['REQUEST_URI'])){ |
| 1320 | $numRun = wfConfig::get('alertEmailMsgCount', 0); |
| 1321 | if($numRun <= 3){ |
| 1322 | echo '<div id="wordfenceConfigWarning" class="updated fade"><p><strong>Please set up an email address to receive Wordfence security alerts</strong> on the <a href="admin.php?page=WordfenceSecOpt">Wordfence Options Page</a>. This message will appear ' . (3 - $numRun) . ' more times.</p></div>'; |
| 1323 | wfConfig::set('alertEmailMsgCount', ++$numRun); |
| 1324 | } |
| 1325 | |
| 1326 | } |
| 1327 | } |
| 1328 | public static function noKeyError(){ |
| 1329 | echo '<div id="wordfenceConfigWarning" class="fade error"><p><strong>Wordfence could not get an API key from the Wordfence scanning servers when it activated.</strong> You can try to fix this by going to the Wordfence "options" page and hitting "Save Changes". This will cause Wordfence to retry fetching an API key for you. If you keep seeing this error it usually means your WordPress server can\'t connect to our scanning servers. You can try asking your WordPress host to allow your WordPress server to connect to noc1.wordfence.com.</p></div>'; |
| 1330 | } |
| 1331 | public static function admin_menus(){ |
| 1332 | if(! wfUtils::isAdmin()){ return; } |
| 1333 | /* Removed this because we now have the tour. |
| 1334 | if(! wfConfig::get('alertEmails')){ |
| 1335 | if(wfUtils::isAdminPageMU()){ |
| 1336 | add_action('network_admin_notices', 'wordfence::configure_warning'); |
| 1337 | } else { |
| 1338 | add_action('admin_notices', 'wordfence::configure_warning'); |
| 1339 | } |
| 1340 | } |
| 1341 | */ |
| 1342 | if(! wfConfig::get('apiKey')){ |
| 1343 | if(wfUtils::isAdminPageMU()){ |
| 1344 | add_action('network_admin_notices', 'wordfence::noKeyError'); |
| 1345 | } else { |
| 1346 | add_action('admin_notices', 'wordfence::noKeyError'); |
| 1347 | } |
| 1348 | } |
| 1349 | add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan'); |
| 1350 | add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', wfUtils::getBaseURL() . 'images/wordfence-logo-16x16.png'); |
| 1351 | if(wfConfig::get('liveTrafficEnabled')){ |
| 1352 | add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity'); |
| 1353 | } |
| 1354 | add_submenu_page('Wordfence', 'Blocked IPs', 'Blocked IPs', 'activate_plugins', 'WordfenceBlockedIPs', 'wordfence::menu_blockedIPs'); |
| 1355 | add_submenu_page("Wordfence", "Country Blocking", "Country Blocking", "activate_plugins", "WordfenceCountryBlocking", 'wordfence::menu_countryBlocking'); |
| 1356 | add_submenu_page("Wordfence", "Scan Schedule", "Scan Schedule", "activate_plugins", "WordfenceScanSchedule", 'wordfence::menu_scanSchedule'); |
| 1357 | add_submenu_page("Wordfence", "Options", "Options", "activate_plugins", "WordfenceSecOpt", 'wordfence::menu_options'); |
| 1358 | } |
| 1359 | public static function menu_options(){ |
| 1360 | require 'menu_options.php'; |
| 1361 | } |
| 1362 | public static function menu_blockedIPs(){ |
| 1363 | require 'menu_blockedIPs.php'; |
| 1364 | } |
| 1365 | public static function menu_scanSchedule(){ |
| 1366 | require 'menu_scanSchedule.php'; |
| 1367 | } |
| 1368 | public static function menu_countryBlocking(){ |
| 1369 | require 'menu_countryBlocking.php'; |
| 1370 | } |
| 1371 | public static function menu_activity(){ |
| 1372 | require 'menu_activity.php'; |
| 1373 | } |
| 1374 | public static function menu_scan(){ |
| 1375 | require 'menu_scan.php'; |
| 1376 | } |
| 1377 | public static function status($level /* 1 has highest visibility */, $type /* info|error */, $msg){ |
| 1378 | if($level > 3 && $level < 10 && (! self::isDebugOn())){ //level 10 and higher is for summary messages |
| 1379 | return false; |
| 1380 | } |
| 1381 | if($type != 'info' && $type != 'error'){ error_log("Invalid status type: $type"); return; } |
| 1382 | if(self::$printStatus){ |
| 1383 | echo "STATUS: $level : $type : $msg\n"; |
| 1384 | } else { |
| 1385 | self::getLog()->addStatus($level, $type, $msg); |
| 1386 | } |
| 1387 | } |
| 1388 | public static function profileUpdateAction($userID, $newDat){ |
| 1389 | if(wfConfig::get('other_pwStrengthOnUpdate')){ |
| 1390 | $oldDat = get_userdata($userID); |
| 1391 | if($newDat->user_pass != $oldDat->user_pass){ |
| 1392 | $wf = new wfScanEngine(); |
| 1393 | $wf->scanUserPassword($userID); |
| 1394 | $wf->emailNewIssues(); |
| 1395 | } |
| 1396 | } |
| 1397 | } |
| 1398 | public static function genFilter($gen, $type){ |
| 1399 | if(wfConfig::get('other_hideWPVersion')){ |
| 1400 | return ''; |
| 1401 | } else { |
| 1402 | return $gen; |
| 1403 | } |
| 1404 | } |
| 1405 | public static function preCommentApprovedFilter($approved, $cData){ |
| 1406 | if( $approved == 1 && (! is_user_logged_in()) && wfConfig::get('other_noAnonMemberComments') ){ |
| 1407 | $user = get_user_by('email', trim($cData['comment_author_email'])); |
| 1408 | if($user){ |
| 1409 | return 0; //hold for moderation if the user is not signed in but used a members email |
| 1410 | } |
| 1411 | } |
| 1412 | |
| 1413 | if(($approved == 1 || $approved == 0) && wfConfig::get('other_scanComments')){ |
| 1414 | $wf = new wfScanEngine(); |
| 1415 | if($wf->isBadComment($cData['comment_author'], $cData['comment_author_email'], $cData['comment_author_url'], $cData['comment_author_IP'], $cData['comment_content'])){ |
| 1416 | return 'spam'; |
| 1417 | } |
| 1418 | } |
| 1419 | return $approved; |
| 1420 | } |
| 1421 | public static function getMyHomeURL(){ |
| 1422 | return admin_url('admin.php?page=Wordfence', 'http'); |
| 1423 | } |
| 1424 | public static function getMyOptionsURL(){ |
| 1425 | return admin_url('admin.php?page=WordfenceSecOpt', 'http'); |
| 1426 | } |
| 1427 | |
| 1428 | public static function alert($subject, $alertMsg, $IP){ |
| 1429 | $IPMsg = ""; |
| 1430 | if($IP){ |
| 1431 | $IPMsg = "User IP: $IP\n"; |
| 1432 | $reverse = wfUtils::reverseLookup($IP); |
| 1433 | if($reverse){ |
| 1434 | $IPMsg .= "User hostname: " . $reverse . "\n"; |
| 1435 | } |
| 1436 | $userLoc = wfUtils::getIPGeo($IP); |
| 1437 | if($userLoc){ |
| 1438 | $IPMsg .= "User location: "; |
| 1439 | if($userLoc['city']){ |
| 1440 | $IPMsg .= $userLoc['city'] . ', '; |
| 1441 | } |
| 1442 | $IPMsg .= $userLoc['countryName'] . "\n"; |
| 1443 | } |
| 1444 | } |
| 1445 | $content = wfUtils::tmpl('email_genericAlert.php', array( |
| 1446 | 'subject' => $subject, |
| 1447 | 'blogName' => get_bloginfo('name', 'raw'), |
| 1448 | 'alertMsg' => $alertMsg, |
| 1449 | 'IPMsg' => $IPMsg, |
| 1450 | 'date' => date('l jS \of F Y \a\t h:i:s A'), |
| 1451 | 'myHomeURL' => self::getMyHomeURL(), |
| 1452 | 'myOptionsURL' => self::getMyOptionsURL() |
| 1453 | )); |
| 1454 | $emails = wfConfig::getAlertEmails(); |
| 1455 | if(sizeof($emails) < 1){ return; } |
| 1456 | $shortSiteURL = preg_replace('/^https?:\/\//i', '', site_url()); |
| 1457 | $subject = "[Wordfence Alert] $shortSiteURL " . $subject; |
| 1458 | wp_mail(implode(',', $emails), $subject, $content); |
| 1459 | } |
| 1460 | private static function getLog(){ |
| 1461 | if(! self::$wfLog){ |
| 1462 | $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion()); |
| 1463 | self::$wfLog = $wfLog; |
| 1464 | } |
| 1465 | return self::$wfLog; |
| 1466 | } |
| 1467 | public static function statusPrep(){ |
| 1468 | wfConfig::set_ser('wfStatusStartMsgs', array()); |
| 1469 | wordfence::status(10, 'info', "SUM_PREP:Preparing a new scan."); |
| 1470 | } |
| 1471 | //In the following functions statusStartMsgs is serialized into the DB so it persists between forks |
| 1472 | public static function statusStart($msg){ |
| 1473 | $statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array()); |
| 1474 | $statusStartMsgs[] = $msg; |
| 1475 | wfConfig::set_ser('wfStatusStartMsgs', $statusStartMsgs); |
| 1476 | self::status(10, 'info', 'SUM_START:' . $msg); |
| 1477 | return sizeof($statusStartMsgs) - 1; |
| 1478 | } |
| 1479 | public static function statusEnd($idx, $haveIssues){ |
| 1480 | $statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array()); |
| 1481 | if($haveIssues){ |
| 1482 | self::status(10, 'info', 'SUM_ENDBAD:' . $statusStartMsgs[$idx]); |
| 1483 | } else { |
| 1484 | self::status(10, 'info', 'SUM_ENDOK:' . $statusStartMsgs[$idx]); |
| 1485 | } |
| 1486 | $statusStartMsgs[$idx] = ''; |
| 1487 | wfConfig::set_ser('wfStatusStartMsgs', $statusStartMsgs); |
| 1488 | } |
| 1489 | public static function statusEndErr(){ |
| 1490 | $statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array()); |
| 1491 | for($i = 0; $i < sizeof($statusStartMsgs); $i++){ |
| 1492 | if(empty($statusStartMsgs[$i]) === false){ |
| 1493 | self::status(10, 'info', 'SUM_ENDERR:' . $statusStartMsgs[$i]); |
| 1494 | $statusStartMsgs[$i] = ''; |
| 1495 | } |
| 1496 | } |
| 1497 | } |
| 1498 | public static function statusDisabled($msg){ |
| 1499 | self::status(10, 'info', "SUM_DISABLED:" . $msg); |
| 1500 | } |
| 1501 | public static function statusPaidOnly($msg){ |
| 1502 | self::status(10, 'info', "SUM_PAIDONLY:" . $msg); |
| 1503 | } |
| 1504 | public static function wfSchemaExists(){ |
| 1505 | $db = new wfDB(); |
| 1506 | global $wpdb; $prefix = $wpdb->base_prefix; |
| 1507 | $exists = $db->querySingle("show tables like '$prefix"."wfConfig'"); |
| 1508 | return $exists ? true : false; |
| 1509 | } |
| 1510 | public static function isDebugOn(){ |
| 1511 | if(is_null(self::$debugOn)){ |
| 1512 | if(wfConfig::get('debugOn')){ |
| 1513 | self::$debugOn = true; |
| 1514 | } else { |
| 1515 | self::$debugOn = false; |
| 1516 | } |
| 1517 | } |
| 1518 | return self::$debugOn; |
| 1519 | } |
| 1520 | /* |
| 1521 | public static function moreCronReccurences(){ |
| 1522 | return array( |
| 1523 | 'everyminute' => array('interval' => 60, 'display' => 'Once Every Minute'), |
| 1524 | ); |
| 1525 | } |
| 1526 | */ |
| 1527 | } |
| 1528 | ?> |
| 1529 |