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.
18 * @version $Id: NP_SecurityEnforcer.php 1721 2012-03-31 10:18:25Z sakamocchi $
20 * @version $Id: NP_SecurityEnforcer.php 1874 2012-06-17 07:27:38Z sakamocchi $
21 >>>>>>> skinnable-master
23 class NP_SecurityEnforcer extends NucleusPlugin
25 public function getName()
27 return 'SecurityEnforcer';
30 public function getAuthor()
32 return 'Frank Truscott + Cacher + Mocchi';
35 public function getURL()
37 return 'http://revcetera.com/ftruscot';
40 public function getVersion()
45 public function getDescription()
47 return _SECURITYENFORCER_DESCRIPTION;
50 public function getMinNucleusVersion()
55 public function getTableList()
57 return array(sql_table('plug_securityenforcer'));
60 public function getEventList()
62 return array('QuickMenu','PrePasswordSet','CustomLogin','LoginSuccess','LoginFailed','PostRegister');
65 public function hasAdminArea() {
69 public function supportsFeature($what)
71 if ( $what == 'SqlTablePrefix' )
78 public function install()
82 // Need to make some options
83 $this->createOption('quickmenu', '_SECURITYENFORCER_OPT_QUICKMENU', 'yesno', 'yes');
84 $this->createOption('del_uninstall_data', '_SECURITYENFORCER_OPT_DEL_UNINSTALL_DATA', 'yesno','no');
85 $this->createOption('enable_security', '_SECURITYENFORCER_OPT_ENABLE', 'yesno','yes');
86 $this->createOption('pwd_min_length', '_SECURITYENFORCER_OPT_PWD_MIN_LENGTH', 'text','8');
87 $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');
88 $this->createOption('max_failed_login', '_SECURITYENFORCER_OPT_MAX_FAILED_LOGIN', 'text', '5');
89 $this->createOption('login_lockout', '_SECURITYENFORCER_OPT_LOGIN_LOCKOUT', 'text', '15');
91 // create needed tables
93 DB::execute("CREATE TABLE IF NOT EXISTS ". sql_table('plug_securityenforcer').
95 DB::execute('CREATE TABLE IF NOT EXISTS '. sql_table('plug_securityenforcer').
96 >>>>>>> skinnable-master
97 " (login varchar(255),
98 fails int(11) NOT NULL default '0',
99 lastfail bigint NOT NULL default '0',
100 KEY login (login)) ENGINE=MyISAM");
104 public function unInstall()
106 // if requested, delete the data table
107 if ( $this->getOption('del_uninstall_data') == 'yes' )
109 DB::execute('DROP TABLE '.sql_table('plug_securityenforcer'));
114 public function init()
116 // include translation file for this plugin
117 if ( file_exists($this->getDirectory() . i18n::get_current_locale() . '.' . i18n::get_current_charset() . '.php') )
119 include_once($this->getDirectory() . i18n::get_current_locale() . '.' . i18n::get_current_charset() . '.php');
123 include_once($this->getDirectory().'en_Latn_US.UTF-8.php');
126 $this->enable_security = $this->getOption('enable_security');
127 $this->pwd_min_length = (integer) $this->getOption('pwd_min_length');
128 $this->pwd_complexity = (integer) $this->getOption('pwd_complexity');
129 $this->max_failed_login = (integer) $this->getOption('max_failed_login');
130 $this->login_lockout = (integer) $this->getOption('login_lockout');
134 public function event_QuickMenu(&$data)
136 // only show when option enabled
138 if ( $this->getOption('quickmenu') != 'yes' || !$member->isAdmin() )
143 if ( !($member->isLoggedIn()) )
147 array_push($data['options'],
148 array('title' => 'Security Enforcer',
149 'url' => $this->getAdminURL(),
150 'tooltip' => _SECURITYENFORCER_ADMIN_TOOLTIP));
154 public function event_PrePasswordSet(&$data)
156 //password, errormessage, valid
157 if ( $this->enable_security == 'no' )
161 $password = $data['password'];
162 // conditional below not needed in 3.60 or higher. Used to keep from setting off error when password not being changed
163 if ( postVar('action') == 'changemembersettings' )
165 $emptyAllowed = true;
169 $emptyAllowed = false;
171 if ( (!$emptyAllowed)||$password )
173 $message = $this->validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity);
176 $data['errormessage'] = _SECURITYENFORCER_INSUFFICIENT_COMPLEXITY . $message. "<br /><br />\n";
177 $data['valid'] = false;
183 public function event_PostRegister(&$data)
185 if ( $this->enable_security != 'yes' )
189 $password = postVar('password');
190 if( postVar('action') == 'memberadd' )
192 $message = $this->validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity);
195 $errormessage = _SECURITYENFORCER_ACCOUNT_CREATED. $message. "<br /><br />\n";
197 $admin->error($errormessage);
203 public function event_CustomLogin(&$data)
205 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
210 //login,password,success,allowlocal
212 $login = $data['login'];
213 $ip = $_SERVER['REMOTE_ADDR'];
215 DB::execute("DELETE FROM " . sql_table('plug_securityenforcer') . " WHERE lastfail < " . (time() - ($this->login_lockout * 60)));
216 $query = "SELECT fails as result FROM " . sql_table('plug_securityenforcer') . " ";
217 $query .= 'WHERE login=' . DB::quoteValue($login);
218 $flogin = DB::getValue($query);
219 $query = "SELECT fails as result FROM " . sql_table('plug_securityenforcer') . " ";
220 $query .= 'WHERE login=' . DB::quoteValue($ip);
223 $query = "DELETE FROM %s WHERE lastfail < %d;";
224 $query = sprintf($query, sql_table('plug_securityenforcer'), (integer) (time() - ($this->login_lockout * 60)));
227 $query = "SELECT fails as result FROM %s WHERE login=%s;";
228 $query = sprintf($query, sql_table('plug_securityenforcer'), DB::quoteValue($login));
229 $flogin = DB::getValue($query);
231 $query = "SELECT fails as result FROM %s WHERE login=%s;";
232 $query = sprintf($query, sql_table('plug_securityenforcer'), DB::quoteValue($ip));
233 >>>>>>> skinnable-master
234 $fip = DB::getValue($query);
236 if ( $flogin >= $this->max_failed_login || $fip >= $this->max_failed_login )
238 $data['success'] = 0;
239 $data['allowlocal'] = 0;
240 $info = sprintf(_SECURITYENFORCER_LOGIN_DISALLOWED, Entity::hsc($login), Entity::hsc($ip));
241 ActionLog::add(INFO, $info);
246 public function event_LoginSuccess(&$data)
248 //member(obj),username
249 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
254 $login = $data['username'];
255 $ip = $_SERVER['REMOTE_ADDR'];
257 DB::execute("DELETE FROM " . sql_table('plug_securityenforcer') . " WHERE login=" . DB::quoteValue($login));
258 DB::execute("DELETE FROM " . sql_table('plug_securityenforcer') . " WHERE login=" . DB::quoteValue($ip));
260 DB::execute('DELETE FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($login));
261 DB::execute('DELETE FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($ip));
262 >>>>>>> skinnable-master
266 public function event_LoginFailed(&$data)
269 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
274 $login = $data['username'];
275 $ip = $_SERVER['REMOTE_ADDR'];
277 $lres = DB::getValue("SELECT * FROM " . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($login));
280 DB::execute("UPDATE " . sql_table('plug_securityenforcer') . " SET fails=fails+1, lastfail=" . time() . ' WHERE login=' . DB::quoteValue($login));
284 DB::execute("INSERT INTO " . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($login) . ',1,' . time() . ')');
286 $lres = DB::getValue("SELECT * FROM " . sql_table('plug_securityenforcer') . " WHERE login='" . DB::quoteValue($ip) . "'");
289 DB::execute("UPDATE " . sql_table('plug_securityenforcer') . ' SET fails=fails+1, lastfail=' . time() . ' WHERE login=' . DB::quoteValue($ip));
293 DB::execute("INSERT INTO " . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($ip) . ',1,' . time() . ')');
295 $lres = DB::getResult('SELECT * FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($login));
296 if ( $lres->rowCount() > 0 )
298 DB::execute('UPDATE ' . sql_table('plug_securityenforcer') . ' SET fails=fails+1, lastfail=' . time() . ' WHERE login=' . DB::quoteValue($login));
302 DB::execute('INSERT INTO ' . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($login) . ',1,' . time() . ')');
304 $lres = DB::getResult('SELECT * FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($ip));
305 if ( $lres->rowCount() > 0 )
307 DB::execute('UPDATE ' . sql_table('plug_securityenforcer') . ' SET fails=fails+1, lastfail=' . time() . ' WHERE login=' . DB::quoteValue($ip));
311 DB::execute('INSERT INTO ' . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($ip) . ',1,' . time() . ')');
312 >>>>>>> skinnable-master
317 private function validate_and_messsage($passwd,$minlength = 6,$complexity = 0)
319 $minlength = intval($minlength);
320 $complexity = intval($complexity);
323 if ( $minlength < 6 )
327 if ( i18n::strlen($passwd) < $minlength )
329 $message = _SECURITYENFORCER_MIN_PWD_LENGTH . $this->pwd_min_length;
332 if ( $complexity > 4 )
341 $ochars = "[-~!@#$%^&*()_+=,.<>?:;|]";
346 $ochars = '[#-~!@\\$%^&*()_+=,.<>?:;|]';
347 >>>>>>> skinnable-master
348 $chartypes = array($ucchars, $lcchars, $numchars, $ochars);
349 $tot = array(0,0,0,0);
352 foreach ( $chartypes as $value )
354 $tot[$i] = preg_match("/" . $value . "/", $passwd);
358 if ( array_sum($tot) < $complexity )
360 $message .= _SECURITYENFORCER_PWD_COMPLEXITY . $this->pwd_complexity;