* Licensed under the GNU GPL. For full terms see the file COPYING. * * @package plugins * @subpackage lockout * */ /** * Initialize this plugin (load config values) * * @return boolean FALSE if no configuration file could be loaded, TRUE otherwise * */ function lockout_init() { if (!@include_once (SM_PATH . 'plugins/lockout/data/config.php')) return FALSE; return TRUE; } /** * Validate that this plugin is configured correctly * * @return boolean Whether or not there was a * configuration error for this plugin. * */ function lockout_configtest_do() { // make sure base config is available // if (!lockout_init()) { do_err('Lockout plugin is missing its main configuration file', FALSE); return TRUE; } // make sure the lockout rules configuration is in place if necessary // global $use_lockout_rules; if ($use_lockout_rules) { if (!file_exists(SM_PATH . 'plugins/lockout/data/lockout_table.php') || !is_readable(SM_PATH . 'plugins/lockout/data/lockout_table.php')) { do_err('Lockout plugin lockout rules file is missing (data/lockout_table.php)', FALSE); return TRUE; } } // make sure config settings are in correct format // global $max_login_attempts; if (!empty($max_login_attempts) && !preg_match('/^\d+:\d+:\d+$/', $max_login_attempts)) { do_err('Lockout plugin is misconfigured: $max_login_attempts must be in the form "n:n:n", where each "n" must be a number', FALSE); return TRUE; } global $max_login_attempts_per_IP; if (!empty($max_login_attempts_per_IP) && !preg_match('/^\d+:\d+:\d+$/', $max_login_attempts_per_IP)) { do_err('Lockout plugin is misconfigured: $max_login_attempts_per_IP must be in the form "n:n:n", where each "n" must be a number', FALSE); return TRUE; } global $activate_CAPTCHA_after_failed_attempts; if (!empty($activate_CAPTCHA_after_failed_attempts) && !preg_match('/^\d+:\d+:\d+$/', $activate_CAPTCHA_after_failed_attempts)) { do_err('Lockout plugin is misconfigured: $activate_CAPTCHA_after_failed_attempts must be in the form "n:n:n", where each "n" must be a number', FALSE); return TRUE; } // make sure the CAPTCHA plugin is available when needed // if ($activate_CAPTCHA_after_failed_attempts && !check_plugin_version('captcha', 1, 0, 0, TRUE)) { do_err('Lockout plugin is configured to use the CAPTCHA plugin, but the CAPTCHA plugin was not found', FALSE); return TRUE; } // only need to do this pre-1.5.2, as 1.5.2 will make this // check for us automatically // if (!check_sm_version(1, 5, 2)) { // try to find Compatibility, and then that it is v2.0.7+ // if (function_exists('check_plugin_version') && check_plugin_version('compatibility', 2, 0, 7, TRUE)) return FALSE; // something went wrong // do_err('Lockou plugin requires the Compatibility plugin version 2.0.7+', FALSE); return TRUE; } return FALSE; } /** * Make sure this plugin fires the LAST on the login_before * hook, but before any plugins that might show different * logout error (captcha, ) * */ function move_lockout_to_end_of_login_before_hook_do() { global $PHP_SELF; if (stristr($PHP_SELF, '/redirect.php')) { reposition_plugin_on_hook('lockout', 'login_before', FALSE); reposition_plugin_on_hook('captcha', 'login_before', FALSE); } } /** * Activate the CAPTCHA plugin if need be * */ function activate_captcha_plugin_do($args) { global $data_dir, $activate_CAPTCHA_after_failed_attempts, $prefs_dsn, $prefs_are_cached, $prefs_cache, $null; lockout_init(); if (!$activate_CAPTCHA_after_failed_attempts || !sqGetGlobalVar('REMOTE_ADDR', $remote_addr, SQ_SERVER)) return; // eiw - a bit messy, but we need to have prefs // functions here on the login page where they // are not usually needed - borrowed from init.php // and NOT tested with custom pref backends // if (check_sm_version(1, 5, 2)) { /* more recently, 1.5.2 fixed this issue...... include_once(SM_PATH . 'functions/strings.php'); include_once(SM_PATH . 'functions/prefs.php'); $prefs_backend = do_hook('prefs_backend', $null); if (isset($prefs_backend) && !empty($prefs_backend) && file_exists(SM_PATH . $prefs_backend)) { include_once(SM_PATH . $prefs_backend); } elseif (isset($prefs_dsn) && !empty($prefs_dsn)) { include_once(SM_PATH . 'functions/db_prefs.php'); } else { include_once(SM_PATH . 'functions/file_prefs.php'); } */ } else include_once(SM_PATH . 'functions/prefs.php'); // check if IP addr has been locked out // because of too many login failures // // note how we unset the prefs cache flag before/after this // code so as not to corrupt any prefs for the user who is // currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $enable_captcha = getPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_CAPTCHA_TOO_MANY_FAILED_LOGIN_ATTEMPTS', ''); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); if ($enable_captcha) { // is the lockout window over? if so, clear all // lockout preference settings, otherwise turn on CAPTCHA // if (is_numeric($enable_captcha) && time() > $enable_captcha) { setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_CAPTCHA_TOO_MANY_FAILED_LOGIN_ATTEMPTS', ''); setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_CAPTCHA_login_failure_times', ''); // clear prefs cache again // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $enable_captcha = ''; } else { add_plugin('captcha', $args); } } } /** * Activate the CAPTCHA plugin (receiving end) if need be * */ function activate_captcha_plugin_check_code_do($args) { global $data_dir, $activate_CAPTCHA_after_failed_attempts, $prefs_are_cached, $prefs_cache; lockout_init(); if (!$activate_CAPTCHA_after_failed_attempts || !sqGetGlobalVar('REMOTE_ADDR', $remote_addr, SQ_SERVER)) return; // check if IP addr has been locked out // because of too many login failures // // note how we unset the prefs cache flag before/after this // code so as not to corrupt any prefs for the user who is // currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $enable_captcha = getPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_CAPTCHA_TOO_MANY_FAILED_LOGIN_ATTEMPTS', ''); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); if ($enable_captcha) { add_plugin('captcha', $args); } } /** * Checks for prior violations of maximum failed login attempts * as well as the rules for what users can and can't log in. * */ function check_lockout_do($args) { global $data_dir, $login_username, $at, $reverseLockout, $use_lockout_rules, $prefs_are_cached, $prefs_cache, $max_login_attempts, $max_login_attempts_per_IP, $obey_x_forwarded_headers; lockout_init(); if ($max_login_attempts) { // first, check if user has already been locked out // because they had too many login failures // // note how we unset the prefs cache flag before/after this // code so as not to corrupt any prefs for the user who is // currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $locked_out = getPref($data_dir, 'lockout_plugin_login_failure_information', $login_username . '_TOO_MANY_FAILED_LOGIN_ATTEMPTS', ''); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); if ($locked_out) { // is the lockout window over? if so, clear all // lockout preference settings, otherwise error out // if (is_numeric($locked_out) && time() > $locked_out) { setPref($data_dir, 'lockout_plugin_login_failure_information', $login_username . '_TOO_MANY_FAILED_LOGIN_ATTEMPTS', ''); setPref($data_dir, 'lockout_plugin_login_failure_information', $login_username . '_login_failure_times', ''); // clear prefs cache again // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $locked_out = ''; } else { sq_change_text_domain('lockout'); logout_error(_("Access denied. Please contact your system administrator.")); exit; } } } if ($max_login_attempts_per_IP && sqGetGlobalVar('REMOTE_ADDR', $remote_addr, SQ_SERVER)) { // first, check if IP addr has already been locked out // because of too many login failures // // note how we unset the prefs cache flag before/after this // code so as not to corrupt any prefs for the user who is // currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $locked_out = getPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_TOO_MANY_FAILED_LOGIN_ATTEMPTS', ''); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); if ($locked_out) { // is the lockout window over? if so, clear all // lockout preference settings, otherwise error out // if (is_numeric($locked_out) && time() > $locked_out) { setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_TOO_MANY_FAILED_LOGIN_ATTEMPTS', ''); setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_login_failure_times', ''); // clear prefs cache again // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $locked_out = ''; } else { sq_change_text_domain('lockout'); logout_error(_("Access denied. Please contact your system administrator.")); exit; } } } // now, proceed with checking lockout rules if necessary // if (!$use_lockout_rules) { activate_captcha_plugin_check_code_do($args); return; } $user = $login_username; // grab hostname into local var // $host = ''; if (!$obey_x_forwarded_headers || !sqGetGlobalVar('HTTP_X_FORWARDED_HOST', $host, SQ_SERVER)) sqGetGlobalVar('HTTP_HOST', $host, SQ_SERVER); // get the domain from the username, since it might // be different than the domain being used to log in // if (strpos($user, $at) !== FALSE) $usersDomain = substr($user, strpos($user, $at) + 1); else $usersDomain = ''; if ($LOCKOUTTABLE = @fopen (SM_PATH . 'plugins/lockout/data/lockout_table.php', 'r')) { while (!feof($LOCKOUTTABLE)) { $line = fgets($LOCKOUTTABLE, 4096); $line = trim($line); // skip blank lines and comment lines and php // open/close tag lines // if (strpos($line, '#') === 0 || strlen($line) < 3 || strpos($line, '') === 0) continue; // if we have a hostname from the username, see if this is // a domain lockout line // if (!empty($usersDomain) && preg_match('/^\s*domain:\s*(\S+)\s+(.+)\s*$/', $line, $matches)) { // check for match with hostname, redirect if found // if (preg_match('/^' . str_replace(array('?', '*'), array('\w{1}', '.*?'), strtoupper($matches[1])) . '$/', strtoupper($usersDomain))) { fclose($LOCKOUTTABLE); if ($reverseLockout) { activate_captcha_plugin_check_code_do($args); return; } lockout_redirect($matches[2]); } } // if we were given a hostname, see if this is // a domain lockout line // if (!empty($host) && preg_match('/^\s*domain:\s*(\S+)\s+(.+)\s*$/', $line, $matches)) { // check for match with hostname, redirect if found // if (preg_match('/^' . str_replace(array('?', '*'), array('\w{1}', '.*?'), strtoupper($matches[1])) . '$/', strtoupper($host))) { fclose($LOCKOUTTABLE); if ($reverseLockout) { activate_captcha_plugin_check_code_do($args); return; } lockout_redirect($matches[2]); } } // if we were given a username, see if this is // a user lockout line // if (!empty($user) && preg_match('/^\s*user:\s*(\S+)\s+(.+)\s*$/', $line, $matches)) { // check for match with username, redirect if found // if (preg_match('/^' . str_replace(array('?', '*'), array('\w{1}', '.*?'), strtoupper($matches[1])) . '$/', strtoupper($user))) { fclose($LOCKOUTTABLE); if ($reverseLockout) { activate_captcha_plugin_check_code_do($args); return; } lockout_redirect($matches[2]); } } } fclose($LOCKOUTTABLE); // if we are reversing functionality, lockout anyone // not found in the lockout file // if ($reverseLockout) lockout_redirect($reverseLockout); } activate_captcha_plugin_check_code_do($args); } /** * Redirect current page request to another page, * or the built in (fake) login error page. * * We also log lockout rules violation events here. * */ function lockout_redirect($uri) { // log this event - note that to use this, // you'll need to have the Squirrel Logger plugin // installed and activated and you'll have to add // a "LOCKOUT" event type to its configuration // global $log_violated_lockout_rules, $login_username; if ($log_violated_lockout_rules && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('User "%s" has tripped lockout rules; login disallowed', $login_username)); } sqsession_destroy(); if (strpos($uri, 'http') === 0) { header('Location: ' . $uri); } else if ($uri == '##BAD_LOGIN_PAGE##') { include_once (SM_PATH . 'functions/display_messages.php'); // simulate delay sleep(5); global $locked_out_per_lockout_rules; $locked_out_per_lockout_rules = TRUE; logout_error( _("Unknown user or password incorrect.") ); // Why did I choose this string before? The above is better, right? //logout_error( _("You must be logged in to access this page.") ); } else { $location = get_location(); // need to trim off the last directory off the location path // $location = substr($location, 0, strrpos($location, '/')); header('Location: ' . $location . '/plugins/lockout/' . $uri); } exit; } /** * Valid login - reset failure count * */ function reset_failed_login_count_do() { global $prefs_are_cached, $prefs_cache, $username, $data_dir, $max_login_attempts, $max_login_attempts_per_IP, $activate_CAPTCHA_after_failed_attempts; lockout_init(); if ($max_login_attempts) { // note how we unset the prefs cache flag before/after this code so as not // to corrupt any prefs for the user who is currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); setPref($data_dir, 'lockout_plugin_login_failure_information', $username . '_login_failure_times', ''); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); } if (!sqGetGlobalVar('REMOTE_ADDR', $remote_addr, SQ_SERVER)) return; if ($max_login_attempts_per_IP) { // note how we unset the prefs cache flag before/after this code so as not // to corrupt any prefs for the user who is currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_login_failure_times', ''); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); } if ($activate_CAPTCHA_after_failed_attempts) { // note how we unset the prefs cache flag before/after this code so as not // to corrupt any prefs for the user who is currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_CAPTCHA_login_failure_times', ''); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); } } /** * Check number of successive failed login attempts * and block user or IP permanently if necessary * (or turn on CAPTCHA plugin) * */ function check_failed_login_count_do($args) { global $data_dir, $login_username, $max_login_attempts, $locked_out_per_lockout_rules, $prefs_are_cached, $prefs_cache, $max_login_attempts_per_IP, $activate_CAPTCHA_after_failed_attempts; lockout_init(); // skip the below if our own lockout rules // were the cause of this failed login // if ($locked_out_per_lockout_rules) return; $now = time(); if (check_sm_version(1, 5, 2)) $logout_message = $args[0]; else $logout_message = $args[1]; // this is a bit precarious - we determine if the user failed // login based on the error string... // $valid_messages = array(_("Unknown user or password incorrect.")); if (is_plugin_enabled('captcha') || $activate_CAPTCHA_after_failed_attempts) { sq_change_text_domain('captcha'); $valid_messages[] = _("Sorry, you did not provide the correct challenge response."); sq_change_text_domain('squirrelmail'); } if (!in_array($logout_message, $valid_messages)) return; if ($max_login_attempts) { list($max_failures, $number_minutes, $lockout_window) = explode(':', $max_login_attempts); // get and parse previous login failure stats and add current time to list // // note how we unset the prefs cache flag before/after this code so as not // to corrupt any prefs for the user who is currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $failure_times = getPref($data_dir, 'lockout_plugin_login_failure_information', $login_username . '_login_failure_times', ''); if ($failure_times) $failure_times = explode(':', $failure_times); else $failure_times = array(); $new_times = array(); foreach ($failure_times as $time) if ($number_minutes == 0 || $time >= $now - ($number_minutes * 60)) $new_times[] = $time; $new_times[] = $now; setPref($data_dir, 'lockout_plugin_login_failure_information', $login_username . '_login_failure_times', implode(':', $new_times)); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); // now check for and ban abusive users // if (sizeof($new_times) >= $max_failures) { if (!$lockout_window) $lockout_end_time = 'PERMANENT'; else $lockout_end_time = $now + ($lockout_window * 60); setPref($data_dir, 'lockout_plugin_login_failure_information', $login_username . '_TOO_MANY_FAILED_LOGIN_ATTEMPTS', $lockout_end_time); // clear prefs cache again // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $failed_times = ''; foreach ($new_times as $time) $failed_times .= date('H:i:s Y-m-d', $time) . "\n"; // send alert to admin if necessary // global $domain, $log_violated_max_user_logins; sq_change_text_domain('lockout'); if ($number_minutes > 0) { if (!$lockout_window) { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_violated_max_user_logins && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('User "%s" (domain "%s") has attempted (and failed) to log in %d times in the last %d minutes; %s has been LOCKED OUT PERMANENTLY', $login_username, $domain, sizeof($new_times), $number_minutes, $login_username)); } l_report_abuse(sprintf(_("NOTICE: User \"%s\" (domain \"%s\") has attempted (and failed) to log in %d times in the last %d minutes.\n\nTimes:\n%s\n\n%s has been LOCKED OUT PERMANENTLY.\n\nTo unlock this user, remove the \"%s_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $login_username, $domain, sizeof($new_times), $number_minutes, $failed_times, $login_username, $login_username), sprintf(_(" --- LOCKED OUT - %s"), $login_username)); } else { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_violated_max_user_logins && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('User "%s" (domain "%s") has attempted (and failed) to log in %d times in the last %d minutes; %s has been LOCKED OUT for %d minutes', $login_username, $domain, sizeof($new_times), $number_minutes, $login_username, $lockout_window)); } l_report_abuse(sprintf(_("NOTICE: User \"%s\" (domain \"%s\") has attempted (and failed) to log in %d times in the last %d minutes.\n\nTimes:\n%s\n\n%s has been LOCKED OUT for %d minutes.\n\nTo unlock this user before then, remove the \"%s_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $login_username, $domain, sizeof($new_times), $number_minutes, $failed_times, $login_username, $lockout_window, $login_username), sprintf(_(" --- LOCKED OUT - %s"), $login_username)); } } else { if (!$lockout_window) { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_violated_max_user_logins && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('User "%s" (domain "%s") has attempted (and failed) to log in %d times; %s has been LOCKED OUT PERMANENTLY', $login_username, $domain, sizeof($new_times), $login_username)); } l_report_abuse(sprintf(_("NOTICE: User \"%s\" (domain \"%s\") has attempted (and failed) to log in %d times.\n\nTimes:\n%s\n\n%s has been LOCKED OUT PERMANENTLY.\n\nTo unlock this user, remove the \"%s_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $login_username, $domain, sizeof($new_times), $failed_times, $login_username, $login_username), sprintf(_(" --- LOCKED OUT - %s"), $login_username)); } else { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_violated_max_user_logins && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('User "%s" (domain "%s") has attempted (and failed) to log in %d times; %s has been LOCKED OUT for %d minutes', $login_username, $domain, sizeof($new_times), $login_username, $lockout_window)); } l_report_abuse(sprintf(_("NOTICE: User \"%s\" (domain \"%s\") has attempted (and failed) to log in %d times.\n\nTimes:\n%s\n\n%s has been LOCKED OUT for %d minutes.\n\nTo unlock this user before then, remove the \"%s_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $login_username, $domain, sizeof($new_times), $failed_times, $login_username, $lockout_window, $login_username), sprintf(_(" --- LOCKED OUT - %s"), $login_username)); } } sq_change_text_domain('squirrelmail'); } } if (!sqGetGlobalVar('REMOTE_ADDR', $remote_addr, SQ_SERVER)) return; if ($max_login_attempts_per_IP) { list($max_failures, $number_minutes, $lockout_window) = explode(':', $max_login_attempts_per_IP); // get and parse previous login failure stats and add current time to list // // note how we unset the prefs cache flag before/after this code so as not // to corrupt any prefs for the user who is currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $failure_times = getPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_login_failure_times', ''); if ($failure_times) $failure_times = explode(':', $failure_times); else $failure_times = array(); $new_times = array(); foreach ($failure_times as $time) if ($number_minutes == 0 || $time >= $now - ($number_minutes * 60)) $new_times[] = $time; $new_times[] = $now; setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_login_failure_times', implode(':', $new_times)); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); // now check for and ban abusive users // if (sizeof($new_times) >= $max_failures) { if (!$lockout_window) $lockout_end_time = 'PERMANENT'; else $lockout_end_time = $now + ($lockout_window * 60); setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_TOO_MANY_FAILED_LOGIN_ATTEMPTS', $lockout_end_time); // clear prefs cache again // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $failed_times = ''; foreach ($new_times as $time) $failed_times .= date('H:i:s Y-m-d', $time) . "\n"; // send alert to admin if necessary // global $domain, $log_violated_max_IP_logins; sq_change_text_domain('lockout'); if ($number_minutes > 0) { if (!$lockout_window) { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_violated_max_IP_logins && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('Someone at %s has attempted (and failed) to log in %d times in the last %d minutes; %s has been LOCKED OUT PERMANENTLY', $remote_addr, sizeof($new_times), $number_minutes, $remote_addr)); } l_report_abuse(sprintf(_("NOTICE: Someone at %s has attempted (and failed) to log in %d times in the last %d minutes.\n\nTimes:\n%s\n\n%s has been LOCKED OUT PERMANENTLY.\n\nTo unlock this client address, remove the \"%s_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $remote_addr, sizeof($new_times), $number_minutes, $failed_times, $remote_addr, $remote_addr), sprintf(_(" --- LOCKED OUT - %s"), $remote_addr)); } else { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_violated_max_IP_logins && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('Someone at %s has attempted (and failed) to log in %d times in the last %d minutes; %s has been LOCKED OUT for %d minutes', $remote_addr, sizeof($new_times), $number_minutes, $remote_addr, $lockout_window)); } l_report_abuse(sprintf(_("NOTICE: Someone at %s has attempted (and failed) to log in %d times in the last %d minutes.\n\nTimes:\n%s\n\n%s has been LOCKED OUT for %d minutes.\n\nTo unlock this client address, remove the \"%s_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $remote_addr, sizeof($new_times), $number_minutes, $failed_times, $remote_addr, $lockout_window, $remote_addr), sprintf(_(" --- LOCKED OUT - %s"), $remote_addr)); } } else { if (!$lockout_window) { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_violated_max_IP_logins && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('Someone at %s has attempted (and failed) to log in %d times; %s has been LOCKED OUT PERMANENTLY', $remote_addr, sizeof($new_times), $remote_addr)); } l_report_abuse(sprintf(_("NOTICE: Someone at %s has attempted (and failed) to log in %d times.\n\nTimes:\n%s\n\n%s has been LOCKED OUT PERMANENTLY.\n\nTo unlock this client address, remove the \"%s_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $remote_addr, sizeof($new_times), $failed_times, $remote_addr, $remote_addr), sprintf(_(" --- LOCKED OUT - %s"), $remote_addr)); } else { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_violated_max_IP_logins && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('Someone at %s has attempted (and failed) to log in %d times; %s has been LOCKED OUT for %d minutes', $remote_addr, sizeof($new_times), $remote_addr, $lockout_window)); } l_report_abuse(sprintf(_("NOTICE: Someone at %s has attempted (and failed) to log in %d times.\n\nTimes:\n%s\n\n%s has been LOCKED OUT for %d minutes.\n\nTo unlock this client address before then, remove the \"%s_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $remote_addr, sizeof($new_times), $failed_times, $remote_addr, $lockout_window, $remote_addr), sprintf(_(" --- LOCKED OUT - %s"), $remote_addr)); } } sq_change_text_domain('squirrelmail'); } } if ($activate_CAPTCHA_after_failed_attempts) { list($max_failures, $number_minutes, $lockout_window) = explode(':', $activate_CAPTCHA_after_failed_attempts); // get and parse previous login failure stats and add current time to list // // note how we unset the prefs cache flag before/after this code so as not // to corrupt any prefs for the user who is currently logging in // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $failure_times = getPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_CAPTCHA_login_failure_times', ''); if ($failure_times) $failure_times = explode(':', $failure_times); else $failure_times = array(); $new_times = array(); foreach ($failure_times as $time) if ($number_minutes == 0 || $time >= $now - ($number_minutes * 60)) $new_times[] = $time; $new_times[] = $now; setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_CAPTCHA_login_failure_times', implode(':', $new_times)); $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); // now check for and ban abusive users // if (sizeof($new_times) >= $max_failures) { if (!$lockout_window) $lockout_end_time = 'PERMANENT'; else $lockout_end_time = $now + ($lockout_window * 60); setPref($data_dir, 'lockout_plugin_login_failure_information', $remote_addr . '_CAPTCHA_TOO_MANY_FAILED_LOGIN_ATTEMPTS', $lockout_end_time); // clear prefs cache again // $prefs_are_cached = FALSE; $prefs_cache = FALSE; sqsession_unregister('prefs_are_cached'); sqsession_unregister('prefs_cache'); $failed_times = ''; foreach ($new_times as $time) $failed_times .= date('H:i:s Y-m-d', $time) . "\n"; // log event and send alert to admin if necessary // global $domain, $log_CAPTCHA_enabled; sq_change_text_domain('lockout'); if ($number_minutes > 0) { if (!$lockout_window) { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_CAPTCHA_enabled && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('Someone at %s has attempted (and failed) to log in %d times in the last %d minutes; users attempting to log in from %s are PERMANENTLY required to enter a CAPTCHA code when logging in', $remote_addr, sizeof($new_times), $number_minutes, $remote_addr)); } l_report_abuse(sprintf(_("NOTICE: Someone at %s has attempted (and failed) to log in %d times in the last %d minutes.\n\nTimes:\n%s\n\nUsers attempting to log in from %s are PERMANENTLY required to enter a CAPTCHA code when logging in.\n\nTo remove the CAPTCHA requirement, remove the \"%s_CAPTCHA_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $remote_addr, sizeof($new_times), $number_minutes, $failed_times, $remote_addr, $remote_addr), sprintf(_(" --- LOCKED OUT - %s"), $remote_addr)); } else { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_CAPTCHA_enabled && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('Someone at %s has attempted (and failed) to log in %d times in the last %d minutes; users attempting to log in from %s will be required to enter a CAPTCHA code when logging in for the next %d minutes', $remote_addr, sizeof($new_times), $number_minutes, $remote_addr, $lockout_window)); } l_report_abuse(sprintf(_("NOTICE: Someone at %s has attempted (and failed) to log in %d times in the last %d minutes.\n\nTimes:\n%s\n\nUsers attempting to log in from %s will be required to enter a CAPTCHA code when logging in for the next %d minutes.\n\nTo remove the CAPTCHA requirement, remove the \"%s_CAPTCHA_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $remote_addr, sizeof($new_times), $number_minutes, $failed_times, $remote_addr, $lockout_window, $remote_addr), sprintf(_(" --- LOCKED OUT - %s"), $remote_addr)); } } else { if (!$lockout_window) { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_CAPTCHA_enabled && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('Someone at %s has attempted (and failed) to log in %d times; users attempting to log in from %s are PERMANENTLY required to enter a CAPTCHA code when logging in', $remote_addr, sizeof($new_times), $remote_addr)); } l_report_abuse(sprintf(_("NOTICE: Someone at %s has attempted (and failed) to log in %d times.\n\nTimes:\n%s\n\nUsers attempting to log in from %s are PERMANENTLY required to enter a CAPTCHA code when logging in.\n\nTo remove the CAPTCHA requirement, remove the \"%s_CAPTCHA_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $remote_addr, sizeof($new_times), $failed_times, $remote_addr, $remote_addr), sprintf(_(" --- LOCKED OUT - %s"), $remote_addr)); } else { // log this event - note that to use this, you'll need to have // the Squirrel Logger plugin installed and activated and you'll // have to add a "LOCKOUT" event type to its configuration // if ($log_CAPTCHA_enabled && is_plugin_enabled('squirrel_logger')) { include_once(SM_PATH . 'plugins/squirrel_logger/functions.php'); sl_logit('LOCKOUT', sprintf('Someone at %s has attempted (and failed) to log in %d times; users attempting to log in from %s will be required to enter a CAPTCHA code when logging in for the next %d minutes', $remote_addr, sizeof($new_times), $remote_addr, $lockout_window)); } l_report_abuse(sprintf(_("NOTICE: Someone at %s has attempted (and failed) to log in %d times.\n\nTimes:\n%s\n\nUsers attempting to log in from %s will be required to enter a CAPTCHA code when logging in for the next %d minutes.\n\nTo remove the CAPTCHA requirement before then, remove the \"%s_CAPTCHA_TOO_MANY_FAILED_LOGIN_ATTEMPTS\" setting from the \"lockout_plugin_login_failure_information\" preference set."), $remote_addr, sizeof($new_times), $failed_times, $remote_addr, $lockout_window, $remote_addr), sprintf(_(" --- LOCKED OUT - %s"), $remote_addr)); } } sq_change_text_domain('squirrelmail'); } } } /** * Reports abuse to administrator * * @param $error string Error text indicating what the abuse problem is * @param $title string Short text for inclusion in email subject line * */ function l_report_abuse($error, $title='') { global $lockout_notification_addresses, $at, $domain, $login_username, $lockout_useSendmail, $lockout_smtpServerAddress, $lockout_smtpPort, $lockout_sendmail_path, $lockout_sendmail_args, $lockout_pop_before_smtp, $lockout_encode_header_key, $lockout_smtp_auth_mech, $lockout_smtp_sitewide_user, $lockout_smtp_sitewide_pass, $useSendmail, $smtpServerAddress, $smtpPort, $sendmail_path, $sendmail_args, $pop_before_smtp, $encode_header_key, $smtp_auth_mech, $smtp_sitewide_user, $smtp_sitewide_pass; lockout_init(); if (empty($lockout_notification_addresses)) return; sq_change_text_domain('lockout'); $user = substr($login_username, 0, strpos($login_username, $at)); $body = $error . "\n\n" . _("User account:") . ' ' . $login_username . "\n" . _("Domain:") . ' ' . $domain . "\n" . _("Timestamp: ") . date('Y-m-d H:i:s'); $subject = _("[POSSIBLE BRUTEFORCE ABUSE]") . (empty($title) ? '' : ': ' . $title) . ' (' . $login_username . ')'; sq_change_text_domain('squirrelmail'); // override any SMTP/Sendmail settings... // $orig_useSendmail = $useSendmail; if (!is_null($lockout_useSendmail)) $useSendmail = $lockout_useSendmail; $orig_smtpServerAddress = $smtpServerAddress; if (!is_null($lockout_smtpServerAddress)) $smtpServerAddress = $lockout_smtpServerAddress; $orig_smtpPort = $smtpPort; if (!is_null($lockout_smtpPort)) $smtpPort = $lockout_smtpPort; $orig_sendmail_path = $sendmail_path; if (!is_null($lockout_sendmail_path)) $sendmail_path = $lockout_sendmail_path; $orig_sendmail_args = $sendmail_args; if (!is_null($lockout_sendmail_args)) $sendmail_args = $lockout_sendmail_args; $orig_pop_before_smtp = $pop_before_smtp; if (!is_null($lockout_pop_before_smtp)) $pop_before_smtp = $lockout_pop_before_smtp; $orig_encode_header_key = $encode_header_key; if (!is_null($lockout_encode_header_key)) $encode_header_key = $lockout_encode_header_key; $orig_smtp_auth_mech = $smtp_auth_mech; if (!is_null($lockout_smtp_auth_mech)) $smtp_auth_mech = $lockout_smtp_auth_mech; $orig_smtp_sitewide_user = $smtp_sitewide_user; if (!is_null($lockout_smtp_sitewide_user)) $smtp_sitewide_user = $lockout_smtp_sitewide_user; $orig_smtp_sitewide_pass = $smtp_sitewide_pass; if (!is_null($lockout_smtp_sitewide_pass)) $smtp_sitewide_pass = $lockout_smtp_sitewide_pass; // function found in SM core 1.5.2+ and Compatibility plugin 2.0.11+ // (suppress include error, since file is only needed for 1.5.2+, // otherwise Compatibility has us covered) // @include_once(SM_PATH . 'functions/compose.php'); $success = sq_send_mail($lockout_notification_addresses, $subject, $body, 'noreply@' . $domain); // return SMTP/Sendmail settings to what they were // $useSendmail = $orig_useSendmail; $smtpServerAddress = $orig_smtpServerAddress; $smtpPort = $orig_smtpPort; $sendmail_path = $orig_sendmail_path; $sendmail_args = $orig_sendmail_args; $pop_before_smtp = $orig_pop_before_smtp; $encode_header_key = $orig_encode_header_key; $smtp_auth_mech = $orig_smtp_auth_mech; $smtp_sitewide_user = $orig_smtp_sitewide_user; $smtp_sitewide_pass = $orig_smtp_sitewide_pass; }