4 This software is published under the same license as NucleusCMS, namely
5 the GNU General Public License. See http://www.gnu.org/licenses/gpl.html for
6 details about the conditions of this license.
8 In general, this program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2 of the License, or (at your option) any
13 This program is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 class NP_SecurityEnforcer extends NucleusPlugin {
19 function getName() { return 'SecurityEnforcer'; }
21 function getAuthor() { return 'Frank Truscott'; }
23 function getURL() { return 'http://revcetera.com/ftruscot'; }
25 function getVersion() { return '1.0'; }
27 function getDescription() {
28 return _SECURITYENFORCER_DESCRIPTION;
31 function getMinNucleusVersion() { return 350; }
33 function supportsFeature($what) {
35 case 'SqlTablePrefix':
44 function getTableList() { return array(sql_table('plug_securityenforcer')); }
45 function getEventList() { return array('QuickMenu','PrePasswordSet','CustomLogin','LoginSuccess','LoginFailed'); }
50 // Need to make some options
51 $this->createOption('quickmenu', _SECURITYENFORCER_OPT_QUICKMENU, 'yesno', 'yes');
52 $this->createOption('del_uninstall_data', _SECURITYENFORCER_OPT_DEL_UNINSTALL_DATA, 'yesno','no');
53 $this->createOption('enable_security', _SECURITYENFORCER_OPT_ENABLE, 'yesno','yes');
54 $this->createOption('pwd_min_length', _SECURITYENFORCER_OPT_PWD_MIN_LENGTH, 'text','8');
55 $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');
56 $this->createOption('max_failed_login', _SECURITYENFORCER_OPT_MAX_FAILED_LOGIN, 'text', '5');
57 $this->createOption('login_lockout', _SECURITYENFORCER_OPT_LOGIN_LOCKOUT, 'text', '15');
59 // create needed tables
60 sql_query("CREATE TABLE IF NOT EXISTS ". sql_table('plug_securityenforcer').
63 `fails` int(11) NOT NULL default '0',
64 `lastfail` bigint NOT NULL default '0',
65 KEY `login` (`login`)) TYPE=MyISAM");
69 function unInstall() {
70 // if requested, delete the data table
71 if ($this->getOption('del_uninstall_data') == 'yes') {
72 sql_query('DROP TABLE '.sql_table('plug_securityenforcer'));
77 // include language file for this plugin
78 // $language = ereg_replace( '[\\|/]', '', getLanguageName());
79 $language = preg_replace( '@\\|/@', '', getLanguageName());
80 if (file_exists($this->getDirectory().$language.'.php'))
81 include_once($this->getDirectory().$language.'.php');
83 include_once($this->getDirectory().'english.php');
85 $this->enable_security = $this->getOption('enable_security');
86 $this->pwd_min_length = intval($this->getOption('pwd_min_length'));
87 $this->pwd_complexity = intval($this->getOption('pwd_complexity'));
88 $this->max_failed_login = intval($this->getOption('max_failed_login'));
89 $this->login_lockout = intval($this->getOption('login_lockout'));
91 function hasAdminArea() { return 1; }
93 function event_QuickMenu($data) {
94 // only show when option enabled
96 if ($this->getOption('quickmenu') != 'yes' || !$member->isAdmin()) return;
98 if (!($member->isLoggedIn())) return;
99 array_push($data['options'],
100 array('title' => 'Security Enforcer',
101 'url' => $this->getAdminURL(),
102 'tooltip' => _SECURITYENFORCER_ADMIN_TOOLTIP));
105 function event_PrePasswordSet($data) {
106 //password, errormessage, valid
107 if ($this->enable_security == 'yes') {
108 if (!$this->_validate_passwd($data['password'],$this->pwd_min_length, $this->pwd_complexity)) {
109 $data['errormessage'] = _SECURITYENFORCER_INSUFFICIENT_COMPLEXITY;
110 $data['errormessage'] .= _SECURITYENFORCER_MIN_PWD_LENGTH . $this->pwd_min_length;
111 $data['errormessage'] .= _SECURITYENFORCER_PWD_COMPLEXITY . $this->pwd_complexity . "<br /><br />\n";
112 $data['valid'] = false;
117 function event_CustomLogin($data) {
118 //login,password,success,allowlocal
119 if ($this->enable_security == 'yes' && $this->max_failed_login > 0) {
121 $login = $data['login'];
122 $ip = $_SERVER['REMOTE_ADDR'];
123 sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE lastfail < ".(time() - ($this->login_lockout * 60)));
124 $query = "SELECT fails as result FROM ".sql_table('plug_securityenforcer')." ";
125 // $query .= "WHERE login='".addslashes($login)."'";
126 $query .= "WHERE login='".sql_real_escape_string($login)."'";
127 $flogin = quickQuery($query);
128 $query = "SELECT fails as result FROM ".sql_table('plug_securityenforcer')." ";
129 // $query .= "WHERE login='".addslashes($ip)."'";
130 $query .= "WHERE login='".sql_real_escape_string($ip)."'";
131 $fip = quickQuery($query);
132 if ($flogin >= $this->max_failed_login || $fip >= $this->max_failed_login) {
133 $data['success'] = 0;
134 $data['allowlocal'] = 0;
135 // ACTIONLOG::add(INFO, 'login disallowed by NP_SecurityEnforcer. login: '.htmlentities($login).', ip: '.htmlentities($ip) );
136 $info = sprintf(_SECURITYENFORCER_LOGIN_DISALLOWED, htmlspecialchars($login), htmlspecialchars($ip));
137 ACTIONLOG::add(INFO, $info);
142 function event_LoginSuccess($data) {
143 //member(obj),username
144 if ($this->enable_security == 'yes' && $this->max_failed_login > 0) {
146 $login = $data['username'];
147 $ip = $_SERVER['REMOTE_ADDR'];
148 // sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE login='".addslashes($login)."'");
149 sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($login)."'");
150 // sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE login='".addslashes($ip)."'");
151 sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($ip)."'");
155 function event_LoginFailed($data) {
157 if ($this->enable_security == 'yes' && $this->max_failed_login > 0) {
159 $login = $data['username'];
160 $ip = $_SERVER['REMOTE_ADDR'];
161 //sql_table('plug_securityenforcer')
162 // $lres = sql_query("SELECT * FROM ".sql_table('plug_securityenforcer')." WHERE login='".addslashes($login)."'");
163 $lres = sql_query("SELECT * FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($login)."'");
164 if (sql_num_rows($lres)) {
165 // sql_query("UPDATE ".sql_table('plug_securityenforcer')." SET fails=fails+1, lastfail=".time()." WHERE login='".addslashes($login)."'");
166 sql_query("UPDATE ".sql_table('plug_securityenforcer')." SET fails=fails+1, lastfail=".time()." WHERE login='".sql_real_escape_string($login)."'");
169 // sql_query("INSERT INTO ".sql_table('plug_securityenforcer')." (login,fails,lastfail) VALUES ('".addslashes($login)."',1,".time().")");
170 sql_query("INSERT INTO ".sql_table('plug_securityenforcer')." (login,fails,lastfail) VALUES ('".sql_real_escape_string($login)."',1,".time().")");
172 $lres = sql_query("SELECT * FROM ".sql_table('plug_securityenforcer')." WHERE login='".addslashes($ip)."'");
173 if (sql_num_rows($lres)) {
174 // sql_query("UPDATE ".sql_table('plug_securityenforcer')." SET fails=fails+1, lastfail=".time()." WHERE login='".addslashes($ip)."'");
175 sql_query("UPDATE ".sql_table('plug_securityenforcer')." SET fails=fails+1, lastfail=".time()." WHERE login='".sql_real_escape_string($ip)."'");
178 // sql_query("INSERT INTO ".sql_table('plug_securityenforcer')." (login,fails,lastfail) VALUES ('".addslashes($ip)."',1,".time().")");
179 sql_query("INSERT INTO ".sql_table('plug_securityenforcer')." (login,fails,lastfail) VALUES ('".sql_real_escape_string($ip)."',1,".time().")");
184 /* Helper Functions */
186 function _validate_passwd($passwd,$minlength = 6,$complexity = 0) {
187 $minlength = intval($minlength);
188 $complexity = intval($complexity);
190 if ($minlength < 6 ) $minlength = 6;
191 if (strlen($passwd) < $minlength) return false;
193 if ($complexity > 4) $complexity = 4;
197 $ochars = "[-~!@#$%^&*()_+=,.<>?:;|]";
198 $chartypes = array($ucchars, $lcchars, $numchars, $ochars);
199 $tot = array(0,0,0,0);
201 foreach ($chartypes as $value) {
202 $tot[$i] = preg_match("/".$value."/", $passwd);
206 if (array_sum($tot) >= $complexity) return true;