Various bug fixes, more debug lines
parent
ade7be0daa
commit
43fa4a0678
62
defense.php
62
defense.php
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue