createOption('quickmenu', '_SECURITYENFORCER_OPT_QUICKMENU', 'yesno', 'yes'); $this->createOption('del_uninstall_data', '_SECURITYENFORCER_OPT_DEL_UNINSTALL_DATA', 'yesno','no'); $this->createOption('enable_security', '_SECURITYENFORCER_OPT_ENABLE', 'yesno','yes'); $this->createOption('pwd_min_length', '_SECURITYENFORCER_OPT_PWD_MIN_LENGTH', 'text','8'); $this->createOption('pwd_complexity', '_SECURITYENFORCER_OPT_PWD_COMPLEXITY', 'select','0', '_SECURITYENFORCER_OPT_SELECT_OFF_COMP|0|_SECURITYENFORCER_OPT_SELECT_ONE_COMP|1|_SECURITYENFORCER_OPT_SELECT_TWO_COMP|2|_SECURITYENFORCER_OPT_SELECT_THREE_COMP|3|_SECURITYENFORCER_OPT_SELECT_FOUR_COMP|4;datatype=numerical'); $this->createOption('max_failed_login', '_SECURITYENFORCER_OPT_MAX_FAILED_LOGIN', 'text', '5'); $this->createOption('login_lockout', '_SECURITYENFORCER_OPT_LOGIN_LOCKOUT', 'text', '15'); // create needed tables DB::execute('CREATE TABLE IF NOT EXISTS '. sql_table('plug_securityenforcer'). " (login varchar(255), fails int(11) NOT NULL default '0', lastfail bigint NOT NULL default '0', KEY login (login)) ENGINE=MyISAM"); return; } public function unInstall() { // if requested, delete the data table if ( $this->getOption('del_uninstall_data') == 'yes' ) { DB::execute('DROP TABLE '.sql_table('plug_securityenforcer')); } return; } public function init() { // include translation file for this plugin if ( file_exists($this->getDirectory() . i18n::get_current_locale() . '.' . i18n::get_current_charset() . '.php') ) { include_once($this->getDirectory() . i18n::get_current_locale() . '.' . i18n::get_current_charset() . '.php'); } else { include_once($this->getDirectory().'en_Latn_US.UTF-8.php'); } $this->enable_security = $this->getOption('enable_security'); $this->pwd_min_length = (integer) $this->getOption('pwd_min_length'); $this->pwd_complexity = (integer) $this->getOption('pwd_complexity'); $this->max_failed_login = (integer) $this->getOption('max_failed_login'); $this->login_lockout = (integer) $this->getOption('login_lockout'); return; } public function event_QuickMenu($data) { // only show when option enabled global $member; if ( $this->getOption('quickmenu') != 'yes' || !$member->isAdmin() ) { return; } //global $member; if ( !($member->isLoggedIn()) ) { return; } array_push($data['options'], array('title' => 'Security Enforcer', 'url' => $this->getAdminURL(), 'tooltip' => _SECURITYENFORCER_ADMIN_TOOLTIP)); return; } public function event_PrePasswordSet($data) { //password, errormessage, valid if ( $this->enable_security == 'no' ) { return; } $password = $data['password']; // conditional below not needed in 3.60 or higher. Used to keep from setting off error when password not being changed if ( postVar('action') == 'changemembersettings' ) { $emptyAllowed = true; } else { $emptyAllowed = false; } if ( (!$emptyAllowed)||$password ) { $message = $this->validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity); if ( $message ) { $data['errormessage'] = _SECURITYENFORCER_INSUFFICIENT_COMPLEXITY . $message. "

\n"; $data['valid'] = false; } } return; } public function event_PostRegister($data) { if ( $this->enable_security != 'yes' ) { return; } $password = postVar('password'); if( postVar('action') == 'memberadd' ) { $message = $this->validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity); if ( $message ) { $errormessage = _SECURITYENFORCER_ACCOUNT_CREATED. $message. "

\n"; global $admin; $admin->error($errormessage); } } return; } public function event_CustomLogin($data) { if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 ) { return; } //login,password,success,allowlocal global $_SERVER; $login = $data['login']; $ip = $_SERVER['REMOTE_ADDR']; $query = "DELETE FROM %s WHERE lastfail < %d;"; $query = sprintf($query, sql_table('plug_securityenforcer'), (integer) (time() - ($this->login_lockout * 60))); DB::execute($query); $query = "SELECT fails as result FROM %s WHERE login=%s;"; $query = sprintf($query, sql_table('plug_securityenforcer'), DB::quoteValue($login)); $flogin = DB::getValue($query); $query = "SELECT fails as result FROM %s WHERE login=%s;"; $query = sprintf($query, sql_table('plug_securityenforcer'), DB::quoteValue($ip)); $fip = DB::getValue($query); if ( $flogin >= $this->max_failed_login || $fip >= $this->max_failed_login ) { $data['success'] = 0; $data['allowlocal'] = 0; $info = sprintf(_SECURITYENFORCER_LOGIN_DISALLOWED, Entity::hsc($login), Entity::hsc($ip)); ActionLog::add(INFO, $info); } return; } public function event_LoginSuccess($data) { //member(obj),username if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 ) { return; } global $_SERVER; $login = $data['username']; $ip = $_SERVER['REMOTE_ADDR']; DB::execute('DELETE FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($login)); DB::execute('DELETE FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($ip)); return; } public function event_LoginFailed($data) { //username if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 ) { return; } global $_SERVER; $login = $data['username']; $ip = $_SERVER['REMOTE_ADDR']; $lres = DB::getResult('SELECT * FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($login)); if ( $lres->rowCount() > 0 ) { DB::execute('UPDATE ' . sql_table('plug_securityenforcer') . ' SET fails=fails+1, lastfail=' . time() . ' WHERE login=' . DB::quoteValue($login)); } else { DB::execute('INSERT INTO ' . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($login) . ',1,' . time() . ')'); } $lres = DB::getResult('SELECT * FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($ip)); if ( $lres->rowCount() > 0 ) { DB::execute('UPDATE ' . sql_table('plug_securityenforcer') . ' SET fails=fails+1, lastfail=' . time() . ' WHERE login=' . DB::quoteValue($ip)); } else { DB::execute('INSERT INTO ' . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($ip) . ',1,' . time() . ')'); } return; } private function validate_and_messsage($passwd,$minlength = 6,$complexity = 0) { $minlength = intval($minlength); $complexity = intval($complexity); if ( $minlength < 6 ) { $minlength = 6; } if ( i18n::strlen($passwd) < $minlength ) { $message = _SECURITYENFORCER_MIN_PWD_LENGTH . $this->pwd_min_length; } if ( $complexity > 4 ) { $complexity = 4; } $ucchars = '[A-Z]'; $lcchars = '[a-z]'; $numchars = '[0-9]'; $ochars = '[-~!@#\$%^&*()_+=,.<>?:;|]'; $chartypes = array($ucchars, $lcchars, $numchars, $ochars); $tot = array(0,0,0,0); $i = 0; foreach ( $chartypes as $value ) { $tot[$i] = preg_match("#" . $value . "#", $passwd); $i = $i + 1; } if ( array_sum($tot) < $complexity ) { $message .= _SECURITYENFORCER_PWD_COMPLEXITY . $this->pwd_complexity; } return $message; } }