diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 28a2ae416..a092e84bf 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -73,6 +73,14 @@ class AppController extends Controller { 'authError' => 'Unauthorised access.', 'loginRedirect' => array('controller' => 'users', 'action' => 'routeafterlogin'), 'logoutRedirect' => array('controller' => 'users', 'action' => 'login', 'admin' => false), + 'authenticate' => array( + 'Form' => array( + 'passwordHasher' => 'Blowfish', + 'fields' => array( + 'username' => 'email' + ) + ) + ) ), 'Security', 'ACL', @@ -125,12 +133,7 @@ class AppController extends Controller { ) ); } else { - $this->Auth->authenticate = array( - 'Form' => array( - 'fields' => array('username' => 'email'), - 'userFields' => $auth_user_fields - ) - ); + $this->Auth->authenticate['Form']['userFields'] = $auth_user_fields; } $versionArray = $this->{$this->modelClass}->checkMISPVersion(); $this->mispVersion = implode('.', array_values($versionArray)); @@ -151,7 +154,6 @@ class AppController extends Controller { if (isset($_SERVER['HTTP_USER_AGENT'])) { if (preg_match('/(?i)msie [2-8]/',$_SERVER['HTTP_USER_AGENT']) && !strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) throw new MethodNotAllowedException('You are using an unsecure and outdated version of IE, please download Google Chrome, Mozilla Firefox or update to a newer version of IE. If you are running IE9 or newer and still receive this error message, please make sure that you are not running your browser in compatibility mode. If you still have issues accessing the site, get in touch with your administration team at ' . Configure::read('MISP.contact')); } - $userLoggedIn = false; if (Configure::read('Plugin.CustomAuth_enable')) $userLoggedIn = $this->__customAuthentication($_SERVER); if (!$userLoggedIn) { diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 10efa5634..63344ef9b 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -84,17 +84,32 @@ class UsersController extends AppController { throw new NotFoundException('Something went wrong. Your user account could not be accessed.'); } if ($this->request->is('post') || $this->request->is('put')) { - // What fields should be saved (allowed to be saved) - $fieldList = array('email', 'autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled'); - if ("" != $this->request->data['User']['password']) - $fieldList[] = 'password'; - // Save the data - if ($this->User->save($this->request->data, true ,$fieldList)) { - $this->Session->setFlash(__('The profile has been updated')); - $this->_refreshAuth(); - $this->redirect(array('action' => 'view', $id)); - } else { - $this->Session->setFlash(__('The profile could not be updated. Please, try again.')); + if (!$this->_isRest()) { + if (!empty($this->request->data['User']['current_password'])) { + $hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']); + if (!$hashed) { + $abortPost = true; + $this->Session->setFlash('Invalid password. Please enter your current password to continue.'); + } + unset($this->request->data['User']['current_password']); + } else { + $abortPost = true; + $this->Session->setFlash('Please enter your current password to continue.'); + } + } + if (!$abortPost) { + // What fields should be saved (allowed to be saved) + $fieldList = array('email', 'autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled'); + if ("" != $this->request->data['User']['password']) + $fieldList[] = 'password'; + // Save the data + if ($this->User->save($this->request->data, true ,$fieldList)) { + $this->Session->setFlash(__('The profile has been updated')); + $this->_refreshAuth(); + $this->redirect(array('action' => 'view', $id)); + } else { + $this->Session->setFlash(__('The profile could not be updated. Please, try again.')); + } } } else { $this->User->set('password', ''); @@ -110,31 +125,47 @@ class UsersController extends AppController { public function change_pw() { $id = $this->Auth->user('id'); - $this->User->id = $id; + $user = $this->User->find('first', array( + 'conditions' => array('User.id' => $id), + 'recursive' => -1 + )); if ($this->request->is('post') || $this->request->is('put')) { - // What fields should be saved (allowed to be saved) - $fieldList[] = 'password'; - // Save the data - if ($this->User->save($this->request->data, true ,$fieldList)) { - $this->Session->setFlash(__('Password Changed.')); - $this->User->saveField('email', $this->Auth->user('email')); - $this->User->saveField('change_pw', 0); - $this->_refreshAuth(); - $this->redirect(array('action' => 'view', $id)); + $abortPost = false; + if (!empty($this->request->data['User']['current_password'])) { + $hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']); + if (!$hashed) { + $abortPost = true; + $this->Session->setFlash('Invalid password. Please enter your current password to continue.'); + } + unset($this->request->data['User']['current_password']); } else { - $this->Session->setFlash(__('The password could not be updated. Please, try again.')); + $abortPost = true; + $this->Session->setFlash('Please enter your current password to continue.'); + } + if (!$abortPost) { + // What fields should be saved (allowed to be saved) + $user['User']['change_pw'] = 0; + $user['User']['password'] = $this->request->data['User']['password']; + $user['User']['confirm_password'] = $this->request->data['User']['confirm_password']; + $temp = $user['User']['password']; + // Save the data + if ($this->User->save($user)) { + $this->Session->setFlash(__('Password Changed.')); + $this->_refreshAuth(); + $this->__extralog("change_pw"); + $this->redirect(array('action' => 'view', $id)); + } else { + $this->Session->setFlash(__('The password could not be updated. Make sure you meet the minimum password length / complexity requirements.')); + } } - } else { - $this->loadModel('Server'); - $this->set('complexity', !empty(Configure::read('Security.password_policy_complexity')) ? Configure::read('Security.password_policy_complexity') : $this->Server->serverSettings['Security']['password_policy_complexity']['value']); - $this->set('length', !empty(Configure::read('Security.password_policy_length')) ? Configure::read('Security.password_policy_length') : $this->Server->serverSettings['Security']['password_policy_length']['value']); - $this->User->recursive = 0; - $this->User->read(null, $id); - $this->User->set('password', ''); - $this->request->data = $this->User->data; } - // XXX ACL roles - $this->__extralog("change_pw"); + $this->loadModel('Server'); + $this->set('complexity', !empty(Configure::read('Security.password_policy_complexity')) ? Configure::read('Security.password_policy_complexity') : $this->Server->serverSettings['Security']['password_policy_complexity']['value']); + $this->set('length', !empty(Configure::read('Security.password_policy_length')) ? Configure::read('Security.password_policy_length') : $this->Server->serverSettings['Security']['password_policy_length']['value']); + $this->User->recursive = 0; + $this->User->read(null, $id); + $this->User->set('password', ''); + $this->request->data = $this->User->data; $roles = $this->User->Role->find('list'); $this->set(compact('roles')); } @@ -512,105 +543,121 @@ class UsersController extends AppController { if (!isset($this->request->data['User'])) { $this->request->data['User'] = $this->request->data; } - $this->request->data['User']['id'] = $id; - if (!isset($this->request->data['User']['email'])) { - $this->request->data['User']['email'] = $userToEdit['User']['email']; - } - if (isset($this->request->data['User']['role_id']) && !array_key_exists($this->request->data['User']['role_id'], $syncRoles)) $this->request->data['User']['server_id'] = 0; - $fields = array(); - $blockedFields = array('id', 'invited_by'); - if (!$this->_isSiteAdmin()) { - $blockedFields[] = 'org_id'; - } - foreach (array_keys($this->request->data['User']) as $field) { - if (in_array($field, $blockedFields)) { - continue; - } - if ($field != 'password') array_push($fields, $field); - } - // TODO Audit, __extralog, fields get orig - $fieldsOldValues = array(); - foreach ($fields as $field) { - if ($field == 'enable_password') continue; - if ($field != 'confirm_password') array_push($fieldsOldValues, $this->User->field($field)); - else array_push($fieldsOldValues, $this->User->field('password')); - } - // TODO Audit, __extralog, fields get orig END - if ( - isset($this->request->data['User']['enable_password']) && $this->request->data['User']['enable_password'] != '0' && - isset($this->request->data['User']['password']) && "" != $this->request->data['User']['password'] - ) { - $fields[] = 'password'; - if ($this->_isRest() && !isset($this->request->data['User']['confirm_password'])) { - $this->request->data['User']['confirm_password'] = $this->request->data['User']['password']; - $fields[] = 'confirm_password'; - } - } + $abortPost = false; if (!$this->_isRest()) { - $fields[] = 'role_id'; - } - if (!$this->_isSiteAdmin()) { - $this->loadModel('Role'); - $this->Role->recursive = -1; - $chosenRole = $this->Role->findById($this->request->data['User']['role_id']); - if (empty($chosenRole) || (($chosenRole['Role']['id'] != $allowedRole) && ($chosenRole['Role']['perm_site_admin'] == 1 || $chosenRole['Role']['perm_regexp_access'] == 1 || $chosenRole['Role']['perm_sync'] == 1))) { - throw new Exception('You are not authorised to assign that role to a user.'); + if (!empty($this->request->data['User']['current_password'])) { + $hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']); + if (!$hashed) { + $abortPost = true; + $this->Session->setFlash('Invalid password. Please enter your current password to continue.'); + } + unset($this->request->data['User']['current_password']); + } else { + $abortPost = true; + $this->Session->setFlash('Please enter your current password to continue.'); } } - if ($this->User->save($this->request->data, true, $fields)) { - // TODO Audit, __extralog, fields compare - // newValues to array - $fieldsNewValues = array(); + if (!$abortPost) { + $this->request->data['User']['id'] = $id; + if (!isset($this->request->data['User']['email'])) { + $this->request->data['User']['email'] = $userToEdit['User']['email']; + } + if (isset($this->request->data['User']['role_id']) && !array_key_exists($this->request->data['User']['role_id'], $syncRoles)) $this->request->data['User']['server_id'] = 0; + $fields = array(); + $blockedFields = array('id', 'invited_by'); + if (!$this->_isSiteAdmin()) { + $blockedFields[] = 'org_id'; + } + foreach (array_keys($this->request->data['User']) as $field) { + if (in_array($field, $blockedFields)) { + continue; + } + if ($field != 'password') array_push($fields, $field); + } + // TODO Audit, __extralog, fields get orig + $fieldsOldValues = array(); foreach ($fields as $field) { - if ($field != 'confirm_password') { - $newValue = $this->data['User'][$field]; - if (gettype($newValue) == 'array') { - $newValueStr = ''; - $cP = 0; - foreach ($newValue as $newValuePart) { - if ($cP < 2) $newValueStr .= '-' . $newValuePart; - else $newValueStr = $newValuePart . $newValueStr; - $cP++; - } - array_push($fieldsNewValues, $newValueStr); - } else { - array_push($fieldsNewValues, $newValue); - } - } else { - array_push($fieldsNewValues, $this->data['User']['password']); + if ($field == 'enable_password') continue; + if ($field != 'confirm_password') array_push($fieldsOldValues, $this->User->field($field)); + else array_push($fieldsOldValues, $this->User->field('password')); + } + // TODO Audit, __extralog, fields get orig END + if ( + isset($this->request->data['User']['enable_password']) && $this->request->data['User']['enable_password'] != '0' && + isset($this->request->data['User']['password']) && "" != $this->request->data['User']['password'] + ) { + $fields[] = 'password'; + if ($this->_isRest() && !isset($this->request->data['User']['confirm_password'])) { + $this->request->data['User']['confirm_password'] = $this->request->data['User']['password']; + $fields[] = 'confirm_password'; } } - // compare - $fieldsResultStr = ''; - $c = 0; - foreach ($fields as $field) { - if (isset($fieldsOldValues[$c]) && $fieldsOldValues[$c] != $fieldsNewValues[$c]) { + if (!$this->_isRest()) { + $fields[] = 'role_id'; + } + if (!$this->_isSiteAdmin()) { + $this->loadModel('Role'); + $this->Role->recursive = -1; + $chosenRole = $this->Role->findById($this->request->data['User']['role_id']); + if (empty($chosenRole) || (($chosenRole['Role']['id'] != $allowedRole) && ($chosenRole['Role']['perm_site_admin'] == 1 || $chosenRole['Role']['perm_regexp_access'] == 1 || $chosenRole['Role']['perm_sync'] == 1))) { + throw new Exception('You are not authorised to assign that role to a user.'); + } + } + if ($this->User->save($this->request->data, true, $fields)) { + // TODO Audit, __extralog, fields compare + // newValues to array + $fieldsNewValues = array(); + foreach ($fields as $field) { if ($field != 'confirm_password') { - $fieldsResultStr = $fieldsResultStr . ', ' . $field . ' (' . $fieldsOldValues[$c] . ') => (' . $fieldsNewValues[$c] . ')'; + $newValue = $this->data['User'][$field]; + if (gettype($newValue) == 'array') { + $newValueStr = ''; + $cP = 0; + foreach ($newValue as $newValuePart) { + if ($cP < 2) $newValueStr .= '-' . $newValuePart; + else $newValueStr = $newValuePart . $newValueStr; + $cP++; + } + array_push($fieldsNewValues, $newValueStr); + } else { + array_push($fieldsNewValues, $newValue); + } + } else { + array_push($fieldsNewValues, $this->data['User']['password']); } } - $c++; - } - $fieldsResultStr = substr($fieldsResultStr, 2); - $this->__extralog("edit", "user", $fieldsResultStr); // TODO Audit, check: modify User - // TODO Audit, __extralog, fields compare END - if ($this->_isRest()) { - $user = $this->User->find('first', array( - 'conditions' => array('User.id' => $this->User->id), - 'recursive' => -1 - )); - $user['User']['password'] = '******'; - return $this->RestResponse->viewData($user, $this->response->type()); + // compare + $fieldsResultStr = ''; + $c = 0; + foreach ($fields as $field) { + if (isset($fieldsOldValues[$c]) && $fieldsOldValues[$c] != $fieldsNewValues[$c]) { + if ($field != 'confirm_password') { + $fieldsResultStr = $fieldsResultStr . ', ' . $field . ' (' . $fieldsOldValues[$c] . ') => (' . $fieldsNewValues[$c] . ')'; + } + } + $c++; + } + $fieldsResultStr = substr($fieldsResultStr, 2); + $this->__extralog("edit", "user", $fieldsResultStr); // TODO Audit, check: modify User + // TODO Audit, __extralog, fields compare END + if ($this->_isRest()) { + $user = $this->User->find('first', array( + 'conditions' => array('User.id' => $this->User->id), + 'recursive' => -1 + )); + $user['User']['password'] = '******'; + return $this->RestResponse->viewData($user, $this->response->type()); + } else { + $this->Session->setFlash(__('The user has been saved')); + $this->_refreshAuth(); // in case we modify ourselves + $this->redirect(array('action' => 'index')); + } } else { - $this->Session->setFlash(__('The user has been saved')); - $this->_refreshAuth(); // in case we modify ourselves - $this->redirect(array('action' => 'index')); - } - } else { - if ($this->_isRest()) { - return $this->RestResponse->saveFailResponse('Users', 'admin_edit', $id, $this->User->validationErrors, $this->response->type()); - } else { - $this->Session->setFlash(__('The user could not be saved. Please, try again.')); + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('Users', 'admin_edit', $id, $this->User->validationErrors, $this->response->type()); + } else { + $this->Session->setFlash(__('The user could not be saved. Please, try again.')); + } } } } else { @@ -702,12 +749,30 @@ class UsersController extends AppController { throw new ForbiddenException('You have reached the maximum number of login attempts. Please wait ' . Configure::read('SecureAuth.expire') . ' seconds and try again.'); } } + if ($this->request->is('post') || $this->request->is('put')) { + // Check the length of the user's authkey + $userPass = $this->User->find('first', array( + 'conditions' => array('User.email' => $this->request->data['User']['email']), + 'fields' => array('User.password'), + 'recursive' => -1 + )); + if (strlen($userPass['User']['password']) == 40) { + $this->AdminSetting = ClassRegistry::init('AdminSetting'); + $db_version = $this->AdminSetting->find('all', array('conditions' => array('setting' => 'db_version'))); + $versionRequirementMet = $this->User->checkVersionRequirements($db_version[0]['AdminSetting']['value'], '2.4.77'); + if ($versionRequirementMet) { + $passwordToSave = $this->request->data['User']['password']; + } + unset($this->Auth->authenticate['Form']['passwordHasher']); + } + } if ($this->Auth->login()) { $this->__extralog("login"); // TODO Audit, __extralog, check: customLog i.s.o. __extralog, no auth user?: $this->User->customLog('login', $this->Auth->user('id'), array('title' => '','user_id' => $this->Auth->user('id'),'email' => $this->Auth->user('email'),'org' => 'IN2')); $this->User->Behaviors->disable('SysLogLogable.SysLogLogable'); $this->User->id = $this->Auth->user('id'); $this->User->saveField('last_login', $this->Auth->user('current_login')); $this->User->saveField('current_login', time()); + if (empty($this->Auth->authenticate['Form']['passwordHasher']) && !empty($passwordToSave)) $this->User->saveField('password', $passwordToSave); $this->User->Behaviors->enable('SysLogLogable.SysLogLogable'); // TODO removed the auto redirect for now, due to security concerns - will look more into this // $this->redirect($this->Auth->redirectUrl()); diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 25b669c89..46c3aa9b0 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -22,8 +22,8 @@ App::uses('Model', 'Model'); App::uses('LogableBehavior', 'Assets.models/behaviors'); +App::uses('BlowfishPasswordHasher', 'Controller/Component/Auth'); class AppModel extends Model { - public $name; public $loadedPubSubTool = false; @@ -47,7 +47,7 @@ class AppModel extends Model { 58 => false, 59 => false, 60 => false, 61 => false, 62 => false, 63 => false, 64 => false, 65 => false, 66 => false, 67 => true, 68 => false, 69 => false, 71 => false, 72 => false, 73 => false, - 75 => false + 75 => false, 77 => false ) ) ); @@ -699,6 +699,9 @@ class AppModel extends Model { $this->__addIndex('attributes', 'value1', 255); $this->__addIndex('attributes', 'value2', 255); break; + case '2.4.77': + $sqlArray[] = 'ALTER TABLE `users` CHANGE `password` `password` VARCHAR(255) COLLATE utf8_bin NOT NULL;'; + break; case 'fixNonEmptySharingGroupID': $sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; $sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; @@ -1051,4 +1054,10 @@ class AppModel extends Model { $this->loadedPubSubTool = $pubSubTool; return true; } + + public function checkVersionRequirements($versionString, $minVersion) { + $version = explode('.', $versionString); + $minVersion = explode('.', $minVersion); + return ($version[0] >= $minVersion[0] && $version[1] >= $minVersion[1] && $version[2] >= $minVersion[2]); + } } diff --git a/app/Model/User.php b/app/Model/User.php index ee40bfed5..e4d31431c 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -251,7 +251,8 @@ class User extends AppModel { public function beforeSave($options = array()) { $this->data[$this->alias]['date_modified'] = time(); if (isset($this->data[$this->alias]['password'])) { - $this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']); + $passwordHasher = new BlowfishPasswordHasher(); + $this->data[$this->alias]['password'] = $passwordHasher->hash($this->data[$this->alias]['password']); } return true; } @@ -1026,4 +1027,21 @@ class User extends AppModel { ) )); } + + public function verifyPassword($user_id, $password) { + $currentUser = $this->find('first', array( + 'conditions' => array('User.id' => $user_id), + 'recursive' => -1, + 'fields' => array('User.password') + )); + if (empty($currentUser)) return false; + if (strlen($currentUser['User']['password']) == 40) { + App::uses('SimplePasswordHasher', 'Controller/Component/Auth'); + $passwordHasher = new SimplePasswordHasher(); + } else { + $passwordHasher = new BlowfishPasswordHasher(); + } + $hashed = $passwordHasher->check($password, $currentUser['User']['password']); + return $hashed; + } } diff --git a/app/View/Users/admin_edit.ctp b/app/View/Users/admin_edit.ctp index bea4528bb..69ff31d32 100644 --- a/app/View/Users/admin_edit.ctp +++ b/app/View/Users/admin_edit.ctp @@ -79,6 +79,12 @@ ?> +