lockout/0000755000000000000000000000000011464464512011243 5ustar rootrootlockout/functions.php0000644000000000000000000013366011464464205013774 0ustar rootroot * 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+(:\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: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', ''); // immediately deactivate CAPTCHA validation if configured // $activate_CAPTCHA_config = explode(':', $activate_CAPTCHA_after_failed_attempts); if (!empty($activate_CAPTCHA_config[3])) setPref($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'); } } /** * 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; } lockout/locked_out.php0000644000000000000000000000115411464444220014077 0ustar rootroot

Please Contact Your System Administrator"); // TODO: this can probably be fixed; just load up the same things that // get loaded in the login.php script, but this isn't high priority // because the string in this file will probably be customized by // the administrator anyway. ?> Access Denied
Please Contact Your System Administrator

lockout/docs/0000755000000000000000000000000011464464146012176 5ustar rootrootlockout/docs/index.php0000644000000000000000000000073111464444553014017 0ustar rootroot Description =========== This plugin allows you to create a list of users and/or domains that should be disallowed (or allowed) login access to SquirrelMail. It also allows you to block brute-force password guessing attacks, although please note that this will ONLY help fight such attacks in the SquirrelMail interface, and should really be implemented in your mail system's authentication backend. Tips on implementing this kind of security measure in the mail server's authentication backend: For Unix-like systems, there is a high probability that your backend authentication method is PAM. For PAM-based authentication, look at the pam_tally or pam_lockout add-on modules, both of which will prevent this kind of attack in a much more robust manner. For OpenLDAP-based authentication, you would set ppolicy_use_lockout and related configuration parameters (all the ppolicy parameters are password related) in the openldap configuration. License ======= This plugin is released under the GNU General Public License (see COPYING for details). Donations ========= If you or your company make regular use of this software, please consider supporting Open Source development by donating to the authors or inquire about hiring them to consult on other projects. Donation links for the author(s) are as follows: Paul Lesniewski: https://sourceforge.net/donate/index.php?user_id=508228 Requirements ============ * SquirrelMail version 1.4.1 or above * Compatibility plugin version 2.0.11 or above Security Considerations ======================= When configuring IP-based blacklisting with $max_login_attempts_per_IP, username-based blacklisting with $max_login_attempts, or adding a CAPTCHA requirement with $activate_CAPTCHA_after_failed_attempts, you should carefully consider your users' habits and the kinds of potential attacks you are trying to mitigate. The most risk-free of these features is to configure the CAPTCHA plugin to activate after a certain number of failed login attempts. This makes SquirrelMail somewhat harder to attack in an automated fashion while not creating any need for the administrator to have to manually unlock any account or IP address. If you use a very strong CAPTCHA backend (beware, however, that most CAPTCHA implementations are not very strong) and are only fighting off an *automated* attack, this step alone may suffice. Adding IP-based blacklisting is effective at blocking an attacker who is trying multiple user account names as long as it is triggered at a reasonably high level of login failures (the threshold should be especially high if you have a lot of users who log in from a small number of client addresses). Username-based blacklisting could be useful when an attacker is targetting a known user or small group thereof, although because locking the user's account is also inconvenient to the legitimate user, IP-based blacklisting will usually still be a better solution. However, if this feature is set with a fairly high threshold (higher than all others), it can be used to halt someone who consistently attacks the same account from a range of IP addresses. After triggering a lockdown on such an account, it might be prudent to change the username or take other (real world?) measures. See the configuration file for more information about how to configure each of the settings discussed above. If your SquirrelMail installation resides behind a proxy server that sends X_FORWARDED_* headers, you should turn on $obey_x_forwarded_headers, but in all other environments, please leave this setting off, otherwise an attacker might be able to thwart a domain lockout rule by tampering with the request headers. In the same vien, if a client is able to forge the regular HTTP_HOST request header, domain-based lockout rules defined in the lockout table may be compromised. The only solution for this kind of problem beside using user-based lockout rules or enforcing login restrictions in the IMAP server (which is ALWAYS more robust than implementing them in the mail client) is to make sure that your usernames are in the format of a full email address (that is, that they always carry a domain name in them, and are not, for instance, in the same format as local account usernames). Configuration ============= Please copy the sample configuration (config.sample.php) file in this plugin's data sub-directory to your own config.php and then edit it as necessary. If you choose to enable the $use_lockout_rules setting, you must also copy the sample lockout table (lockout_table.sample.php) in the data sub-directory into your own lockout_table.php and edit it as follows: Users and domains can be indicated using wildcards and can each have their own page that will be displayed when they are locked out. See the sample lockout_table.sample.php file in the data directory for help building this table. You may want to edit the file(s) you have indicated as the target lockout page to be displayed in order to customize the message that will be seen when someone is locked out, or send them to a special web page elsewhere or redirect them to the standard SquirrelMail "bad username or password" error page if you desire this plugin to be more stealthy. Note that this will typically catch locked out users no matter which of your domains they attempt to log in through. Note also that if you enable reverse lockout functionality, the users and domains listed in the lockout table will all be ALLOWED to log in, all OTHERS will be locked out. Troubleshooting =============== * If you have some SMTP authentication method configured in the main SquirrelMail configuration (or perhaps POP before SMTP is turned on) and administrative alert emails are not getting sent when a user is locked out, this is because the SMTP authentication credentials are not yet available before a user has logged in, so you need to provide administrative credentials for sending such emails. See $lockout_smtp_auth_mech (and related settings) in the configuration file. Help Requests ============= Before looking for help elsewhere, please try to help yourself: * Read the Troubleshooting section herein. * Look to see if others have already asked about the same issue. There are tips and links for the best places to do this in the SquirrelMail mailing list posting guidelines: http://squirrelmail.org/wiki/MailingListPostingGuidelines You should also try Google or some other search engine. * If you cannot find any information about your issue, please first mail your help request to the squirrelmail-plugins mailing list. Information about it can be found here: http://lists.sourceforge.net/mailman/listinfo/squirrelmail-plugins You MUST read the mailing list posting guidelines (see above) and include as much information about your issue (and your system) as possible. Including configtest output, any debug output, the plugin configuration settings you've made and anything else you can think of to make it easier to diagnose your problem will get you the most useful responses. Inquiries that do not comply with the posting guidelines are liable to be ignored. * If you don't get any replies on the mailing list, you are welcome to send a help request to the authors' personal address(es), but please be patient with the mailing list. Change Log ========== v1.7 2010/11/11 Paul Lesniewski * Added a configuration setting to $activate_CAPTCHA_after_failed_attempts that allows immediate CAPTCHA deactivation upon a successful login v1.6 2008/04/01 Paul Lesniewski * Allow overrides of SquirrelMail SMTP/Sendmail settings when sending administrative alert emails v1.5 2008/03/11 Paul Lesniewski * Updated to use sq_send_mail for notification messages * Added ability to log lockout events in Squirrel Logger plugin * Remove use of login_top hook v1.4.1 2007/08/24 Paul Lesniewski * Very small change due to change in SM 1.5.2; does not affect plugin functionality - no upgrade needed for anyone who does not want to bother v1.4 2007/07/12 Paul Lesniewski * Added ability to disable accounts that have too many successive login failures, including optional administrative alert email * Added IP-based blacklisting * Added ability to enable the CAPTCHA plugin for IP addresses that have too many successive login failures * Move lockout check to hook that occurs BEFORE user is actually logged in * Updated for compatibility with SM 1.5.2+ * Updated for use with new Compatibility plugin * Miscellaneous cleanup * Security considerations/audit thanks to Ben at reCAPTCHA.net v1.3 2004/02/05 Paul Lesniewski * Added abilty to reverse lockout functionality (lock out everyone *except* those listed in the lockout table) v1.2 2004/01/05 Paul Lesniewski * Fixed typo in INSTALL v1.1 2003/09/05 Paul Lesniewski * Added ability to redirect to another web page or to the standard SquirrelMail "bad username or password" page (with simulated bad login delay) v1.0 2003/07/05 Paul Lesniewski * Initial release Future Work =========== * Ideas? lockout/index.php0000644000000000000000000000073111464444545013070 0ustar rootroot SquirrelMail Lockout Plugin Translation File # Copyright (c) 2003-2010 The Squirrelmail Development Team # This file is distributed under the same license as the SquirrelMail package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: lockout\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2007-04-10 20:10-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: SquirrelMail Language Team \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "Access denied. Please contact your system administrator." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid " --- LOCKED OUT - %s" msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" #, c-format msgid "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." msgstr "" msgid "User account:" msgstr "" msgid "Domain:" msgstr "" msgid "Timestamp: " msgstr "" msgid "[POSSIBLE BRUTEFORCE ABUSE]" msgstr "" lockout/locale/getpot0000744000000000000000000000056110653504601013723 0ustar rootroot#!/bin/sh XGETTEXT_OPTIONS="--keyword=_ -keyword=N_ --default-domain=lockout --no-location" # Allows controlling language option # (gettext v.0.10.40 = -C, gettext 0.11+ = -L php). if [ $SM_OLD_GETTEXT ] ; then XGETTEXT_OPTIONS="${XGETTEXT_OPTIONS} -C"; else XGETTEXT_OPTIONS="${XGETTEXT_OPTIONS} -L php"; fi xgettext ${XGETTEXT_OPTIONS} *.php --output=lockout.pot lockout/version0000644000000000000000000000001411464464512012646 0ustar rootrootLockout 1.7 lockout/data/0000755000000000000000000000000011464464274012161 5ustar rootrootlockout/data/index.php0000644000000000000000000000073111464444550013775 0ustar rootroot * Licensed under the GNU GPL. For full terms see the file COPYING. * * @package plugins * @subpackage lockout * */ /** * Register this plugin with SquirrelMail * */ function squirrelmail_plugin_init_lockout() { global $squirrelmail_plugin_hooks; $squirrelmail_plugin_hooks['configtest']['lockout'] = 'lockout_configtest'; $squirrelmail_plugin_hooks['logout_error']['lockout'] = 'check_failed_login_count'; $squirrelmail_plugin_hooks['login_verified']['lockout'] = 'reset_failed_login_count'; $squirrelmail_plugin_hooks['prefs_backend']['lockout'] = 'move_lockout_to_end_of_login_before_hook'; $squirrelmail_plugin_hooks['loading_constants']['lockout'] = 'move_lockout_to_end_of_login_before_hook'; $squirrelmail_plugin_hooks['login_before']['lockout'] = 'check_lockout'; $squirrelmail_plugin_hooks['login_cookie']['lockout'] = 'activate_captcha_plugin'; } /** * Returns info about this plugin * */ function lockout_info() { return array( 'english_name' => 'Lockout', 'authors' => array( 'Paul Lesniewski' => array( 'email' => 'paul@squirrelmail.org', 'sm_site_username' => 'pdontthink', ), ), 'version' => '1.7', 'required_sm_version' => '1.4.1', 'requires_configuration' => 1, 'requires_source_patch' => 0, 'summary' => 'Specify a list of users or domains that should be disallowed login access and/or stop brute-force password guessing attacks.', 'details' => 'This plugin allows you to create a list of users and/or domains that should be disallowed login access to SquirrelMail. It also allows you to block brute-force password guessing attacks, although please note that this will ONLY help fight such attacks in the SquirrelMail interface, and should really be implemented in your mail system\'s authentication backend.', 'required_plugins' => array( 'compatibility' => array( 'version' => '2.0.11', 'activate' => FALSE, ) ) ); } /** * Returns version info about this plugin * */ function lockout_version() { $info = lockout_info(); return $info['version']; } /** * Validate that this plugin is configured correctly * */ function lockout_configtest() { include_once(SM_PATH . 'plugins/lockout/functions.php'); lockout_configtest_do(); } /** * 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($args) { include_once(SM_PATH . 'plugins/lockout/functions.php'); check_lockout_do($args); } /** * Check number of successive failed login attempts * and block user permanently if necessary * */ function check_failed_login_count($args) { include_once(SM_PATH . 'plugins/lockout/functions.php'); check_failed_login_count_do($args); } /** * Valid login - reset failure count * */ function reset_failed_login_count() { include_once(SM_PATH . 'plugins/lockout/functions.php'); reset_failed_login_count_do(); } /** * Make sure this plugin fires the LAST on the login_before * hook * */ function move_lockout_to_end_of_login_before_hook() { include_once(SM_PATH . 'plugins/lockout/functions.php'); move_lockout_to_end_of_login_before_hook_do(); } /** * Activate the CAPTCHA plugin if need be * */ function activate_captcha_plugin($args) { include_once(SM_PATH . 'plugins/lockout/functions.php'); activate_captcha_plugin_do($args); } lockout/README0000644000000000000000000000006511464444033012120 0ustar rootrootPlease see all documentation in the docs/ directory. lockout/make_release.sh0000744000000000000000000001352611464444611014222 0ustar rootroot#!/bin/sh # Generic shell script for building SquirrelMail plugin release # # Copyright (c) 2004-2010 Paul Lesniewski # Licensed under the GNU GPL. For full terms see the file COPYING. # ####################################################### # # CONFIGURATION # # Relative paths to any and all configuration files # for this plugin: these files will NOT be included # in the release package built by this script; they # should be given as relative paths and filenames from # the plugin's own directory - for example, if you # have a config.php file in the main plugin directory # and a special_config.php file in a "data" subdirectory, # this should be set as follows: # # CONFIG_FILES=( config.php data/special_config.php ) # # Note that you can also use this setting to exclude # entire subdirectories while creating the release # package. Here is an example that skips any files # inside a subdirectory called "cache_files" and # completely removes a subdirectory called "tmp", as # well as the standard config.php file: # # CONFIG_FILES=( config.php tmp cache_files/* ) # # CONFIG_FILES=( data/config.php data/lockout_table.php ) # # END CONFIGURATION # ####################################################### # avoid all kinds of potential problems; only allow # this to be run from directory where it resides # if [ "$0" != "./make_release.sh" ]; then echo echo "Please do not run from remote directory" echo exit 1 fi # grab name of package being built from directory name # # PACKAGE=`echo "$PWD" | sed s/.*\\\///` # get "pretty name" from version file # if [ ! -e version ]; then echo echo "No version file found. Please create before making release" echo exit 2 fi PRETTY_NAME=`head -1 version` # announce ourselves # echo echo "Creating Release Package for $PRETTY_NAME" echo # grab old version number straight from the php code # OLD_VERSION=`echo "" | php -q` REQ_SM_VERSION=`echo "" | php -q` # check for the standard files... # if [ ! -e README ]; then echo echo "No README file found. Please create before making release" echo exit 3 fi if [ ! -e docs/README ]; then echo echo "No docs/README file found. Please create before making release" echo exit 3 fi if [ ! -e docs/INSTALL ]; then echo echo "No docs/INSTALL file found. Please create before making release" echo exit 4 fi if [ ! -e locale/getpot ]; then echo echo "No locale/getpot file found. Please create before making release" echo exit 5 fi if [ ! -e locale/$PACKAGE.pot ]; then echo echo "No locale/$PACKAGE.pot file found. Please create before making release" echo exit 5 fi # just copy index.php and COPYING automatically if not found # if [ ! -e docs/COPYING ]; then echo "No docs/COPYING file found. Grabbing one from ../../doc" cp ../../doc/COPYING ./docs/ fi if [ ! -e index.php ]; then echo "No index.php file found. Grabbing one from ../" cp ../index.php . fi if [ ! -e docs/index.php ]; then echo "No docs/index.php file found. Grabbing one from ../" cp ../index.php ./docs/ fi if [ ! -e locale/index.php ]; then echo "No locale/index.php file found. Grabbing one from ../" cp ../index.php ./locale/ fi # Make our own docs/.htaccess and locale/.htaccess if needed # if [ ! -e docs/.htaccess ]; then echo "No docs/.htaccess file found. Creating..." echo "Deny from All" > ./docs/.htaccess fi if [ ! -e locale/.htaccess ]; then echo "No locale/.htaccess file found. Creating..." echo "Deny from All" > ./locale/.htaccess fi # remove any previous tarballs # while test 1; do echo echo -n "Remove all .tar.gz files? (y/[n]): " read REPLY if test -z $REPLY; then REPLY="n" break fi if test $REPLY = "y"; then break fi if test $REPLY = "n"; then break fi done if [ "$REPLY" = "y" ]; then rm -f *.tar.gz fi # get new version number if needed # if [ ! -z "$REQ_SM_VERSION" ] ; then OLD_FULL_VERSION=$OLD_VERSION-$REQ_SM_VERSION else OLD_FULL_VERSION=$OLD_VERSION fi echo read -p "Enter Version Number [$OLD_VERSION]: " VERSION if [ -z "$VERSION" ] ; then VERSION=$OLD_VERSION; # VERSION=$OLD_FULL_VERSION; fi PURE_VERSION=`echo "$VERSION" | sed 's/-.*//'` if [ ! -z "$REQ_SM_VERSION" ] ; then FINAL_VERSION="$PURE_VERSION-$REQ_SM_VERSION" else FINAL_VERSION="$PURE_VERSION" fi # remove tarball we are building if present # echo echo "Removing $PACKAGE-$FINAL_VERSION.tar.gz" rm -f $PACKAGE-$FINAL_VERSION.tar.gz # replace version number in info function in setup.php # NOTE that this requires specific syntax in setup.php # for the _info() function which should be # a line that looks like: # 'version' => '', # if test -e setup.php; then echo "Replacing version in setup.php (info function)" sed -e "s/'version' => '$OLD_VERSION',/'version' => '$PURE_VERSION',/" setup.php > setup.php.tmp mv setup.php.tmp setup.php fi # update version number in version file too # echo "Replacing version in version file" echo "$PRETTY_NAME" > version echo $PURE_VERSION >> version # Build tar command; exclude config and other irrelevant files # TAR_COMMAND="tar -c -z -v --exclude CVS --exclude .svn" J=0 while [ "$J" -lt ${#CONFIG_FILES[@]} ]; do echo "Excluding ${CONFIG_FILES[$J]}" TAR_COMMAND="$TAR_COMMAND --exclude ${CONFIG_FILES[$J]}" J=`expr $J + 1` done TAR_COMMAND="$TAR_COMMAND -f $PACKAGE-$FINAL_VERSION.tar.gz $PACKAGE" # make tarball # echo "Creating $PACKAGE-$FINAL_VERSION.tar.gz" cd ../ $TAR_COMMAND mv $PACKAGE-$FINAL_VERSION.tar.gz $PACKAGE cd $PACKAGE echo echo "Finished" echo