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
wordfenceHash.php
224 lines
| 1 | <?php |
| 2 | require_once('wordfenceClass.php'); |
| 3 | class wordfenceHash { |
| 4 | private $apiKey = false; |
| 5 | private $wp_version = false; |
| 6 | private $api = false; |
| 7 | private $db = false; |
| 8 | private $table = false; |
| 9 | private $fileQ = array(); |
| 10 | |
| 11 | //Begin serialized vars |
| 12 | private $whitespace = array("\n","\r","\t"," "); |
| 13 | public $totalData = 0; //To do a sanity check, don't use 'du' because it gets sparse files wrong and reports blocks used on disk. Use : find . -type f -ls | awk '{total += $7} END {print total}' |
| 14 | public $totalFiles = 0; |
| 15 | public $totalDirs = 0; |
| 16 | public $linesOfPHP = 0; |
| 17 | public $linesOfJCH = 0; //lines of HTML, CSS and javascript |
| 18 | public $striplen = 0; |
| 19 | private $hashPacket = ""; |
| 20 | public $hashStorageID = false; |
| 21 | private $hashingStartTime = false; |
| 22 | private $lastStatusTime = false; |
| 23 | public function __sleep(){ //same order as above |
| 24 | if(sizeof($this->fileQ) > 0){ |
| 25 | throw new Exception("Sanity fail. fileQ is not empty. Has: " . sizeof($this->fileQ)); |
| 26 | } |
| 27 | return array('whitespace', 'totalData', 'totalFiles', 'totalDirs', 'linesOfPHP', 'linesOfJCH', 'striplen', 'hashPacket', 'hashStorageID', 'hashingStartTime', 'lastStatusTime'); |
| 28 | } |
| 29 | public function __construct($striplen){ |
| 30 | $this->striplen = $striplen; |
| 31 | $this->db = new wfDB(); |
| 32 | $this->table = $this->db->prefix() . 'wfFileQueue'; |
| 33 | $this->apiKey = wfConfig::get('apiKey'); |
| 34 | $this->wp_version = wfUtils::getWPVersion(); |
| 35 | $this->api = new wfAPI($this->apiKey, $this->wp_version); |
| 36 | } |
| 37 | public function __wakeup(){ |
| 38 | $this->db = new wfDB(); |
| 39 | $this->table = $this->db->prefix() . 'wfFileQueue'; |
| 40 | $this->apiKey = wfConfig::get('apiKey'); |
| 41 | $this->wp_version = wfUtils::getWPVersion(); |
| 42 | $this->api = new wfAPI($this->apiKey, $this->wp_version); |
| 43 | } |
| 44 | public function buildFileQueue($path, $only = array()){ //base path and 'only' is a list of files and dirs in the bast that are the only ones that should be processed. Everything else in base is ignored. If only is empty then everything is processed. |
| 45 | $this->db->truncate($this->table); |
| 46 | if($path[strlen($path) - 1] != '/'){ |
| 47 | $path .= '/'; |
| 48 | } |
| 49 | if(! is_readable($path)){ |
| 50 | throw new Exception("Could not read directory $path to do scan."); |
| 51 | exit(); |
| 52 | } |
| 53 | $files = scandir($path); |
| 54 | foreach($files as $file){ |
| 55 | if(sizeof($only) > 0 && (! in_array($file, $only))){ |
| 56 | continue; |
| 57 | } |
| 58 | $file = $path . $file; |
| 59 | wordfence::status(4, 'info', "Hashing item in base dir: $file"); |
| 60 | $this->_dirHash($file); |
| 61 | } |
| 62 | $this->writeFileQueue(); //Final write to DB |
| 63 | |
| 64 | } |
| 65 | public function genHashes($forkObj){ |
| 66 | if(! $this->hashingStartTime){ |
| 67 | $this->hashingStartTime = microtime(true); |
| 68 | } |
| 69 | if(! $this->lastStatusTime){ |
| 70 | $this->lastStatusTime = microtime(true); |
| 71 | } |
| 72 | $haveMoreInDB = true; |
| 73 | while($haveMoreInDB){ |
| 74 | $haveMoreInDB = false; |
| 75 | //This limit used to be 1000, but we changed it to 5 because forkIfNeeded needs to run frequently, but |
| 76 | // we still want to minimize the number of queries we do. |
| 77 | // So now we select, process and delete 5 from teh queue and then check forkIfNeeded() |
| 78 | // So this assumes that processing 5 files won't take longer than wfScanEngine::$maxExecTime (which was 10 at the time of writing, which is 2 secs per file) |
| 79 | $res = $this->db->query("select id, filename from " . $this->table . " limit 5"); |
| 80 | $ids = array(); |
| 81 | while($rec = mysql_fetch_row($res)){ |
| 82 | $this->processFile($rec[1]); |
| 83 | array_push($ids, $rec[0]); |
| 84 | $haveMoreInDB = true; |
| 85 | } |
| 86 | if(sizeof($ids) > 0){ |
| 87 | $this->db->query("delete from " . $this->table . " where id IN (" . implode(',', $ids) . ")"); |
| 88 | } |
| 89 | $forkObj->forkIfNeeded(); |
| 90 | } |
| 91 | //Will only reach here if we empty file queue. fork may cause exit |
| 92 | $this->sendHashPacket(); |
| 93 | $this->db->truncate($this->table); //Also resets id autoincrement to 1 |
| 94 | $this->writeHashingStatus(); |
| 95 | } |
| 96 | private function writeHashingStatus(){ |
| 97 | $this->lastStatusTime = microtime(true); |
| 98 | wordfence::status(2, 'info', "Scanned " . $this->totalFiles . " files at a rate of " . sprintf('%.2f', ($this->totalFiles / (microtime(true) - $this->hashingStartTime))) . " files per second."); |
| 99 | } |
| 100 | private function _dirHash($path){ |
| 101 | if(substr($path, -3, 3) == '/..' || substr($path, -2, 2) == '/.'){ |
| 102 | return; |
| 103 | } |
| 104 | if(! is_readable($path)){ return; } //Applies to files and dirs |
| 105 | if(is_dir($path)){ |
| 106 | $this->totalDirs++; |
| 107 | if($path[strlen($path) - 1] != '/'){ |
| 108 | $path .= '/'; |
| 109 | } |
| 110 | $cont = scandir($path); |
| 111 | for($i = 0; $i < sizeof($cont); $i++){ |
| 112 | if($cont[$i] == '.' || $cont[$i] == '..'){ continue; } |
| 113 | $file = $path . $cont[$i]; |
| 114 | if(is_file($file)){ |
| 115 | $this->qFile($file); |
| 116 | } else if(is_dir($file)) { |
| 117 | $this->_dirHash($file); |
| 118 | } |
| 119 | } |
| 120 | } else { |
| 121 | if(is_file($path)){ |
| 122 | $this->qFile($path); |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | private function qFile($file){ |
| 127 | $this->fileQ[] = $file; |
| 128 | if(sizeof($this->fileQ) > 1000){ |
| 129 | $this->writeFileQueue(); |
| 130 | } |
| 131 | } |
| 132 | private function writeFileQueue(){ |
| 133 | $sql = "insert into " . $this->table . " (filename) values "; |
| 134 | $added = false; |
| 135 | foreach($this->fileQ as $val){ |
| 136 | $added = true; |
| 137 | $sql .= "('" . mysql_real_escape_string($val) . "'),"; |
| 138 | } |
| 139 | if($added){ |
| 140 | $sql = rtrim($sql, ','); |
| 141 | $this->db->query($sql); |
| 142 | } |
| 143 | $this->fileQ = array(); |
| 144 | } |
| 145 | private function processFile($file){ |
| 146 | if(wfUtils::fileTooBig($file)){ |
| 147 | wordfence::status(4, 'info', "Skipping file larger than max size: $file"); |
| 148 | return; |
| 149 | } |
| 150 | if(function_exists('memory_get_usage')){ |
| 151 | wordfence::status(4, 'info', "Scanning: $file (Mem:" . sprintf('%.1f', memory_get_usage(true) / (1024 * 1024)) . "M)"); |
| 152 | } else { |
| 153 | wordfence::status(4, 'info', "Scanning: $file"); |
| 154 | } |
| 155 | $wfHash = $this->wfHash($file); |
| 156 | if($wfHash){ |
| 157 | $packetFile = substr($file, $this->striplen); |
| 158 | $this->hashPacket .= $wfHash[0] . $wfHash[1] . pack('n', strlen($packetFile)) . $packetFile; |
| 159 | if(strlen($this->hashPacket) > 500000){ //roughly 2 megs in string mem space |
| 160 | $this->writeHashingStatus(); |
| 161 | $this->sendHashPacket(); |
| 162 | } |
| 163 | |
| 164 | //Now that we know we can open the file, lets update stats |
| 165 | if(preg_match('/\.(?:js|html|htm|css)$/i', $file)){ |
| 166 | $this->linesOfJCH += sizeof(file($file)); |
| 167 | } else if(preg_match('/\.php$/i', $file)){ |
| 168 | $this->linesOfPHP += sizeof(file($file)); |
| 169 | } |
| 170 | $this->totalFiles++; |
| 171 | $this->totalData += filesize($file); //We already checked if file overflows int in the fileTooBig routine above |
| 172 | if(microtime(true) - $this->lastStatusTime > 1){ |
| 173 | $this->writeHashingStatus(); |
| 174 | } |
| 175 | } else { |
| 176 | wordfence::status(2, 'error', "Could not gen hash for file (probably because we don't have permission to access the file): $file"); |
| 177 | } |
| 178 | } |
| 179 | private function sendHashPacket(){ |
| 180 | wordfence::status(4, 'info', "Sending packet of hash data to Wordfence scanning servers"); |
| 181 | if(strlen($this->hashPacket) < 1){ |
| 182 | return; |
| 183 | } |
| 184 | if($this->hashStorageID){ |
| 185 | $dataArr = $this->api->binCall('add_hash_chunk', "WFID:" . pack('N', $this->hashStorageID) . $this->hashPacket); |
| 186 | $this->hashPacket = ""; |
| 187 | if(is_array($dataArr) && isset($dataArr['data']) && $dataArr['data'] == $this->hashStorageID){ |
| 188 | //keep going |
| 189 | } else { |
| 190 | throw new Exception("Could not store an additional chunk of hash data on Wordfence servers with ID: " . $this->hashStorageID); |
| 191 | } |
| 192 | } else { |
| 193 | $dataArr = $this->api->binCall('add_hash_chunk', "WFST:" . $this->hashPacket); |
| 194 | $this->hashPacket = ""; |
| 195 | if(is_array($dataArr) && isset($dataArr['data']) && preg_match('/^\d+$/', $dataArr['data'])){ |
| 196 | $this->hashStorageID = $dataArr['data']; |
| 197 | } else { |
| 198 | throw new Exception("Could not store hash data on Wordfence servers. Got response: " . var_export($dataArr, true)); |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | public function getHashStorageID(){ |
| 203 | return $this->hashStorageID; |
| 204 | } |
| 205 | public function wfHash($file){ |
| 206 | wfUtils::errorsOff(); |
| 207 | $md5 = @md5_file($file, false); |
| 208 | wfUtils::errorsOn(); |
| 209 | |
| 210 | if(! $md5){ return false; } |
| 211 | $fp = @fopen($file, "rb"); |
| 212 | if(! $fp){ |
| 213 | return false; |
| 214 | } |
| 215 | $ctx = hash_init('sha256'); |
| 216 | while (!feof($fp)) { |
| 217 | hash_update($ctx, str_replace($this->whitespace,"",fread($fp, 65536))); |
| 218 | } |
| 219 | $shac = hash_final($ctx, false); |
| 220 | return array($md5, $shac); |
| 221 | } |
| 222 | } |
| 223 | ?> |
| 224 |