3 * Attach plugin for Nucleus CMS
4 * Version 0.9.5 (1.0 RC) for PHP5
5 * Written by Cacher, Jan.16, 2011
6 * Original code was written by Frank Truscott, Nov. 01, 2009
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
14 class NP_SecurityEnforcer extends NucleusPlugin {
15 public function getName() { return 'SecurityEnforcer'; }
16 public function getAuthor() { return 'Frank Truscott + Cacher'; }
17 public function getURL() { return 'http://revcetera.com/ftruscot'; }
18 public function getVersion() { return '1.02JP'; }
19 public function getDescription() { return _SECURITYENFORCER_DESCRIPTION;}
20 public function getTableList() { return array(sql_table('plug_securityenforcer')); }
21 public function hasAdminArea() { return 1; }
22 public function getMinNucleusVersion() { return 350; }
23 public function supportsFeature($feature) { return in_array ($feature, array ('SqlTablePrefix', 'SqlApi'));}
24 public function getEventList() { return array('QuickMenu','PrePasswordSet','CustomLogin','LoginSuccess','LoginFailed','PostRegister','PrePluginOptionsEdit'); }
26 public function install() {
28 $this->createOption('quickmenu', '_SECURITYENFORCER_OPT_QUICKMENU', 'yesno', 'yes');
29 $this->createOption('del_uninstall_data', '_SECURITYENFORCER_OPT_DEL_UNINSTALL_DATA', 'yesno','no');
30 $this->createOption('enable_security', '_SECURITYENFORCER_OPT_ENABLE', 'yesno','yes');
31 $this->createOption('pwd_min_length', '_SECURITYENFORCER_OPT_PWD_MIN_LENGTH', 'text','8');
32 //$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');
33 $this->createOption('pwd_complexity', '_SECURITYENFORCER_OPT_PWD_COMPLEXITY', 'select','0','_SECURITYENFORCER_OPT_SELECT');
34 $this->createOption('max_failed_login', '_SECURITYENFORCER_OPT_MAX_FAILED_LOGIN', 'text', '5');
35 $this->createOption('login_lockout', '_SECURITYENFORCER_OPT_LOGIN_LOCKOUT', 'text', '15');
37 $query = "CREATE TABLE IF NOT EXISTS ". sql_table('plug_securityenforcer').
40 `fails` int(11) NOT NULL default '0',
41 `lastfail` bigint NOT NULL default '0',
42 KEY `login` (`login`)) ENGINE=MyISAM";
47 public function unInstall() {
48 if ($this->getOption('del_uninstall_data') == 'yes') {
49 sql_query('DROP TABLE '.sql_table('plug_securityenforcer'));
54 public function init() {
55 $language = preg_replace( '#\\\\|/#', '', getLanguageName());
56 if (file_exists($this->getDirectory().$language.'.php')){
57 include_once($this->getDirectory().$language.'.php');
59 include_once($this->getDirectory().'english.php');
62 $this->enable_security = $this->getOption('enable_security');
63 $this->pwd_min_length = intval($this->getOption('pwd_min_length'));
64 $this->pwd_complexity = intval($this->getOption('pwd_complexity'));
65 $this->max_failed_login = intval($this->getOption('max_failed_login'));
66 $this->login_lockout = intval($this->getOption('login_lockout'));
70 public function event_QuickMenu(&$data) {
72 if ($this->getOption('quickmenu') != 'yes' || !$member->isAdmin()) {
75 if (!($member->isLoggedIn())) {
78 array_push($data['options'],
79 array('title' => 'Security Enforcer',
80 'url' => $this->getAdminURL(),
81 'tooltip' => _SECURITYENFORCER_ADMIN_TOOLTIP)
86 public function event_PrePasswordSet(&$data) {
87 if ($this->enable_security == 'yes') {
88 $password = $data['password'];
89 if (postVar('action') == 'changemembersettings'){
92 $emptyAllowed = false;
94 if ((!$emptyAllowed)||$password) {
95 $message = $this->_validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity);
97 $data['errormessage'] = _SECURITYENFORCER_INSUFFICIENT_COMPLEXITY . $message. "<br /><br />\n";
98 $data['valid'] = false;
105 public function event_PostRegister(&$data) {
106 if ($this->enable_security == 'yes') {
107 $password = postVar('password');
108 if(postVar('action') == 'memberadd'){
109 $message = $this->_validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity);
111 $errormessage = _SECURITYENFORCER_ACCOUNT_CREATED. $message. "<br /><br />\n";
113 $admin->error($errormessage);
120 public function event_CustomLogin(&$data) {
121 if ($this->enable_security == 'yes' && $this->max_failed_login > 0) {
123 $login = $data['login'];
124 $ip = $_SERVER['REMOTE_ADDR'];
125 sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE lastfail < ".(time() - ($this->login_lockout * 60)));
126 $query = "SELECT fails as result FROM ".sql_table('plug_securityenforcer')." ";
127 $query .= "WHERE login='".sql_real_escape_string($login)."'";
128 $flogin = quickQuery($query);
129 $query = "SELECT fails as result FROM ".sql_table('plug_securityenforcer')." ";
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 $info = sprintf(_SECURITYENFORCER_LOGIN_DISALLOWED, htmlspecialchars($login), htmlspecialchars($ip));
136 ACTIONLOG::add(INFO, $info);
142 public function event_LoginSuccess(&$data) {
143 if ($this->enable_security == 'yes' && $this->max_failed_login > 0) {
145 $login = $data['username'];
146 $ip = $_SERVER['REMOTE_ADDR'];
147 sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($login)."'");
148 sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($ip)."'");
153 function event_LoginFailed(&$data) {
154 if ($this->enable_security == 'yes' && $this->max_failed_login > 0) {
156 $login = $data['username'];
157 $ip = $_SERVER['REMOTE_ADDR'];
158 $lres = sql_query("SELECT * FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($login)."'");
159 if (sql_num_rows($lres)) {
160 sql_query("UPDATE ".sql_table('plug_securityenforcer')." SET fails=fails+1, lastfail=".time()." WHERE login='".sql_real_escape_string($login)."'");
163 sql_query("INSERT INTO ".sql_table('plug_securityenforcer')." (login,fails,lastfail) VALUES ('".sql_real_escape_string($login)."',1,".time().")");
165 $lres = sql_query("SELECT * FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($ip)."'");
166 if (sql_num_rows($lres)) {
167 sql_query("UPDATE ".sql_table('plug_securityenforcer')." SET fails=fails+1, lastfail=".time()." WHERE login='".sql_real_escape_string($ip)."'");
170 sql_query("INSERT INTO ".sql_table('plug_securityenforcer')." (login,fails,lastfail) VALUES ('".sql_real_escape_string($ip)."',1,".time().")");
176 public function event_PrePluginOptionsEdit($data) {
177 if (array_key_exists('plugid', $data) && $data['plugid'] === $this->getID()) {
178 foreach($data['options'] as $key => $value){
179 if (defined($value['description'])) {
180 $data['options'][$key]['description'] = constant($value['description']);
182 if (!strcmp($value['type'], 'select') && defined($value['typeinfo'])) {
183 $data['options'][$key]['typeinfo'] = constant($value['typeinfo']);
190 /* Helper Functions */
192 private function _validate_passwd($passwd,$minlength = 6,$complexity = 0) {
193 $minlength = intval($minlength);
194 $complexity = intval($complexity);
196 if ($minlength < 6 ) {
199 if (strlen($passwd) < $minlength) {
203 if ($complexity > 4) $complexity = 4;
207 $ochars = "[-~!@#$%^&*()_+=,.<>?:;|]";
208 $chartypes = array($ucchars, $lcchars, $numchars, $ochars);
209 $tot = array(0,0,0,0);
211 foreach ($chartypes as $value) {
212 $tot[$i] = preg_match("/".$value."/", $passwd);
216 if (array_sum($tot) >= $complexity) {
222 private function _validate_and_messsage($passwd,$minlength = 6,$complexity = 0) {
223 $minlength = intval($minlength);
224 $complexity = intval($complexity);
226 if ($minlength < 6 ) {
229 if (strlen($passwd) < $minlength) {
230 $message = _SECURITYENFORCER_MIN_PWD_LENGTH . $this->pwd_min_length;
233 if ($complexity > 4) {
239 $ochars = "[-~!@#$%^&*()_+=,.<>?:;|]";
240 $chartypes = array($ucchars, $lcchars, $numchars, $ochars);
241 $tot = array(0,0,0,0);
243 foreach ($chartypes as $value) {
244 $tot[$i] = preg_match("/".$value."/", $passwd);
248 if (array_sum($tot) < $complexity) {
249 $message .= _SECURITYENFORCER_PWD_COMPLEXITY . $this->pwd_complexity;