Various bug fixes, more debug lines

develop
Steve Allison 2013-02-21 14:02:37 +00:00
parent ade7be0daa
commit 43fa4a0678
1 changed files with 37 additions and 25 deletions

View File

@ -33,6 +33,7 @@ class defense extends rcube_plugin {
// Logfile // Logfile
private $logfile = 'defense.log'; private $logfile = 'defense.log';
private $debugEnabled;
/** /**
* Output text to log file: $this->logfile * Output text to log file: $this->logfile
@ -54,10 +55,10 @@ class defense extends rcube_plugin {
foreach ($array as $value) { foreach ($array as $value) {
// If no slash '/' then its not a CIDR address and we can just string match // If no slash '/' then its not a CIDR address and we can just string match
if ((strpos($value, '/') === false) && (strcmp($ip, $value) == 0)) { return true; } if ((strpos($value, '/') === false) && (strcmp($ip, $value) == 0)) { return true; }
if ((isIPv6($ip)) && (!isIPv6($value))) { return false; } if (($this->isIPv6($ip)) && (!$this->isIPv6($value))) { return false; }
if ((isIPv4($value)) && (!isIPv4($ip))) { return false; } if (($this->isIPv4($value)) && (!$this->isIPv4($ip))) { return false; }
if ((isIPv4($ip) && ($this->isIPv4inCIDR($ip, $value))) { return true; } if (($this->isIPv4($ip) && ($this->isIPv4inCIDR($ip, $value)))) { return true; }
if ((isIPv6($ip) && ($this->isIPv6inCIDR($ip, $value))) { return true; } if (($this->isIPv6($ip) && ($this->isIPv6inCIDR($ip, $value)))) { return true; }
} }
return false; return false;
} }
@ -103,9 +104,10 @@ class defense extends rcube_plugin {
* @param string subnet mask * @param string subnet mask
* @return string byte array * @return string byte array
*/ */
private function isIPv6inCIDR($address, $subnetAddress, $subnetMask) { private function isIPv6inCIDR($ip, $cidr) {
$binMask = IPv6MaskToByteArray($subnetMask); list($subnet, $mask) = explode('/', $cidr);
return ($address & $binMask) == $subnetAddress; $binMask = $this->IPv6MaskToByteArray($mask);
return ($ip & $binMask) == $subnet;
} }
/** /**
* Check string if it is IPv6 * Check string if it is IPv6
@ -114,7 +116,7 @@ class defense extends rcube_plugin {
* @return bool * @return bool
*/ */
private function isIPv6($ip) { private function isIPv6($ip) {
return (((!preg_match('/^[\.\/:0-9a-f]+$/', strtolower($ip))) || (substr_count($ip, ':') < 2)) ? true : false) return (((!preg_match('/^[\.\/:0-9a-f]+$/', strtolower($ip))) || (substr_count($ip, ':') < 2)) ? true : false);
} }
/** /**
* Check string if it is IPv6 * Check string if it is IPv6
@ -123,7 +125,7 @@ class defense extends rcube_plugin {
* @return bool * @return bool
*/ */
private function isIPv4($ip) { private function isIPv4($ip) {
return ((preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?$/', $ip)) ? true : false) return ((preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?$/', $ip)) ? true : false);
} }
/** /**
* Write to log stating database error * Write to log stating database error
@ -160,6 +162,8 @@ class defense extends rcube_plugin {
$this->db_expire = $this->rc->config->get('defense_db_expire', 40); $this->db_expire = $this->rc->config->get('defense_db_expire', 40);
$this->log_pwd = $this->rc->config->get('defense_log_pwd', false); $this->log_pwd = $this->rc->config->get('defense_log_pwd', false);
$this->debug_enabled = $this->rc->config->get('defense_debug_enabled', false);
// set client ip // set client ip
$this->ipaddr = rcmail_remote_ip(); $this->ipaddr = rcmail_remote_ip();
@ -196,6 +200,7 @@ class defense extends rcube_plugin {
header('HTTP/1.1 403 Forbidden'); header('HTTP/1.1 403 Forbidden');
die(); die();
} }
$this->debug("send login form");
} }
/** /**
@ -221,46 +226,53 @@ class defense extends rcube_plugin {
// Log failed login attempt // Log failed login attempt
$data = array('user' => $args['user']); $data = array('user' => $args['user']);
$query = "INSERT INTO " . $this->db_table . " (epoch, type, ipaddr, data) VALUES (?, ?, ?, ?)"; $query = sprintf("INSERT INTO %s (epoch, type, ipaddr, data) VALUES (%d, %d, '%s', '%s')", $this->db_table, time(), 0, $this->ipaddr, serialize($data));
$result = $this->rc->db->query($query, time(), 0, $this->ipaddr, serialize($data)); $result = $this->rc->db->query($query);
if (!$result) { $this->dbError(); return; } if (!$result) { $this->dbError($query); return; }
$this->debug($query . " [" . $result->rowCount() . "]");
// Get number of failed attempts in <fail_reset> seconds // Get number of failed attempts in <fail_reset> seconds
$rTime = (time() - $this->fail_reset); // How far to look back for failed logins $rTime = (time() - $this->fail_reset); // How far to look back for failed logins
$query = "SELECT count(*) AS n FROM " . $this->db_table . " WHERE ipaddr = ? AND epoch >= ?"; $query = sprintf("SELECT count(*) AS n FROM %s WHERE ipaddr = '%s' AND epoch >= %d", $this->db_table, $this->ipaddr, $rTime);
$result = $this->rc->db->query($query, $this->ipaddr, $rTime); $result = $this->rc->db->query($query);
if (!$result) { $this->dbError(); return; } if (!$result) { $this->dbError($query); return; }
$this->debug($query . " [" . $result->rowCount() . "]");
$row = $result->fetch(); $row = $result->fetch();
if (!$row) { return; } // No rows? Strange, abort. if (!$row) { $this->debug("Warning, SQL result empty: $query"); return; } // No rows? Strange, abort.
$this->debug("Found " . $row['n'] . " failed attempts");
// Check if we have too many failures // Check if we have too many failures
if ($row['n'] >= $this->fail_max) { if ($row['n'] >= $this->fail_max) {
$this->debug("IP banned.");
// This IP is now banned // This IP is now banned
$repeat = 0; $repeat = 0;
// Check if its been banned before // Check if its been banned before
$query = "SELECT epoch, data FROM " . $this->db_table . " WHERE ipaddr = ? AND type = 1 ORDER BY id DESC LIMIT 1"; $query = sprintf("SELECT epoch, data FROM %s WHERE ipaddr = '%s' AND type = %d ORDER BY id DESC LIMIT 1", $this->db_table, $this->ipaddr, 1);
$result = $this->rc->db->query($query, $this->ipaddr); $result = $this->rc->db->query($query);
if (!$result) { $this->dbError(); return; } if (!$result) { $this->dbError($query); return; }
$this->debug($query . " [" . $result->rowCount() . "]");
if ($result->rowCount() > 0) { if ($result->rowCount() > 0) {
// IP has been banned before, check if its a recent repeat offender // IP has been banned before, check if its a recent repeat offender
$row = $result->fetch(); $row = $result->fetch();
$data = unserialize($row['data']); $data = unserialize($row['data']);
$this->debug("IP previous ban data: " . $row['data']);
// Classed as a repeate offender if IP is banned again after the previous ban duration // Classed as a repeate offender if IP is banned again after the previous ban duration
// multiplied by <repeat_multiplier> // multiplied by <repeat_multiplier>
if (time() <= (($data['duration'] * $this->repeat_multiplier) + $row['epoch'])) { if (time() <= (($data['duration'] * $this->repeat_multiplier) + $row['epoch'])) {
// Repeat offender, increase repeat // Repeat offender, increase repeat
echo "increase repeat\n";
$repeat = $data['repeat'] +1; $repeat = $data['repeat'] +1;
$this->debug("Repeat offender. Repeat set to " . $repeat);
} }
} }
$duration = ($this->ban_period * ($repeat > 0 ? pow($this->repeat_multiplier,$repeat) : 1));
$data = array( $data = array(
'duration' => ($this->ban_period * ($repeat > 0 ? pow($this->repeat_multiplier,$repeat) : 1)), // Ban duration based on history 'duration' => $duration, // Ban duration based on history
'repeat' => $repeat 'repeat' => $repeat
); );
$query = "INSERT INTO " . $this->db_table . " (epoch, type, ipaddr, data) VALUES (?, ?, ?, ?)"; $query = sprintf("INSERT INTO %s (epoch, type, ipaddr, data) VALUES (%d, %d, '%s', '%s')", $this->db_table, time(), 1, $this->ipaddr, serialize($data));
$result = $this->rc->db->query($query, time(), 1, $this->ipaddr, serialize($data)); $result = $this->rc->db->query($query, time(), 1, $this->ipaddr, serialize($data));
if (!$result) { $this->dbError(); return; } if (!$result) { $this->dbError($query); return; }
$this->debug($query . " [" . $result->rowCount() . "]");
$this->debug("Ban set to: " . $duration . "s");
return; return;
} }