| 1 | <?php if (!defined('BB2_CWD')) die("I said no cheating!"); |
| 2 | |
| 3 | // Bad Behavior entry point is start_bad_behavior(). |
| 4 | // If you're reading this, you are probably lost. |
| 5 | // Go read the bad-behavior-generic.php file. |
| 6 | |
| 7 | define('BB2_CORE', dirname(__FILE__)); |
| 8 | define('BB2_COOKIE', 'bb2_screener_'); |
| 9 | |
| 10 | require_once(BB2_CORE . "/functions.inc.php"); |
| 11 | |
| 12 | // Our log table structure |
| 13 | function bb2_table_structure($name) |
| 14 | { |
| 15 | // It's not paranoia if they really are out to get you. |
| 16 | $name_escaped = bb2_db_escape($name); |
| 17 | return "CREATE TABLE IF NOT EXISTS `$name_escaped` ( |
| 18 | `id` INT(11) NOT NULL auto_increment, |
| 19 | `ip` TEXT NOT NULL, |
| 20 | `date` DATETIME NOT NULL default '0000-00-00 00:00:00', |
| 21 | `request_method` TEXT NOT NULL, |
| 22 | `request_uri` TEXT NOT NULL, |
| 23 | `server_protocol` TEXT NOT NULL, |
| 24 | `http_headers` TEXT NOT NULL, |
| 25 | `user_agent` TEXT NOT NULL, |
| 26 | `request_entity` TEXT NOT NULL, |
| 27 | `key` TEXT NOT NULL, |
| 28 | INDEX (`ip`(15)), |
| 29 | INDEX (`user_agent`(10)), |
| 30 | PRIMARY KEY (`id`) );"; // TODO: INDEX might need tuning |
| 31 | } |
| 32 | |
| 33 | // Insert a new record |
| 34 | function bb2_insert($settings, $package, $key) |
| 35 | { |
| 36 | $ip = bb2_db_escape($package['ip']); |
| 37 | $date = bb2_db_date(); |
| 38 | $request_method = bb2_db_escape($package['request_method']); |
| 39 | $request_uri = bb2_db_escape($package['request_uri']); |
| 40 | $server_protocol = bb2_db_escape($package['server_protocol']); |
| 41 | $user_agent = bb2_db_escape($package['user_agent']); |
| 42 | $headers = "$request_method $request_uri $server_protocol\n"; |
| 43 | foreach ($package['headers'] as $h => $v) { |
| 44 | $headers .= bb2_db_escape("$h: $v\n"); |
| 45 | } |
| 46 | $request_entity = ""; |
| 47 | if (!strcasecmp($request_method, "POST")) { |
| 48 | foreach ($package['request_entity'] as $h => $v) { |
| 49 | $request_entity .= bb2_db_escape("$h: $v\n"); |
| 50 | } |
| 51 | } |
| 52 | return "INSERT INTO `" . bb2_db_escape($settings['log_table']) . "` |
| 53 | (`ip`, `date`, `request_method`, `request_uri`, `server_protocol`, `http_headers`, `user_agent`, `request_entity`, `key`) VALUES |
| 54 | ('$ip', '$date', '$request_method', '$request_uri', '$server_protocol', '$headers', '$user_agent', '$request_entity', '$key')"; |
| 55 | } |
| 56 | |
| 57 | // Kill 'em all! |
| 58 | function bb2_banned($settings, $package, $key, $previous_key=false) |
| 59 | { |
| 60 | // Some spambots hit too hard. Slow them down a bit. |
| 61 | sleep(2); |
| 62 | |
| 63 | require_once(BB2_CORE . "/banned.inc.php"); |
| 64 | bb2_display_denial($settings, $key, $previous_key); |
| 65 | bb2_log_denial($settings, $package, $key, $previous_key); |
| 66 | if (is_callable('bb2_banned_callback')) { |
| 67 | bb2_banned_callback($settings, $package, $key); |
| 68 | } |
| 69 | // Penalize the spammers some more |
| 70 | require_once(BB2_CORE . "/housekeeping.inc.php"); |
| 71 | bb2_housekeeping($settings, $package); |
| 72 | die(); |
| 73 | } |
| 74 | |
| 75 | function bb2_approved($settings, $package) |
| 76 | { |
| 77 | // Dirk wanted this |
| 78 | if (is_callable('bb2_approved_callback')) { |
| 79 | bb2_approved_callback($settings, $package); |
| 80 | } |
| 81 | |
| 82 | // Decide what to log on approved requests. |
| 83 | if (($settings['verbose'] && $settings['logging']) || empty($package['user_agent'])) { |
| 84 | bb2_db_query(bb2_insert($settings, $package, "00000000")); |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | // Check the results of a particular test; see below for usage |
| 89 | // Returns FALSE if test passed (yes this is backwards) |
| 90 | function bb2_test($settings, $package, $result) |
| 91 | { |
| 92 | if ($result !== FALSE) |
| 93 | { |
| 94 | bb2_banned($settings, $package, $result); |
| 95 | return TRUE; |
| 96 | } |
| 97 | return FALSE; |
| 98 | } |
| 99 | |
| 100 | |
| 101 | // Let God sort 'em out! |
| 102 | function bb2_start($settings) |
| 103 | { |
| 104 | // Gather up all the information we need, first of all. |
| 105 | $headers = bb2_load_headers(); |
| 106 | // Postprocess the headers to mixed-case |
| 107 | // FIXME: get the world to stop using PHP as CGI |
| 108 | $headers_mixed = array(); |
| 109 | foreach ($headers as $h => $v) { |
| 110 | $headers_mixed[uc_all($h)] = $v; |
| 111 | } |
| 112 | |
| 113 | // We use these frequently. Keep a copy close at hand. |
| 114 | $ip = $_SERVER['REMOTE_ADDR']; |
| 115 | $request_method = $_SERVER['REQUEST_METHOD']; |
| 116 | $request_uri = $_SERVER['REQUEST_URI']; |
| 117 | $server_protocol = $_SERVER['SERVER_PROTOCOL']; |
| 118 | @$user_agent = $_SERVER['HTTP_USER_AGENT']; |
| 119 | |
| 120 | // Reconstruct the HTTP entity, if present. |
| 121 | $request_entity = array(); |
| 122 | if (!strcasecmp($request_method, "POST") || !strcasecmp($request_method, "PUT")) { |
| 123 | foreach ($_POST as $h => $v) { |
| 124 | $request_entity[$h] = $v; |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | $package = array('ip' => $ip, 'headers' => $headers, 'headers_mixed' => $headers_mixed, 'request_method' => $request_method, 'request_uri' => $request_uri, 'server_protocol' => $server_protocol, 'request_entity' => $request_entity, 'user_agent' => $user_agent, 'is_browser' => false); |
| 129 | |
| 130 | // Please proceed to the security checkpoint and have your |
| 131 | // identification and boarding pass ready. |
| 132 | |
| 133 | // First check the whitelist |
| 134 | require_once(BB2_CORE . "/whitelist.inc.php"); |
| 135 | if (!bb2_whitelist($package)) { |
| 136 | // Now check the blacklist |
| 137 | require_once(BB2_CORE . "/blacklist.inc.php"); |
| 138 | bb2_test($settings, $package, bb2_blacklist($package)); |
| 139 | |
| 140 | // Check the http:BL |
| 141 | require_once(BB2_CORE . "/blackhole.inc.php"); |
| 142 | bb2_test($settings, $package, bb2_httpbl($settings, $package)); |
| 143 | |
| 144 | // Check for common stuff |
| 145 | require_once(BB2_CORE . "/common_tests.inc.php"); |
| 146 | bb2_test($settings, $package, bb2_protocol($settings, $package)); |
| 147 | bb2_test($settings, $package, bb2_cookies($settings, $package)); |
| 148 | bb2_test($settings, $package, bb2_misc_headers($settings, $package)); |
| 149 | |
| 150 | // Specific checks |
| 151 | @$ua = $headers_mixed['User-Agent']; |
| 152 | // MSIE checks |
| 153 | if (stripos($ua, "MSIE") !== FALSE) { |
| 154 | $package['is_browser'] = true; |
| 155 | if (stripos($ua, "Opera") !== FALSE) { |
| 156 | require_once(BB2_CORE . "/opera.inc.php"); |
| 157 | bb2_test($settings, $package, bb2_opera($package)); |
| 158 | } else { |
| 159 | require_once(BB2_CORE . "/msie.inc.php"); |
| 160 | bb2_test($settings, $package, bb2_msie($package)); |
| 161 | } |
| 162 | } elseif (stripos($ua, "Konqueror") !== FALSE) { |
| 163 | $package['is_browser'] = true; |
| 164 | require_once(BB2_CORE . "/konqueror.inc.php"); |
| 165 | bb2_test($settings, $package, bb2_konqueror($package)); |
| 166 | } elseif (stripos($ua, "Opera") !== FALSE) { |
| 167 | $package['is_browser'] = true; |
| 168 | require_once(BB2_CORE . "/opera.inc.php"); |
| 169 | bb2_test($settings, $package, bb2_opera($package)); |
| 170 | } elseif (stripos($ua, "Safari") !== FALSE) { |
| 171 | $package['is_browser'] = true; |
| 172 | require_once(BB2_CORE . "/safari.inc.php"); |
| 173 | bb2_test($settings, $package, bb2_safari($package)); |
| 174 | } elseif (stripos($ua, "Lynx") !== FALSE) { |
| 175 | $package['is_browser'] = true; |
| 176 | require_once(BB2_CORE . "/lynx.inc.php"); |
| 177 | bb2_test($settings, $package, bb2_lynx($package)); |
| 178 | } elseif (stripos($ua, "MovableType") !== FALSE) { |
| 179 | require_once(BB2_CORE . "/movabletype.inc.php"); |
| 180 | bb2_test($settings, $package, bb2_movabletype($package)); |
| 181 | } elseif (stripos($ua, "msnbot") !== FALSE || stripos($ua, "MS Search") !== FALSE) { |
| 182 | require_once(BB2_CORE . "/msnbot.inc.php"); |
| 183 | bb2_test($settings, $package, bb2_msnbot($package)); |
| 184 | } elseif (stripos($ua, "Googlebot") !== FALSE || stripos($ua, "Mediapartners-Google") !== FALSE) { |
| 185 | require_once(BB2_CORE . "/google.inc.php"); |
| 186 | bb2_test($settings, $package, bb2_google($package)); |
| 187 | } elseif (stripos($ua, "Mozilla") !== FALSE && stripos($ua, "Mozilla") == 0) { |
| 188 | $package['is_browser'] = true; |
| 189 | require_once(BB2_CORE . "/mozilla.inc.php"); |
| 190 | bb2_test($settings, $package, bb2_mozilla($package)); |
| 191 | } |
| 192 | |
| 193 | // More intensive screening applies to POST requests |
| 194 | if (!strcasecmp('POST', $package['request_method'])) { |
| 195 | require_once(BB2_CORE . "/post.inc.php"); |
| 196 | bb2_test($settings, $package, bb2_post($settings, $package)); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | // Last chance screening. |
| 201 | require_once(BB2_CORE . "/screener.inc.php"); |
| 202 | bb2_screener($settings, $package); |
| 203 | |
| 204 | // And that's about it. |
| 205 | bb2_approved($settings, $package); |
| 206 | return true; |
| 207 | } |
| 208 | ?> |
| 209 | |