Showing with 1,164 additions and 758 deletions.
  1. +0 −6 .gitmodules
  2. +1 −1 .travis.yml
  3. +0 −9 INSTALL/INSTALL.sh
  4. +1 −1 INSTALL/INSTALL.sh.sha1
  5. +1 −1 INSTALL/INSTALL.sh.sha256
  6. +1 −1 INSTALL/INSTALL.sh.sha384
  7. +1 −1 INSTALL/INSTALL.sh.sha512
  8. +0 −20 INSTALL/ansible/roles/misp/tasks/main.yml
  9. +0 −1 INSTALL/dependencies/Console_CommandLine
  10. +0 −1 INSTALL/dependencies/Crypt_GPG
  11. +1 −1 VERSION.json
  12. +26 −0 app/Console/Command/AdminShell.php
  13. +29 −9 app/Console/Command/ServerShell.php
  14. +2 −2 app/Controller/AppController.php
  15. +81 −7 app/Controller/Component/ACLComponent.php
  16. +1 −1 app/Controller/Component/RestResponseComponent.php
  17. +14 −1 app/Controller/EventsController.php
  18. +31 −14 app/Controller/FeedsController.php
  19. +24 −6 app/Controller/ServersController.php
  20. +34 −61 app/Controller/UsersController.php
  21. +2 −2 app/Locale/ara/LC_MESSAGES/default.po
  22. +2 −2 app/Locale/cze/LC_MESSAGES/default.po
  23. +2 −2 app/Locale/dan/LC_MESSAGES/default.po
  24. +2 −2 app/Locale/default.pot
  25. +2 −2 app/Locale/deu/LC_MESSAGES/default.po
  26. +3 −3 app/Locale/fra/LC_MESSAGES/default.po
  27. +1 −1 app/Locale/hun/LC_MESSAGES/default.po
  28. +1 −1 app/Locale/ita/LC_MESSAGES/default.po
  29. +3 −3 app/Locale/jpn/LC_MESSAGES/default.po
  30. +2 −2 app/Locale/kor/LC_MESSAGES/default.po
  31. +4 −4 app/Locale/no/LC_MESSAGES/default.po
  32. +1 −1 app/Locale/pol/LC_MESSAGES/default.po
  33. +2 −2 app/Locale/pt/LC_MESSAGES/default.po
  34. +2 −2 app/Locale/pt_BR/LC_MESSAGES/default.po
  35. +3 −3 app/Locale/rus/LC_MESSAGES/default.po
  36. +2 −2 app/Locale/spa/LC_MESSAGES/default.po
  37. +1 −1 app/Locale/swe/LC_MESSAGES/default.po
  38. +1 −1 app/Locale/ukr/LC_MESSAGES/default.po
  39. +1 −1 app/Locale/zh-s/LC_MESSAGES/default.po
  40. +2 −3 app/Model/Event.php
  41. +473 −427 app/Model/Feed.php
  42. +1 −0 app/Model/Log.php
  43. +2 −2 app/Model/Organisation.php
  44. +103 −17 app/Model/Server.php
  45. +232 −62 app/Model/User.php
  46. +1 −1 app/View/Elements/Users/userIndexTable.ctp
  47. +15 −0 app/View/Servers/index.ctp
  48. +0 −18 app/View/Servers/ondemand_action.ctp
  49. +1 −1 app/View/Users/admin_add.ctp
  50. +1 −1 app/View/Users/admin_edit.ctp
  51. +20 −0 app/View/Users/admin_index.ctp
  52. +1 −1 app/View/Users/edit.ctp
  53. +2 −1 app/composer.json
  54. +13 −1 app/files/scripts/misp2stix.py
  55. +1 −1 app/files/scripts/stix2/misp2stix2.py
  56. +3 −2 app/files/scripts/stix2/stix2misp.py
  57. +6 −0 app/files/scripts/stix2misp.py
  58. +1 −1 cti-python-stix2
  59. +0 −5 docs/INSTALL.rhel7.md
  60. +1 −6 docs/INSTALL.rhel8.md
  61. +0 −5 docs/INSTALL.ubuntu1804.md
  62. +0 −2 docs/generic/supportFunctions.md
  63. +0 −5 docs/xINSTALL.centos6.md
  64. +0 −5 docs/xINSTALL.centos7.md
  65. +0 −4 docs/xINSTALL.debian10.md
  66. +0 −4 docs/xINSTALL.debian9.md
  67. +0 −2 docs/xINSTALL.tsurugi.md
@@ -37,12 +37,6 @@
path = Plugin/DebugKit
url = https://github.com/cakephp/debug_kit.git
branch = 2.2
[submodule "INSTALL/Crypt_GPG"]
path = INSTALL/dependencies/Crypt_GPG
url = https://github.com/pear/Crypt_GPG
[submodule "INSTALL/Console_CommandLine"]
path = INSTALL/dependencies/Console_CommandLine
url = https://github.com/pear/Console_CommandLine
[submodule "INSTALL/dependencies/Net_GeoIP"]
path = INSTALL/dependencies/Net_GeoIP
url = https://github.com/pear/Net_GeoIP
@@ -28,7 +28,7 @@ install:
- sudo apt-get -y update
# Travis lacks entropy.
- sudo apt-get -y install haveged
- sudo apt-get -y install python3 python3-pip python3-dev python3-nose libxml2-dev libzmq3-dev zlib1g-dev apache2 curl php-mysql php-dev php-cli libapache2-mod-php libfuzzy-dev php-mbstring libonig4 php-json php-xml php-opcache php-readline php-pear php-redis php-gnupg php-gd
- sudo apt-get -y install python3 python3-pip python3-dev python3-nose libxml2-dev libzmq3-dev zlib1g-dev apache2 curl php-mysql php-dev php-cli libapache2-mod-php libfuzzy-dev php-mbstring libonig4 php-json php-xml php-opcache php-readline php-redis php-gnupg php-gd
- sudo apt-get -y dist-upgrade
- sudo pip3 install --upgrade pip setuptools requests pyzmq
- sudo pip3 install --upgrade -r requirements.txt
@@ -763,7 +763,6 @@ installDepsPhp70 () {
php php-cli \
php-dev \
php-json php-xml php-mysql php-opcache php-readline php-mbstring \
php-pear \
php-redis php-gnupg \
php-gd

@@ -785,7 +784,6 @@ installDepsPhp73 () {
php7.3 php7.3-cli \
php7.3-dev \
php7.3-json php7.3-xml php7.3-mysql php7.3-opcache php7.3-readline php7.3-mbstring \
php-pear \
php-redis php-gnupg \
php-gd
}
@@ -1137,7 +1135,6 @@ installDepsPhp73 () {
php7.3 php7.3-cli \
php7.3-dev \
php7.3-json php7.3-xml php7.3-mysql php7.3-opcache php7.3-readline php7.3-mbstring \
php-pear \
php-redis php-gnupg \
php-gd
}
@@ -1153,7 +1150,6 @@ installDepsPhp72 () {
php php-cli \
php-dev \
php-json php-xml php-mysql php7.2-opcache php-readline php-mbstring \
php-pear \
php-redis php-gnupg \
php-gd

@@ -1174,7 +1170,6 @@ installDepsPhp70 () {
php php-cli \
php-dev \
php-json php-xml php-mysql php-opcache php-readline php-mbstring \
php-pear \
php-redis php-gnupg \
php-gd

@@ -1324,10 +1319,6 @@ installCore () {

# install plyara
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install plyara

# Install Crypt_GPG and Console_CommandLine
sudo pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml
sudo pear install ${PATH_TO_MISP}/INSTALL/dependencies/Crypt_GPG/package.xml
}

installCake () {
@@ -1 +1 @@
75d9de9742ccb2383d044f6a19fed73ea2909f9d INSTALL.sh
6f5260ea0b7af730f4b94007e5046f661e3c2585 INSTALL.sh
@@ -1 +1 @@
c5f790fc1c13af0b95490cc2136324b9dd9930af1b4c0afb6da4687b47c58e23 INSTALL.sh
babd4491825edd02153d7d09624f1668c452ee14279872f367c5729dd51171bc INSTALL.sh
@@ -1 +1 @@
5c2aaba9cafc88f5f81dafa7717e95daaec671ddd9aa32ed2ce0daf7654c2a11eab59271802590566f1cdd285a485673 INSTALL.sh
0cf66499a027baaf5b52aba19270a7f6e5fbc7d99df225a9049bf9c35c35f9c4316a59ef92ec544ef2f23eea416897b0 INSTALL.sh
@@ -1 +1 @@
dcb06c97ca3d4528b41c81f9d6933de235260e5fcfcc28c5fdd2fb19a287b421e492c3ae5ab3896ee3119309f7539be4b1a03422510721dae3f20d07ec2cc415 INSTALL.sh
cfc7e4b1749ad8ed2d75fd3e7d984bb48ab253559c4a37318568dfc175fad40612a05bb59d3672dc3de88b651bd18e8b959457c4ae9c72eff2c0a7418e51fce8 INSTALL.sh
@@ -74,26 +74,6 @@
- "/opt/misp-server/tmp"
- "/opt/misp-server/backup"

######### PEAR: CRYPTPGP #########
- name: Configure PEAR proxy
shell: "{{ item }}"
args:
creates: /home/misp/ansible/ansible_shell_pear_configure_proxy.log
with_items:
- "pear config-set http_proxy http://{{proxy_host}}:{{proxy_port}} > /home/misp/ansible/ansible_shell_pear_configure_proxy.log"

- name: Configure PEAR tmp
shell: "{{ item }}"
args:
creates: /home/misp/ansible/ansible_shell_pear_configure_tmp.log
with_items:
- pear config-set temp_dir /opt/misp-server/tmp/ > /home/misp/ansible/ansible_shell_pear_configure_tmp.log

- name: Install CryptGPG
pear:
name: Crypt_GPG
state: present

######### MISP REPOSITORY #########

- name: Clone MISP repository
Submodule Console_CommandLine deleted from 40fca1
Submodule Crypt_GPG deleted from bf07ab
@@ -1 +1 @@
{"major":2, "minor":4, "hotfix":114}
{"major":2, "minor":4, "hotfix":115}
@@ -506,4 +506,30 @@ public function cleanCaches()
$this->Server->cleanCacheFiles();
echo '...caches lost in time, like tears in rain.' . PHP_EOL;
}

public function resetSyncAuthkeys()
{
if (empty($this->args[0])) {
echo sprintf(
__("MISP mass sync authkey reset command line tool.\n\nUsage: %sConsole/cake resetSyncAuthkeys [user_id]") . "\n\n",
APP
);
die();
} else {
$userId = $this->args[0];
$user = $this->User->getAuthUser($userId);
if (empty($user)) {
echo __('Invalid user.') . "\n\n";
}
if (!$user['Role']['perm_site_admin']) {
echo __('User has to be a site admin.') . "\n\n";
}
if (!empty($this->args[1])) {
$jobId = $this->args[1];
} else {
$jobId = false;
}
$this->User->resetAllSyncAuthKeys($user, $jobId);
}
}
}
@@ -253,13 +253,19 @@ public function cacheFeed() {
$jobId = $this->Job->id;
}
$this->Job->read(null, $jobId);
$result = $this->Feed->cacheFeedInitiator($user, $jobId, $scope);
try {
$result = $this->Feed->cacheFeedInitiator($user, $jobId, $scope);
} catch (Exception $e) {
CakeLog::error($e->getMessage());
$result = false;
}

$this->Job->id = $jobId;
if ($result !== true) {
$message = 'Job Failed. Reason: ';
$message = 'Job failed. See logs for more details.';
$this->Job->save(array(
'id' => $jobId,
'message' => $message . $result,
'message' => $message,
'progress' => 0,
'status' => 3
));
@@ -401,12 +407,26 @@ public function enqueueFeedCache() {
);
$this->Job->save($data);
$jobId = $this->Job->id;
$result = $this->Feed->cacheFeedInitiator($user, $jobId, 'all');
$this->Job->save(array(
'message' => 'Job done.',
'progress' => 100,
'status' => 4
));
try {
$result = $this->Feed->cacheFeedInitiator($user, $jobId, 'all');
} catch (Exception $e) {
CakeLog::error($e->getMessage());
$result = false;
}
if ($result) {
$this->Job->save(array(
'message' => 'Job done.',
'progress' => 100,
'status' => 4
));
} else {
$this->Job->save(array(
'message' => 'Job failed. See logs for more details.',
'progress' => 100,
'status' => 3,
));
}

$this->Task->id = $task['Task']['id'];
$this->Task->saveField('message', 'Job completed at ' . date('d/m/Y - H:i:s'));
}
@@ -76,8 +76,6 @@ public function __construct($id = false, $table = null, $ds = null)
'Session',
'Auth' => array(
'authError' => 'Unauthorised access.',
'loginRedirect' => array('controller' => 'users', 'action' => 'routeafterlogin'),
'logoutRedirect' => array('controller' => 'users', 'action' => 'login', 'admin' => false),
'authenticate' => array(
'Form' => array(
'passwordHasher' => 'Blowfish',
@@ -105,6 +103,8 @@ private function __isApiFunction($controller, $action)

public function beforeFilter()
{
$this->Auth->loginRedirect = Configure::read('MISP.baseurl') . '/users/routeafterlogin';
$this->Auth->logoutRedirect = Configure::read('MISP.baseurl') . '/users/login';
$this->__sessionMassage();
if (Configure::read('Security.allow_cors')) {
// Add CORS headers
@@ -335,7 +335,7 @@ class ACLComponent extends Component
),
'servers' => array(
'add' => array(),
'cache' => array('perm_site_admin'),
'cache' => array(),
'checkout' => array(),
'createSync' => array('perm_sync'),
'delete' => array(),
@@ -348,14 +348,14 @@ class ACLComponent extends Component
'getInstanceUUID' => array('perm_sync'),
'getPyMISPVersion' => array('*'),
'getSetting' => array(),
'getSubmodulesStatus' => array('perm_site_admin'),
'getSubmoduleQuickUpdateForm' => array('perm_site_admin'),
'getSubmodulesStatus' => array(),
'getSubmoduleQuickUpdateForm' => array(),
'getWorkers' => array(),
'getVersion' => array('*'),
'import' => ('perm_site_admin'),
'index' => array('OR' => array('perm_sync', 'perm_admin')),
'import' => array(),
'index' => array(),
'ondemandAction' => array(),
'postTest' => array('perm_sync'),
'postTest' => array(),
'previewEvent' => array(),
'previewIndex' => array(),
'pull' => array(),
@@ -371,7 +371,7 @@ class ACLComponent extends Component
'statusZeroMQServer' => array(),
'stopWorker' => array(),
'stopZeroMQServer' => array(),
'testConnection' => array('perm_sync'),
'testConnection' => array(),
'update' => array(),
'updateJSON' => array(),
'updateProgress' => array(),
@@ -518,6 +518,7 @@ class ACLComponent extends Component
'initiatePasswordReset' => array('perm_admin'),
'login' => array('*'),
'logout' => array('*'),
'resetAllSyncAuthKeys' => array(),
'resetauthkey' => array('*'),
'request_API' => array('*'),
'routeafterlogin' => array('*'),
@@ -553,6 +554,78 @@ class ACLComponent extends Component
)
);

private function __checkLoggedActions($user, $controller, $action)
{
$loggedActions = array(
'servers' => array(
'index' => array(
'role' => array(
'NOT' => array(
'perm_site_admin'
)
),
'message' => __('This could be an indication of an attempted privilege escalation on older vulnerable versions of MISP (<2.4.115)')
)
)
);
foreach ($loggedActions as $k => $v) {
$loggedActions[$k] = array_change_key_case($v);
}
$message = '';
if (!empty($loggedActions[$controller])) {
if (!empty($loggedActions[$controller][$action])) {
$message = $loggedActions[$controller][$action]['message'];
$hit = false;
if (empty($loggedActions[$controller][$action]['role'])) {
$hit = true;
} else {
$role_req = $loggedActions[$controller][$action]['role'];
if (empty($role_req['OR']) && empty($role_req['AND']) && empty($role_req['NOT'])) {
$role_req = array('OR' => $role_req);
}
if (!empty($role_req['NOT'])) {
foreach ($role_req['NOT'] as $k => $v) {
if (!$user['Role'][$v]) {
$hit = true;
continue;
}
}
}
if (!$hit && !empty($role_req['AND'])) {
$subhit = true;
foreach ($role_req['AND'] as $k => $v) {
$subhit = $subhit && $user['Role'][$v];
}
if ($subhit) {
$hit = true;
}
}
if (!$hit && !empty($role_req['OR'])) {
foreach ($role_req['OR'] as $k => $v) {
if ($user['Role'][$v]) {
$hit = true;
continue;
}
}
}
if ($hit) {
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'User',
'model_id' => $user['id'],
'email' => $user['email'],
'action' => 'security',
'user_id' => $user['id'],
'title' => __('User triggered security alert by attempting to access /%s/%s. Reason why this endpoint is of interest: %s', $controller, $action, $message),
));
}
}
}
}
}

// The check works like this:
// If the user is a site admin, return true
// If the requested action has an OR-d list, iterate through the list. If any of the permissions are set for the user, return true
@@ -567,6 +640,7 @@ public function checkAccess($user, $controller, $action, $soft = false)
foreach ($aclList as $k => $v) {
$aclList[$k] = array_change_key_case($v);
}
$this->__checkLoggedActions($user, $controller, $action);
if ($user['Role']['perm_site_admin']) {
return true;
}
@@ -279,7 +279,7 @@ class RestResponseComponent extends Component

private $__scopedFieldsConstraint = array();

public function initialize($controller) {
public function initialize(Controller $controller) {
$this->__configureFieldConstraints();
}

@@ -730,6 +730,7 @@ public function index()
unset($rules['contain']);
$rules['recursive'] = -1;
$rules['fields'] = array('id', 'timestamp', 'published', 'uuid');
$rules['contain'] = array('Orgc.uuid');
}
$paginationRules = array('page', 'limit', 'sort', 'direction', 'order');
foreach ($paginationRules as $paginationRule) {
@@ -835,6 +836,7 @@ public function index()
return $this->RestResponse->viewData($events, $this->response->type(), false, false, false, array('X-Result-Count' => $absolute_total));
} else {
foreach ($events as $key => $event) {
$event['Event']['orgc_uuid'] = $event['Orgc']['uuid'];
$events[$key] = $event['Event'];
}
return $this->RestResponse->viewData($events, $this->response->type(), false, false, false, array('X-Result-Count' => $absolute_total));
@@ -3416,7 +3418,18 @@ public function restSearch(
$this->render('/Events/module_views/' . $renderView);
} else {
$responseType = $this->Event->validFormats[$returnFormat][0];
return $this->RestResponse->viewData($final, $responseType, false, true, false, array('X-Result-Count' => $elementCounter, 'X-Export-Module-Used' => $returnFormat, 'X-Response-Format' => $responseType));
$filename = 'misp.event.';
if (!empty($filters['eventid']) && !is_array($filters['eventid'])) {
if (Validation::uuid(trim($filters['eventid']))) {
$filename .= trim($filters['eventid']);
} else if (!empty(intval(trim($filters['eventid'])))) {
$filename .= intval(trim($filters['eventid']));
}
} else {
$filename .= 'list';
}
$filename .= '.' . $responseType;
return $this->RestResponse->viewData($final, $responseType, false, true, $filename, array('X-Result-Count' => $elementCounter, 'X-Export-Module-Used' => $returnFormat, 'X-Response-Format' => $responseType));
}

}
@@ -520,7 +520,13 @@ public function getEvent($feedId, $eventUuid, $all = false)
$this->Flash->info(__('Feed is currently not enabled. Make sure you enable it.'));
$this->redirect(array('action' => 'previewIndex', $feedId));
}
$result = $this->Feed->downloadAndSaveEventFromFeed($this->Feed->data, $eventUuid, $this->Auth->user());
try {
$result = $this->Feed->downloadAndSaveEventFromFeed($this->Feed->data, $eventUuid, $this->Auth->user());
} catch (Exception $e) {
$this->Flash->error(__('Download failed.') . ' ' . $e->getMessage());
$this->redirect(array('action' => 'previewIndex', $feedId));
}

if (isset($result['action'])) {
if ($result['result']) {
if ($result['action'] == 'add') {
@@ -579,11 +585,13 @@ private function __previewIndex($feed, $filterParams = array())
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocketFeed($feed);
$events = $this->Feed->getManifest($feed, $HttpSocket);
if (!is_array($events)) {
$this->Flash->info($events);
try {
$events = $this->Feed->getManifest($feed, $HttpSocket);
} catch (Exception $e) {
$this->Flash->error("Could not fetch manifest for feed: {$e->getMessage()}");
$this->redirect(array('controller' => 'feeds', 'action' => 'index'));
}

if (!empty($this->params['named']['searchall'])) {
foreach ($events as $uuid => $event) {
$found = false;
@@ -672,10 +680,10 @@ private function __previewFreetext($feed)
$HttpSocket = $syncTool->setupHttpSocketFeed($feed);
$params = array();
// params is passed as reference here, the pagination happens in the method, which isn't ideal but considering the performance gains here it's worth it
$resultArray = $this->Feed->getFreetextFeed($feed, $HttpSocket, $feed['Feed']['source_format'], $currentPage, 60, $params);
// we want false as a valid option for the split fetch, but we don't want it for the preview
if (!is_array($resultArray)) {
$this->Flash->info($resultArray);
try {
$resultArray = $this->Feed->getFreetextFeed($feed, $HttpSocket, $feed['Feed']['source_format'], $currentPage, 60, $params);
} catch (Exception $e) {
$this->Flash->error("Could not fetch feed: {$e->getMessage()}");
$this->redirect(array('controller' => 'feeds', 'action' => 'index'));
}
$this->params->params['paging'] = array($this->modelClass => $params);
@@ -721,7 +729,12 @@ private function __previewCSV($feed)
throw new MethodNotAllowedException(__('Invalid feed type.'));
}
$HttpSocket = $syncTool->setupHttpSocketFeed($feed);
$resultArray = $this->Feed->getFreetextFeed($feed, $HttpSocket, $feed['Feed']['source_format'], $currentPage);
try {
$resultArray = $this->Feed->getFreetextFeed($feed, $HttpSocket, $feed['Feed']['source_format'], $currentPage);
} catch (Exception $e) {
$this->Flash->error("Could not fetch feed: {$e->getMessage()}");
$this->redirect(array('controller' => 'feeds', 'action' => 'index'));
}
// we want false as a valid option for the split fetch, but we don't want it for the preview
if ($resultArray == false) {
$resultArray = array();
@@ -755,7 +768,11 @@ public function previewEvent($feedId, $eventUuid, $all = false)
throw new NotFoundException(__('Invalid feed.'));
}
$this->Feed->read();
$event = $this->Feed->downloadEventFromFeed($this->Feed->data, $eventUuid, $this->Auth->user());
try {
$event = $this->Feed->downloadEventFromFeed($this->Feed->data, $eventUuid, $this->Auth->user());
} catch (Exception $e) {
throw new Exception(__('Could not download the selected Event'), 0, $e);
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($event, $this->response->type());
}
@@ -858,11 +875,11 @@ public function fetchSelectedFromFreetextIndex($id)
$feed['Feed']['settings'] = json_decode($feed['Feed']['settings'], true);
}
$data = json_decode($this->request->data['Feed']['data'], true);
$result = $this->Feed->saveFreetextFeedData($feed, $data, $this->Auth->user());
if ($result === true) {
try {
$this->Feed->saveFreetextFeedData($feed, $data, $this->Auth->user());
$this->Flash->success(__('Data pulled.'));
} else {
$this->Flash->error(__('Could not pull the selected data. Reason: %s', $result));
} catch (Exception $e) {
$this->Flash->error(__('Could not pull the selected data. Reason: %s', $e->getMessage()));
}
$this->redirect(array('controller' => 'feeds', 'action' => 'index'));
}
@@ -44,12 +44,6 @@ public function beforeFilter()

public function index()
{
if (!$this->_isSiteAdmin()) {
if (!$this->userRole['perm_sync'] && !$this->userRole['perm_admin']) {
$this->redirect(array('controller' => 'events', 'action' => 'index'));
}
$this->paginate['conditions'] = array('Server.org_id LIKE' => $this->Auth->user('org_id'));
}
if ($this->_isRest()) {
$params = array(
'recursive' => -1,
@@ -2089,4 +2083,28 @@ public function import()
}
}
}

public function resetRemoteAuthKey($id)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException(__('This endpoint expects POST requests.'));
}
$result = $this->Server->resetRemoteAuthkey($id);
if ($result !== true) {
if (!$this->_isRest()) {
$this->Flash->error($result);
$this->redirect(array('action' => 'index'));
} else {
return $this->RestResponse->saveFailResponse('Servers', 'resetRemoteAuthKey', $id, $message, $this->response->type());
}
} else {
$message = __('API key updated.');
if (!$this->_isRest()) {
$this->Flash->success($message);
$this->redirect(array('action' => 'index'));
} else {
return $this->RestResponse->saveSuccessResponse('Servers', 'resetRemoteAuthKey', $message, $this->response->type());
}
}
}
}
@@ -211,7 +211,7 @@ public function change_pw()
// Save the data
if ($this->User->save($user)) {
$message = __('Password Changed.');
$this->__extralog("change_pw");
$this->User->extralog($this->Auth->user(), "change_pw", null, null, $user);
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('User', 'change_pw', false, $this->response->type(), $message);
}
@@ -869,7 +869,11 @@ public function admin_edit($id = null)
$c++;
}
$fieldsResultStr = substr($fieldsResultStr, 2);
$this->__extralog("edit", "user", $fieldsResultStr);
$user = $this->User->find('first', array(
'recursive' => -1,
'conditions' => array('User.id' => $this->id)
));
$this->User->extralog($this->Auth->user(), "edit", "user", $fieldsResultStr, $user);
if ($this->_isRest()) {
$user = $this->User->find('first', array(
'conditions' => array('User.id' => $this->User->id),
@@ -954,7 +958,7 @@ public function admin_delete($id = null)
}
$fieldsDescrStr = 'User (' . $id . '): ' . $user['User']['email'];
if ($this->User->delete($id)) {
$this->__extralog("delete", $fieldsDescrStr, '');
$this->User->extralog($this->Auth->user(), "delete", $fieldsDescrStr, '');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('User', 'admin_delete', $id, $this->response->type(), 'User deleted.');
} else {
@@ -1010,7 +1014,7 @@ public function login()
}
}
if ($this->Auth->login()) {
$this->__extralog("login");
$this->User->extralog($this->Auth->user(), "login");
$this->User->Behaviors->disable('SysLogLogable.SysLogLogable');
$this->User->id = $this->Auth->user('id');
$user = $this->User->find('first', array(
@@ -1125,7 +1129,7 @@ public function routeafterlogin()
public function logout()
{
if ($this->Session->check('Auth.User')) {
$this->__extralog("logout");
$this->User->extralog($this->Auth->user(), "logout");
}
$this->Flash->info(__('Good-Bye'));
$user = $this->User->find('first', array(
@@ -1140,7 +1144,7 @@ public function logout()
$this->redirect($this->Auth->logout());
}

public function resetauthkey($id = null)
public function resetauthkey($id = null, $alert = false)
{
if (!$this->_isAdmin() && Configure::read('MISP.disableUserSelfManagement')) {
throw new MethodNotAllowedException('User self-management has been disabled on this instance.');
@@ -1149,24 +1153,12 @@ public function resetauthkey($id = null)
$id = $this->Auth->user('id');
}
if (!$this->userRole['perm_auth']) {
throw new MethodNotAllowedException('Invalid action.');
throw new MethodNotAllowedException(__('Invalid action.'));
}
$newkey = $this->User->resetauthkey($this->Auth->user(), $id, $alert);
if ($newkey === false) {
throw new MethodNotAllowedException(__('Invalid user.'));
}
$this->User->id = $id;
if (!$id || !$this->User->exists($id)) {
throw new MethodNotAllowedException('Invalid user.');
}
$user = $this->User->read();
$oldKey = $this->User->data['User']['authkey'];
if (!$this->_isSiteAdmin() && !($this->_isAdmin() && $this->Auth->user('org_id') == $this->User->data['User']['org_id']) && ($this->Auth->user('id') != $id)) {
throw new MethodNotAllowedException('Invalid user.');
}
$newkey = $this->User->generateAuthKey();
$this->User->saveField('authkey', $newkey);
$this->__extralog(
'reset_auth_key',
'Authentication key for user ' . $user['User']['id'] . ' (' . $user['User']['email'] . ')',
$fieldsResult = 'authkey(' . $oldKey . ') => (' . $newkey . ')'
);
if (!$this->_isRest()) {
$this->Flash->success(__('New authkey generated.', true));
$this->_refreshAuth();
@@ -1176,6 +1168,25 @@ public function resetauthkey($id = null)
}
}

public function resetAllSyncAuthKeys()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException(__('This functionality is only accessible via POST requests.'));
}
$results = $this->User->resetAllSyncAuthKeysRouter($this->Auth->user());
if ($results === true) {
$message = __('Job initiated.');
} else {
$message = __('%s authkeys reset, %s could not be reset.', $results['success'], $results['fails']);
}
if (!$this->_isRest()) {
$this->Flash->info($message);
$this->redirect($this->referer());
} else {
return $this->RestResponse->saveSuccessResponse('User', 'resetAllSyncAuthKeys', false, $this->response->type(), $message);
}
}

public function histogram($selected = null)
{
//if (!$this->request->is('ajax') && !$this->_isRest()) throw new MethodNotAllowedException('This function can only be accessed via AJAX or the API.');
@@ -1297,44 +1308,6 @@ public function downloadTerms()
return $this->response;
}

private function __extralog($action = null, $description = null, $fieldsResult = null)
{
// new data
$model = 'User';
$modelId = $this->Auth->user('id');
if ($action == 'login') {
$description = "User (" . $this->Auth->user('id') . "): " . $this->data['User']['email'];
} elseif ($action == 'logout') {
$description = "User (" . $this->Auth->user('id') . "): " . $this->Auth->user('email');
} elseif ($action == 'edit') {
$description = "User (" . $this->User->id . "): " . $this->data['User']['email'];
} elseif ($action == 'change_pw') {
$description = "User (" . $this->User->id . "): " . $this->Auth->user('email');
$fieldsResult = "Password changed.";
}

// query
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => $model,
'model_id' => $modelId,
'email' => $this->Auth->user('email'),
'action' => $action,
'title' => $description,
'change' => isset($fieldsResult) ? $fieldsResult : ''));

// write to syslogd as well
App::import('Lib', 'SysLog.SysLog');
$syslog = new SysLog();
if (isset($fieldsResult) && $fieldsResult) {
$syslog->write('notice', $description . ' -- ' . $action . ' -- ' . $fieldsResult);
} else {
$syslog->write('notice', $description . ' -- ' . $action);
}
}

// Used for fields_before and fields for audit
public function arrayCopy(array $array)
{
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr ""

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15316,7 +15316,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr ""

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15316,7 +15316,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr "Instansens debug-niveauet for webstedsadmins. Denne funktion lader webstedsadmins eksekvere debug-tilstand på en idriftsat instans uden at afsløre dette overfor andre brugere. Den mest detaljerigholdige Indstilling for debug og site_admin_debug benyttes for admins."

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15325,7 +15325,7 @@ msgstr "Synkroniser bruger for"

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3890,7 +3890,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr ""

#: Model/Server.php:2254
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2258
@@ -15821,7 +15821,7 @@ msgstr ""
#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
#: View/Users/edit.ctp:23
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr ""

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15316,7 +15316,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr "Le niveau de debuggage de l'instance pour les adminstrateurs. Cette option permet aux administrateurs de l'instance de lancer le mode debug sur une instance active sans exposer les autres utilisateurs. L'option la plus verbeuse de debug et site_admin_debug sont utilisés pour les administrateurs d'instance."

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15355,8 +15355,8 @@ msgstr "Synchroniser l'utilisateur pour"

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgstr "Coller ici la clé GnuPG de l’utilisateur, ou essayer de le récupérer depuis le serveur de clés du MIT en cliquant sur « Fetch GnuPG key » ci-dessous."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr "Coller ici la clé GnuPG de l’utilisateur, ou essayer de le récupérer depuis le serveur de clés du CIRCL en cliquant sur « Fetch GnuPG key » ci-dessous."

#: View/Users/admin_add.ctp:72
#: View/Users/admin_edit.ctp:66
@@ -12464,7 +12464,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -13164,7 +13164,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr "サイト管理者用のインスタンスのデバッグレベル。 この機能により、サイト管理者は、他のユーザーに公開することなく、動作中のインスタンスでデバッグモードを実行できます。 debug と site_admin_debug の最も冗長なオプションは、サイト管理者に使用されます。"

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15347,8 +15347,8 @@ msgstr "同期ユーザー用"

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgstr "ユーザーの GnuPG キーをここに貼り付けるか、下の \"GnuPG キーを取得\"をクリックして MIT キーサーバーから取得します。"
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr "ユーザーの GnuPG キーをここに貼り付けるか、下の \"GnuPG キーを取得\"をクリックして CIRCL キーサーバーから取得します。"

#: View/Users/admin_add.ctp:72
#: View/Users/admin_edit.ctp:66
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr ""

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15316,7 +15316,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3891,8 +3891,8 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr "Feilsøkingsnivået for forekomsten for nettstedadministratorer. Denne funksjonen tillater at webansvarlige kan kjøre feilsøkingsmodus på en levende forekomst uten å utsette den for andre brukere. Det mest fordelaktige alternativet for feilsøking og site_admin_debug brukes til administratorer på nettstedet."

#: Model/Server.php:2254
msgid "Failed (partially?) because of validation errors: "
msgstr "Mislyktes (delvis?) På grunn av valideringsfeil: "
msgid "Failed (partially?) because of errors: "
msgstr "Mislyktes (delvis?) På grunn av feil: "

#: Model/Server.php:2258
msgid "Blocked an edit to an event that was created locally. This can happen if a synchronised event that was created on this instance was modified by an administrator on the remote side."
@@ -15945,8 +15945,8 @@ msgstr "Synkroniser brukeren for"
#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
#: View/Users/edit.ctp:23
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgstr "Lim inn brukerens GnuPG-nøkkel her, eller prøv å hente den fra MIT-nøkkelserveren ved å klikke på \"Hent GnuPG-nøkkel\" nedenfor."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr "Lim inn brukerens GnuPG-nøkkel her, eller prøv å hente den fra CIRCL-nøkkelserveren ved å klikke på \"Hent GnuPG-nøkkel\" nedenfor."

#: View/Users/admin_add.ctp:72
#: View/Users/admin_edit.ctp:66
@@ -13159,7 +13159,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr ""

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15316,7 +15316,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr ""

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15316,7 +15316,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr "Уровень отладки приложения для администраторов сайта. Данный функционал позволяет администраторам сайта включать режим отладки на рабтающих инстансах без воздействия на других пользователей."

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15332,8 +15332,8 @@ msgstr "Синхронизировать пользователя"

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgstr "Вставьте GnuPG ключ пользователя сюда или нажмите кнопку \"Получить GnuPG ключ\" для получения ключа с сервера MIT."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr "Вставьте GnuPG ключ пользователя сюда или нажмите кнопку \"Получить GnuPG ключ\" для получения ключа с сервера CIRCL."

#: View/Users/admin_add.ctp:72
#: View/Users/admin_edit.ctp:66
@@ -3705,7 +3705,7 @@ msgid "The debug level of the instance for site admins. This feature allows site
msgstr ""

#: Model/Server.php:2172
msgid "Failed (partially?) because of validation errors: "
msgid "Failed (partially?) because of errors: "
msgstr ""

#: Model/Server.php:2176
@@ -15316,7 +15316,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -13159,7 +13159,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -12464,7 +12464,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -13159,7 +13159,7 @@ msgstr ""

#: View/Users/admin_add.ctp:70
#: View/Users/admin_edit.ctp:64
msgid "Paste the user's GnuPG key here or try to retrieve it from the MIT key server by clicking on \"Fetch GnuPG key\" below."
msgid "Paste the user's GnuPG key here or try to retrieve it from the CIRCL key server by clicking on \"Fetch GnuPG key\" below."
msgstr ""

#: View/Users/admin_add.ctp:72
@@ -3453,13 +3453,12 @@ public function _add(&$data, $fromXml, $user, $org_id = 0, $passAlong = null, $f
}
if (isset($data['Event']['uuid'])) {
// check if the uuid already exists
$existingEventCount = $this->find('count', array('conditions' => array('Event.uuid' => $data['Event']['uuid'])));
if ($existingEventCount > 0) {
$existingEvent = $this->find('first', array('conditions' => array('Event.uuid' => $data['Event']['uuid'])));
if ($existingEvent) {
// RESTful, set response location header so client can find right URL to edit
if ($fromPull) {
return false;
}
$existingEvent = $this->find('first', array('conditions' => array('Event.uuid' => $data['Event']['uuid'])));
if ($fromXml) {
$created_id = $existingEvent['Event']['id'];
}
@@ -49,8 +49,7 @@ class Feed extends AppModel

public function urlOrExistingFilepath($fields)
{
$input_source = empty($this->data['Feed']['input_source']) ? 'network' : $this->data['Feed']['input_source'];
if ($input_source == 'local') {
if ($this->isFeedLocal($this->data)) {
if ($this->data['Feed']['source_format'] == 'misp') {
if (!is_dir($this->data['Feed']['url'])) {
return 'For MISP type local feeds, please specify the containing directory.';
@@ -77,29 +76,17 @@ public function getFeedTypesOptions()
return $result;
}

// gets the event UUIDs from the feed by ID
// returns an array with the UUIDs of events that are new or that need updating
/**
* Gets the event UUIDs from the feed by ID
* Returns an array with the UUIDs of events that are new or that need updating
* @param array $feed
* @param HttpSocket $HttpSocket
* @return array
* @throws Exception
*/
public function getNewEventUuids($feed, $HttpSocket)
{
$result = array();
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($feed['Feed']['url'] . '/manifest.json')) {
$data = file_get_contents($feed['Feed']['url'] . '/manifest.json');
}
} else {
$request = $this->__createFeedRequest($feed['Feed']['headers']);
$uri = $feed['Feed']['url'] . '/manifest.json';
$response = $HttpSocket->get($uri, '', $request);
if ($response->code != 200) {
return 1;
}
$data = $response->body;
unset($response);
}
$manifest = json_decode($data, true);
if (!$manifest) {
return 2;
}
$manifest = $this->downloadManifest($feed, $HttpSocket);
$this->Event = ClassRegistry::init('Event');
$events = $this->Event->find('all', array(
'conditions' => array(
@@ -108,147 +95,119 @@ public function getNewEventUuids($feed, $HttpSocket)
'recursive' => -1,
'fields' => array('Event.id', 'Event.uuid', 'Event.timestamp')
));
$result = array('add' => array(), 'edit' => array());
foreach ($events as $event) {
if ($event['Event']['timestamp'] < $manifest[$event['Event']['uuid']]['timestamp']) {
$result['edit'][] = array('uuid' => $event['Event']['uuid'], 'id' => $event['Event']['id']);
$eventUuid = $event['Event']['uuid'];
if ($event['Event']['timestamp'] < $manifest[$eventUuid]['timestamp']) {
$result['edit'][] = array('uuid' => $eventUuid, 'id' => $event['Event']['id']);
} else {
$this->__cleanupFile($feed, '/' . $event['Event']['uuid'] . '.json');
$this->__cleanupFile($feed, '/' . $eventUuid . '.json');
}
unset($manifest[$event['Event']['uuid']]);
}
if (!empty($manifest)) {
$result['add'] = array_keys($manifest);
unset($manifest[$eventUuid]);
}
// Rest events in manifest does't exists, they will be added
$result['add'] = array_keys($manifest);
return $result;
}

/**
* @param array $feed
* @param HttpSocket $HttpSocket
* @return array
* @throws Exception
*/
public function getCache($feed, $HttpSocket)
{
$result = array();
$request = $this->__createFeedRequest($feed['Feed']['headers']);
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($feed['Feed']['url'] . '/hashes.csv')) {
$data = file_get_contents($feed['Feed']['url'] . '/hashes.csv');
if (empty($data)) {
return false;
}
} else {
return false;
}
} else {
$uri = $feed['Feed']['url'] . '/hashes.csv';
try {
$response = $HttpSocket->get($uri, '', $request);
} catch (Exception $e) {
return false;
}
if ($response->code != 200) {
return false;
}
$data = $response->body;
unset($response);
$uri = $feed['Feed']['url'] . '/hashes.csv';
$data = $this->feedGetUri($feed, $uri, $HttpSocket);

if (empty($data)) {
throw new Exception("File '$uri' with hashes for cache filling is empty.");
}
try {
$data = trim($data);
$data = explode("\n", $data);
foreach ($data as $k => $v) {
$data[$k] = explode(',', $v);
}
} catch (Exception $e) {
return false;

$data = trim($data);
$data = explode("\n", $data);
$result = array();
foreach ($data as $v) {
$result[] = explode(',', $v);
}
return $data;
return $result;
}


public function getManifest($feed, $HttpSocket)
/**
* @param array $feed
* @param HttpSocket $HttpSocket
* @return array
* @throws Exception
*/
private function downloadManifest($feed, $HttpSocket)
{
$result = array();
$request = $this->__createFeedRequest($feed['Feed']['headers']);
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($feed['Feed']['url'] . '/manifest.json')) {
$data = file_get_contents($feed['Feed']['url'] . '/manifest.json');
if (empty($data)) {
return false;
}
} else {
throw new NotFoundException('Invalid file.');
}
} else {
$uri = $feed['Feed']['url'] . '/manifest.json';
try {
$response = $HttpSocket->get($uri, '', $request);
} catch (Exception $e) {
return $e->getMessage();
}
if ($response->code != 200) {
return 'Fetching the manifest failed with error: ' . $response->code;
}
$data = $response->body;
unset($response);
}
try {
$events = json_decode($data, true);
} catch (Exception $e) {
return 'Invalid MISP JSON returned.';
$manifestUrl = $feed['Feed']['url'] . '/manifest.json';
$data = $this->feedGetUri($feed, $manifestUrl, $HttpSocket);

$manifest = json_decode($data, true);
if ($manifest === null) {
throw new Exception('Could not parse manifest JSON: ' . json_last_error_msg(), json_last_error());
}
$events = $this->__filterEventsIndex($events, $feed);
return $events;

return $manifest;
}

private function __getRecursive($url, $query, $request, $iterations = 0)
/**
* @param array $feed
* @param HttpSocket $HttpSocket
* @return array
* @throws Exception
*/
public function getManifest($feed, $HttpSocket)
{
if ($iterations == 5) {
return false;
}
$HttpSocket = $this->__setupHttpSocket(false);
$response = $HttpSocket->get($url, $query, $request);
if ($response->code == 302 || $response->code == 301) {
$response = $this->__getRecursive($response['header']['Location'], $query, $request, $iterations + 1);
}
return $response;
$events = $this->downloadManifest($feed, $HttpSocket);
$events = $this->__filterEventsIndex($events, $feed);
return $events;
}

/**
* @param array $feed
* @param HttpSocket $HttpSocket
* @param string $type
* @param int|string $page
* @param int $limit
* @param array $params
* @return array|bool
* @throws Exception
*/
public function getFreetextFeed($feed, $HttpSocket, $type = 'freetext', $page = 1, $limit = 60, &$params = array())
{
$result = array();
$data = '';
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($feed['Feed']['url'])) {
$data = file_get_contents($feed['Feed']['url']);
}
} else {
$isLocal = $this->isFeedLocal($feed);
$data = false;

if (!$isLocal) {
$feedCache = APP . 'tmp' . DS . 'cache' . DS . 'misp_feed_' . intval($feed['Feed']['id']) . '.cache';
$doFetch = true;
if (file_exists($feedCache)) {
$file = new File($feedCache);
if (time() - $file->lastChange() < 600) {
$doFetch = false;
$data = file_get_contents($feedCache);
$data = $file->read();
if ($data === false) {
throw new Exception("Could not read feed cache file '$feedCache'.");
}
}
}
if ($doFetch) {
$fetchIssue = false;
try {
$request = $this->__createFeedRequest($feed['Feed']['headers']);
$response = $this->__getRecursive($feed['Feed']['url'], '', $request);
//$response = $HttpSocket->get($feed['Feed']['url'], '', array());
} catch (Exception $e) {
return $e->getMessage();
}
if ($response->code == 200) {
$redis = $this->setupRedis();
if ($redis === false) {
return 'Could not reach Redis.';
}
$redis->del('misp:feed_cache:' . $feed['Feed']['id']);
$data = $response->body;
file_put_contents($feedCache, $data);
} else {
return 'Invalid response code returned: ' . $response->code;
}

if ($data === false) {
$feedUrl = $feed['Feed']['url'];
$data = $this->feedGetUri($feed, $feedUrl, $HttpSocket, true);

if (!$isLocal) {
$redis = $this->setupRedis();
if ($redis === false) {
throw new Exception('Could not reach Redis.');
}
$redis->del('misp:feed_cache:' . $feed['Feed']['id']);
file_put_contents($feedCache, $data);
}
}

App::uses('ComplexTypeTool', 'Tools');
$complexTypeTool = new ComplexTypeTool();
$this->Warninglist = ClassRegistry::init('Warninglist');
@@ -449,76 +408,57 @@ public function attachFeedCorrelations($objects, $user, &$event, $overrideLimit

public function downloadFromFeed($actions, $feed, $HttpSocket, $user, $jobId = false)
{
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->read(null, $jobId);
$email = "Scheduled job";
}
$total = 0;
if (isset($actions['add']) && !empty($actions['add'])) {
$total += count($actions['add']);
}
if (isset($actions['edit']) && !empty($actions['edit'])) {
$total += count($actions['edit']);
}
$total = count($actions['add']) + count($actions['edit']);
$currentItem = 0;
$this->Event = ClassRegistry::init('Event');
$results = array();
$filterRules = false;
if (isset($feed['Feed']['rules']) && !empty($feed['Feed']['rules'])) {
$filterRules = json_decode($feed['Feed']['rules'], true);
}
if (isset($actions['add']) && !empty($actions['add'])) {
foreach ($actions['add'] as $uuid) {
$filterRules = $this->__prepareFilterRules($feed);

foreach ($actions['add'] as $uuid) {
try {
$result = $this->__addEventFromFeed($HttpSocket, $feed, $uuid, $user, $filterRules);
$this->__cleanupFile($feed, '/' . $uuid . '.json');
if ($result === 'blocked') {
continue;
}
if ($result === true) {
if ($result !== 'blocked') {
$results['add']['success'] = $uuid;
} else {
$results['add']['fail'] = array('uuid' => $uuid, 'reason' => $result);
}
if ($jobId) {
$job->id = $jobId;
$job->saveField('progress', 100 * (($currentItem + 1) / $total));
}
$currentItem++;

} catch (Exception $e) {
CakeLog::error($this->exceptionAsMessage("Could not add event '$uuid' from feed {$feed['Feed']['id']}.", $e));
$results['add']['fail'] = array('uuid' => $uuid, 'reason' => $e->getMessage());
}

$this->__cleanupFile($feed, '/' . $uuid . '.json');
$this->jobProgress($jobId, null, 100 * (($currentItem + 1) / $total));
$currentItem++;
}
if (isset($actions['edit']) && !empty($actions['edit'])) {
foreach ($actions['edit'] as $editTarget) {
$uuid = $editTarget['uuid'];
$result = $this->__updateEventFromFeed($HttpSocket, $feed, $editTarget['uuid'], $editTarget['id'], $user, $filterRules);
if ($result === 'blocked') {
continue;
}
$this->__cleanupFile($feed, '/' . $uuid . '.json');
if ($result === true) {

foreach ($actions['edit'] as $editTarget) {
$uuid = $editTarget['uuid'];
try {
$result = $this->__updateEventFromFeed($HttpSocket, $feed, $uuid, $editTarget['id'], $user, $filterRules);
if ($result !== 'blocked') {
$results['edit']['success'] = $uuid;
} else {
$results['edit']['fail'] = array('uuid' => $uuid, 'reason' => $result);
}
if ($jobId && $currentItem % 10 == 0) {
$job->id = $jobId;
$job->saveField('progress', 100 * (($currentItem + 1) / $total));
}
$currentItem++;
} catch (Exception $e) {
CakeLog::error($this->exceptionAsMessage("Could not edit event '$uuid' from feed {$feed['Feed']['id']}.", $e));
$results['edit']['fail'] = array('uuid' => $uuid, 'reason' => $e->getMessage());
}

$this->__cleanupFile($feed, '/' . $uuid . '.json');
if ($currentItem % 10 == 0) {
$this->jobProgress($jobId, null, 100 * (($currentItem + 1) / $total));
}
$currentItem++;
}

return $results;
}

private function __createFeedRequest($headers = false)
{
$version = $this->checkMISPVersion();
$version = implode('.', $version);
try {
$commit = trim(shell_exec('git log --pretty="%H" -n1 HEAD'));
} catch (Exception $e) {
$commit = false;
}
$commit = trim(shell_exec('git log --pretty="%H" -n1 HEAD'));

$result = array(
'header' => array(
'Accept' => 'application/json',
@@ -583,9 +523,9 @@ private function __checkIfEventBlockedByFilter($event, $filterRules)

private function __filterEventsIndex($events, $feed)
{
$filterRules = array();
if (isset($feed['Feed']['rules']) && !empty($feed['Feed']['rules'])) {
$filterRules = json_decode($feed['Feed']['rules'], true);
$filterRules = $this->__prepareFilterRules($feed);
if (!$filterRules) {
$filterRules = array();
}
foreach ($events as $k => $event) {
if (isset($filterRules['orgs']['OR']) && !empty($filterRules['orgs']['OR']) && !in_array($event['Orgc']['name'], $filterRules['orgs']['OR'])) {
@@ -632,6 +572,13 @@ private function __filterEventsIndex($events, $feed)
return $events;
}

/**
* @param array $feed
* @param string $uuid
* @param $user Not used
* @return array|bool
* @throws Exception
*/
public function downloadAndSaveEventFromFeed($feed, $uuid, $user)
{
$event = $this->downloadEventFromFeed($feed, $uuid, $user);
@@ -641,24 +588,19 @@ public function downloadAndSaveEventFromFeed($feed, $uuid, $user)
return $this->__saveEvent($event, $user);
}

/**
* @param array $feed
* @param string $uuid
* @param $user Not used
* @return bool|string|array
* @throws Exception
*/
public function downloadEventFromFeed($feed, $uuid, $user)
{
$path = $feed['Feed']['url'] . '/' . $uuid . '.json';
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($path)) {
$data = file_get_contents($path);
}
} else {
$HttpSocket = $this->__setupHttpSocket($feed);
$request = $this->__createFeedRequest($feed['Feed']['headers']);
$response = $HttpSocket->get($path, '', $request);
if ($response->code != 200) {
return false;
}
$data = $response->body;
unset($response->body);
}
return $this->__prepareEvent($data, $feed);
$filerRules = $this->__prepareFilterRules($feed);
$HttpSocket = $this->isFeedLocal($feed) ? false : $this->__setupHttpSocket($feed);
$event = $this->downloadAndParseEventFromFeed($feed, $uuid, $HttpSocket);
return $this->__prepareEvent($event, $feed, $filerRules);
}

private function __saveEvent($event, $user)
@@ -684,18 +626,16 @@ private function __saveEvent($event, $user)
return $result;
}

private function __prepareEvent($body, $feed)
private function __prepareEvent($event, $feed, $filterRules)
{
$filterRules = $this->__prepareFilterRules($feed);
$event = json_decode($body, true);
if (isset($event['response'])) {
$event = $event['response'];
}
if (isset($event[0])) {
$event = $event[0];
}
if (!isset($event['Event']['uuid'])) {
return false;
throw new Exception("Event uuid field missing.");
}
$event['Event']['distribution'] = $feed['Feed']['distribution'];
$event['Event']['sharing_group_id'] = $feed['Feed']['sharing_group_id'];
@@ -709,11 +649,10 @@ private function __prepareEvent($body, $feed)
$event['Event']['Tag'] = array();
}
$found = false;
if (!empty($event['Event']['Tag'])) {
foreach ($event['Event']['Tag'] as $tag) {
if (strtolower($tag['name']) === strtolower($feed['Tag']['name'])) {
$found = true;
}
foreach ($event['Event']['Tag'] as $tag) {
if (strtolower($tag['name']) === strtolower($feed['Tag']['name'])) {
$found = true;
break;
}
}
if (!$found) {
@@ -742,11 +681,19 @@ private function __prepareEvent($body, $feed)
return $event;
}

/**
* @param array $feed
* @return bool|mixed
* @throws Exception
*/
private function __prepareFilterRules($feed)
{
$filterRules = false;
if (isset($feed['Feed']['rules']) && !empty($feed['Feed']['rules'])) {
$filterRules = json_decode($feed['Feed']['rules'], true);
if ($filterRules === null) {
throw new Exception('Could not parse feed filter rules JSON: ' . json_last_error_msg(), json_last_error());
}
}
return $filterRules;
}
@@ -758,26 +705,19 @@ private function __setupHttpSocket($feed)
return ($syncTool->setupHttpSocketFeed($feed));
}

/**
* @param HttpSocket $HttpSocket
* @param array $feed
* @param string $uuid
* @param $user
* @param array|bool $filterRules
* @return array|bool|string
* @throws Exception
*/
private function __addEventFromFeed($HttpSocket, $feed, $uuid, $user, $filterRules)
{
if (!Validation::uuid($uuid)) {
return false;
}
$path = $feed['Feed']['url'] . '/' . $uuid . '.json';
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($path)) {
$data = file_get_contents($path);
}
} else {
$request = $this->__createFeedRequest($feed['Feed']['headers']);
$response = $HttpSocket->get($path, '', $request);
if ($response->code != 200) {
return false;
}
$data = $response->body;
unset($response);
}
$event = $this->__prepareEvent($data, $feed);
$event = $this->downloadAndParseEventFromFeed($feed, $uuid, $HttpSocket);
$event = $this->__prepareEvent($event, $feed, $filterRules);
if (is_array($event)) {
$this->Event = ClassRegistry::init('Event');
return $this->Event->_add($event, true, $user);
@@ -786,24 +726,20 @@ private function __addEventFromFeed($HttpSocket, $feed, $uuid, $user, $filterRul
}
}

/**
* @param HttpSocket $HttpSocket
* @param array $feed
* @param string $uuid
* @param int $eventId
* @param $user
* @param array|bool $filterRules
* @return mixed
* @throws Exception
*/
private function __updateEventFromFeed($HttpSocket, $feed, $uuid, $eventId, $user, $filterRules)
{
if (!Validation::uuid($uuid)) {
return false;
}
$path = $feed['Feed']['url'] . '/' . $uuid . '.json';
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($path)) {
$data = file_get_contents($path);
}
} else {
$request = $this->__createFeedRequest($feed['Feed']['headers']);
$response = $HttpSocket->get($path, '', $request);
if ($response->code != 200) {
return false;
}
}
$event = $this->__prepareEvent($response->body, $feed);
$event = $this->downloadAndParseEventFromFeed($feed, $uuid, $HttpSocket);
$event = $this->__prepareEvent($event, $feed, $filterRules);
$this->Event = ClassRegistry::init('Event');
return $this->Event->_edit($event, $user, $uuid, $jobId = null);
}
@@ -835,82 +771,70 @@ public function addDefaultFeeds($newFeeds)
public function downloadFromFeedInitiator($feedId, $user, $jobId = false)
{
$this->id = $feedId;
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
$job = ClassRegistry::init('Job');
$this->read();
if (isset($this->data['Feed']['settings']) && !empty($this->data['Feed']['settings'])) {
$this->data['Feed']['settings'] = json_decode($this->data['Feed']['settings'], true);
}
if (isset($this->data['Feed']['input_source']) && $this->data['Feed']['input_source'] == 'local') {
$HttpSocket = false;
} else {
$HttpSocket = $syncTool->setupHttpSocketFeed($this->data);
}

$HttpSocket = $this->isFeedLocal($this->data) ? false : $this->__setupHttpSocket($this->data);
if ($this->data['Feed']['source_format'] == 'misp') {
if ($jobId) {
$job->id = $jobId;
$job->saveField('message', 'Fetching event manifest.');
}
$actions = $this->getNewEventUuids($this->data, $HttpSocket);
if ($jobId) {
$job->id = $jobId;
$job->saveField('message', 'Fetching events.');
}
if (empty($actions)) {
if ($jobId) {
$job->id = $jobId;
$job->saveField('message', 'Job complete.');
}
$this->jobProgress($jobId, 'Fetching event manifest.');
try {
$actions = $this->getNewEventUuids($this->data, $HttpSocket);
} catch (Exception $e) {
CakeLog::error($this->exceptionAsMessage("Could not get new event uuids for feed $feedId.", $e));
$this->jobProgress($jobId, 'Could not fetch event manifest. See log for more details.');
return false;
}

if (empty($actions['add']) && empty($actions['edit'])) {
return true;
}

$total = count($actions['add']) + count($actions['edit']);
$this->jobProgress($jobId, "Fetching $total events.");
$result = $this->downloadFromFeed($actions, $this->data, $HttpSocket, $user, $jobId);
$this->__cleanupFile($this->data, '/manifest.json');
if ($jobId) {
$job->id = $jobId;
$job->saveField('message', 'Job complete.');
}
} else {
if ($jobId) {
$job->id = $jobId;
$job->saveField('message', 'Fetching data.');
$this->jobProgress($jobId, 'Fetching data.');
try {
$temp = $this->getFreetextFeed($this->data, $HttpSocket, $this->data['Feed']['source_format'], 'all');
} catch (Exception $e) {
CakeLog::error($this->exceptionAsMessage("Could not get freetext feed $feedId", $e));
$this->jobProgress($jobId, 'Could not fetch freetext feed. See log for more details.');
return false;
}
$temp = $this->getFreetextFeed($this->data, $HttpSocket, $this->data['Feed']['source_format'], 'all');

$data = array();
if (!empty($temp)) {
foreach ($temp as $key => $value) {
$data[] = array(
'category' => $value['category'],
'type' => $value['default_type'],
'value' => $value['value'],
'to_ids' => $value['to_ids']
);
}
}
if ($jobId) {
$job->saveField('progress', 50);
$job->saveField('message', 'Saving data.');
foreach ($temp as $value) {
$data[] = array(
'category' => $value['category'],
'type' => $value['default_type'],
'value' => $value['value'],
'to_ids' => $value['to_ids']
);
}
if (empty($data)) {
return true;
}
$result = $this->saveFreetextFeedData($this->data, $data, $user);
$message = 'Job complete.';
if ($result !== true) {

$this->jobProgress($jobId, 'Saving data.', 50);

try {
$result = $this->saveFreetextFeedData($this->data, $data, $user);
} catch (Exception $e) {
CakeLog::error($this->exceptionAsMessage("Could not save freetext feed data for feed $feedId.", $e));
return false;
}

$this->__cleanupFile($this->data, '');
if ($jobId) {
$job->saveField('progress', '100');
$job->saveField('message', 'Job complete.');
}
}
return $result;
}

private function __cleanupFile($feed, $file)
{
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if ($this->isFeedLocal($feed)) {
if (isset($feed['Feed']['delete_local_file']) && $feed['Feed']['delete_local_file']) {
if (file_exists($feed['Feed']['url'] . $file)) {
unlink($feed['Feed']['url'] . $file);
@@ -920,17 +844,24 @@ private function __cleanupFile($feed, $file)
return true;
}

/**
* @param array $feed
* @param array $data
* @param array $user
* @param int|bool $jobId
* @return bool
* @throws Exception
*/
public function saveFreetextFeedData($feed, $data, $user, $jobId = false)
{
$this->Event = ClassRegistry::init('Event');
$event = false;

if ($feed['Feed']['fixed_event'] && $feed['Feed']['event_id']) {
$event = $this->Event->find('first', array('conditions' => array('Event.id' => $feed['Feed']['event_id']), 'recursive' => -1));
if (empty($event)) {
return 'The target event is no longer valid. Make sure that the target event exists.';
throw new Exception("The target event is no longer valid. Make sure that the target event {$feed['Feed']['event_id']} exists.");
}
}
if (!$event) {
} else {
$this->Event->create();
$event = array(
'info' => $feed['Feed']['name'] . ' feed',
@@ -945,11 +876,11 @@ public function saveFreetextFeedData($feed, $data, $user, $jobId = false)
);
$result = $this->Event->save($event);
if (!$result) {
return 'Something went wrong while creating a new event.';
throw new Exception('Something went wrong while creating a new event.');
}
$event = $this->Event->find('first', array('conditions' => array('Event.id' => $this->Event->id), 'recursive' => -1));
if (empty($event)) {
return 'The newly created event is no longer valid. Make sure that the target event exists.';
throw new Exception("The newly created event is no longer valid. Make sure that the target event {$this->Event->id} exists.");
}
if ($feed['Feed']['fixed_event']) {
$feed['Feed']['event_id'] = $event['Event']['id'];
@@ -969,16 +900,15 @@ public function saveFreetextFeedData($feed, $data, $user, $jobId = false)
'fields' => array('id', 'value1', 'value2')
));
$event['Attribute'] = array();
foreach ($temp as $k => $t) {
foreach ($temp as $t) {
if (!empty($t['Attribute']['value2'])) {
$t['Attribute']['value'] = $t['Attribute']['value1'] . '|' . $t['Attribute']['value2'];
$value = $t['Attribute']['value1'] . '|' . $t['Attribute']['value2'];
} else {
$t['Attribute']['value'] = $t['Attribute']['value1'];
$value = $t['Attribute']['value1'];
}
$event['Attribute'][$t['Attribute']['id']] = $t['Attribute']['value'];
$event['Attribute'][$t['Attribute']['id']] = $value;
}
unset($temp);
$to_delete = array();
foreach ($data as $k => $dataPoint) {
$finder = array_search($dataPoint['value'], $event['Attribute']);
if ($finder !== false) {
@@ -987,40 +917,35 @@ public function saveFreetextFeedData($feed, $data, $user, $jobId = false)
}
}
if ($feed['Feed']['delta_merge']) {
foreach ($event['Attribute'] as $k => $attribute) {
$to_delete[] = $k;
}
$to_delete = array_keys($event['Attribute']);
if (!empty($to_delete)) {
$this->Event->Attribute->deleteAll(array('Attribute.id' => $to_delete, 'Attribute.deleted' => 0));
}
}
}
$data = array_values($data);
if (empty($data)) {
return true;
}

$data = array_values($data);
$uniqueValues = array();
foreach ($data as $key => $value) {
if (in_array($value['value'], $uniqueValues)) {
if (isset($uniqueValues[$value['value']])) {
unset($data[$key]);
continue;
}
$data[$key]['event_id'] = $event['Event']['id'];
$data[$key]['distribution'] = $feed['Feed']['distribution'];
$data[$key]['sharing_group_id'] = $feed['Feed']['sharing_group_id'];
$data[$key]['to_ids'] = $feed['Feed']['override_ids'] ? 0 : $data[$key]['to_ids'];
$uniqueValues[] = $data[$key]['value'];
$data[$key]['to_ids'] = $feed['Feed']['override_ids'] ? 0 : $value['to_ids'];
$uniqueValues[$value['value']] = true;
}
$data = array_values($data);
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->id = $jobId;
}
foreach ($data as $k => $chunk) {
$this->Event->Attribute->create();
$this->Event->Attribute->save($chunk);
if ($jobId && $k % 100 == 0) {
$job->saveField('progress', 50 + round((50 * ((($k + 1) * 100) / count($data)))));
if ($k % 100 == 0) {
$this->jobProgress($jobId, null, 50 + round((50 * ((($k + 1) * 100) / count($data)))));
}
}
if (!empty($data)) {
@@ -1037,6 +962,13 @@ public function saveFreetextFeedData($feed, $data, $user, $jobId = false)
return true;
}

/**
* @param $user Not used
* @param int|bool $jobId
* @param string $scope
* @return bool Returns true if at least one feed was cached sucessfully.
* @throws Exception
*/
public function cacheFeedInitiator($user, $jobId = false, $scope = 'freetext')
{
$params = array(
@@ -1046,7 +978,7 @@ public function cacheFeedInitiator($user, $jobId = false, $scope = 'freetext')
);
$redis = $this->setupRedis();
if ($redis === false) {
return 'Redis not reachable.';
throw new Exception('Could not reach Redis.');
}
if ($scope !== 'all') {
if (is_numeric($scope)) {
@@ -1056,27 +988,26 @@ public function cacheFeedInitiator($user, $jobId = false, $scope = 'freetext')
} elseif ($scope == 'misp') {
$redis->del('misp:feed_cache:event_uuid_lookup:');
$params['conditions']['source_format'] = 'misp';
} else {
throw new InvalidArgumentException("Invalid value for scope, it must be integer or 'freetext', 'csv', 'misp' or 'all' string.");
}
} else {
$redis->del('misp:feed_cache:combined');
$redis->del('misp:feed_cache:event_uuid_lookup:');
}
$feeds = $this->find('all', $params);
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->id = $jobId;
if (!$job->exists()) {
$jobId = false;
}
}
$atLeastOneSuccess = false;
foreach ($feeds as $k => $feed) {
$this->__cacheFeed($feed, $redis, $jobId);
if ($jobId) {
$job->saveField('progress', 100 * $k / count($feeds));
$job->saveField('message', 'Feed ' . $feed['Feed']['id'] . ' cached.');
if ($this->__cacheFeed($feed, $redis, $jobId)) {
$message = 'Feed ' . $feed['Feed']['id'] . ' cached.';
$atLeastOneSuccess = true;
} else {
$message = 'Failed to cache feed ' . $feed['Feed']['id'] . '. See logs for more details.';
}

$this->jobProgress($jobId, $message, 100 * $k / count($feeds));
}
return true;
return $atLeastOneSuccess;
}

public function attachFeedCacheTimestamps($data)
@@ -1093,11 +1024,7 @@ public function attachFeedCacheTimestamps($data)

private function __cacheFeed($feed, $redis, $jobId = false)
{
if ($feed['Feed']['input_source'] == 'local') {
$HttpSocket = false;
} else {
$HttpSocket = $this->__setupHttpSocket($feed);
}
$HttpSocket = $this->isFeedLocal($feed) ? false : $this->__setupHttpSocket($feed);
if ($feed['Feed']['source_format'] == 'misp') {
return $this->__cacheMISPFeed($feed, $redis, $HttpSocket, $jobId);
} else {
@@ -1107,131 +1034,111 @@ private function __cacheFeed($feed, $redis, $jobId = false)

private function __cacheFreetextFeed($feed, $redis, $HttpSocket, $jobId = false)
{
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->id = $jobId;
if (!$job->exists()) {
$jobId = false;
}
}
$values = $this->getFreetextFeed($feed, $HttpSocket, $feed['Feed']['source_format'], 'all');
if (!empty($values)) {
foreach ($values as $k => $value) {
$redis->sAdd('misp:feed_cache:' . $feed['Feed']['id'], md5($value['value']));
$redis->sAdd('misp:feed_cache:combined', md5($value['value']));
if ($jobId && ($k % 1000 == 0)) {
$job->saveField('message', 'Feed ' . $feed['Feed']['id'] . ': ' . $k . ' values cached.');
}
$feedId = $feed['Feed']['id'];

try {
$values = $this->getFreetextFeed($feed, $HttpSocket, $feed['Feed']['source_format'], 'all');
} catch (Exception $e) {
CakeLog::error($this->exceptionAsMessage("Could not get freetext feed $feedId", $e));
$this->jobProgress($jobId, 'Could not fetch freetext feed. See log for more details.');
return false;
}

foreach ($values as $k => $value) {
$redis->sAdd('misp:feed_cache:' . $feedId, md5($value['value']));
$redis->sAdd('misp:feed_cache:combined', md5($value['value']));
if ($k % 1000 == 0) {
$this->jobProgress($jobId, "Feed $feedId: $k/" . count($values) . " values cached.");
}
$redis->set('misp:feed_cache_timestamp:' . $feed['Feed']['id'], time());
return true;
}
return false;
$redis->set('misp:feed_cache_timestamp:' . $feedId, time());
return true;
}

private function __cacheMISPFeedTraditional($feed, $redis, $HttpSocket, $jobId = false)
{
$feedId = $feed['Feed']['id'];
$this->Attribute = ClassRegistry::init('Attribute');
$manifest = $this->getManifest($feed, $HttpSocket);
if (!empty($manifest)) {
$redis->del('misp:feed_cache:' . $feed['Feed']['id']);
} else {
try {
$manifest = $this->getManifest($feed, $HttpSocket);
} catch (Exception $e) {
CakeLog::error($this->exceptionAsMessage("Could not get manifest for feed $feedId.", $e));
return false;
}

$redis->del('misp:feed_cache:' . $feedId);

$k = 0;
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->id = $jobId;
}
foreach ($manifest as $uuid => $event) {
$data = false;
$path = $feed['Feed']['url'] . '/' . $uuid . '.json';
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($path)) {
$data = file_get_contents($path);
}
} else {
$HttpSocket = $this->__setupHttpSocket($feed);
$request = $this->__createFeedRequest($feed['Feed']['headers']);
$fetchIssue = false;
try {
$response = $HttpSocket->get($path, '', $request);
} catch (Exception $e) {
$fetchIssue = true;
}
if ($fetchIssue || $response->code != 200) {
return false;
}
$data = $response->body;
try {
$event = $this->downloadAndParseEventFromFeed($feed, $uuid, $HttpSocket);
} catch (Exception $e) {
CakeLog::error($this->exceptionAsMessage("Could not get and parse event '$uuid' for feed $feedId.", $e));
return false;
}
if ($data) {
$event = json_decode($data, true);
if (!empty($event['Event']['Attribute'])) {
$pipe = $redis->multi(Redis::PIPELINE);
foreach ($event['Event']['Attribute'] as $attribute) {
if (!in_array($attribute['type'], $this->Attribute->nonCorrelatingTypes)) {
if (in_array($attribute['type'], $this->Attribute->getCompositeTypes())) {
$value = explode('|', $attribute['value']);
$redis->sAdd('misp:feed_cache:' . $feed['Feed']['id'], md5($value[0]));
$redis->sAdd('misp:feed_cache:' . $feed['Feed']['id'], md5($value[1]));
$redis->sAdd('misp:feed_cache:combined', md5($value[0]));
$redis->sAdd('misp:feed_cache:combined', md5($value[1]));
$redis->sAdd('misp:feed_cache:event_uuid_lookup:' . md5($value[0]), $feed['Feed']['id'] . '/' . $event['Event']['uuid']);
$redis->sAdd('misp:feed_cache:event_uuid_lookup:' . md5($value[1]), $feed['Feed']['id'] . '/' . $event['Event']['uuid']);
} else {
$redis->sAdd('misp:feed_cache:' . $feed['Feed']['id'], md5($attribute['value']));
$redis->sAdd('misp:feed_cache:combined', md5($attribute['value']));
$redis->sAdd('misp:feed_cache:event_uuid_lookup:' . md5($attribute['value']), $feed['Feed']['id'] . '/' . $event['Event']['uuid']);
}

if (!empty($event['Event']['Attribute'])) {
$pipe = $redis->multi(Redis::PIPELINE);
foreach ($event['Event']['Attribute'] as $attribute) {
if (!in_array($attribute['type'], $this->Attribute->nonCorrelatingTypes)) {
if (in_array($attribute['type'], $this->Attribute->getCompositeTypes())) {
$value = explode('|', $attribute['value']);
$redis->sAdd('misp:feed_cache:' . $feedId, md5($value[0]));
$redis->sAdd('misp:feed_cache:' . $feedId, md5($value[1]));
$redis->sAdd('misp:feed_cache:combined', md5($value[0]));
$redis->sAdd('misp:feed_cache:combined', md5($value[1]));
$redis->sAdd('misp:feed_cache:event_uuid_lookup:' . md5($value[0]), $feedId . '/' . $event['Event']['uuid']);
$redis->sAdd('misp:feed_cache:event_uuid_lookup:' . md5($value[1]), $feedId . '/' . $event['Event']['uuid']);
} else {
$redis->sAdd('misp:feed_cache:' . $feedId, md5($attribute['value']));
$redis->sAdd('misp:feed_cache:combined', md5($attribute['value']));
$redis->sAdd('misp:feed_cache:event_uuid_lookup:' . md5($attribute['value']), $feedId . '/' . $event['Event']['uuid']);
}
}
$pipe->exec();
}
$pipe->exec();
}

$k++;
if ($jobId && ($k % 10 == 0)) {
$job->saveField('message', 'Feed ' . $feed['Feed']['id'] . ': ' . $k . ' events cached.');
if ($k % 10 == 0) {
$this->jobProgress($jobId, "Feed $feedId: $k/" . count($manifest) . " events cached.");
}
}
return true;
}

private function __cacheMISPFeedCache($feed, $redis, $HttpSocket, $jobId = false)
{
$cache = $this->getCache($feed, $HttpSocket);
if (empty($cache)) {
$feedId = $feed['Feed']['id'];

try {
$cache = $this->getCache($feed, $HttpSocket);
} catch (Exception $e) {
CakeLog::notice($this->exceptionAsMessage("Could not get cache file for $feedId.", $e));
return false;
}

$pipe = $redis->multi(Redis::PIPELINE);
$events = array();
foreach ($cache as $k => $v) {
$redis->sAdd('misp:feed_cache:' . $feed['Feed']['id'], $v[0]);
foreach ($cache as $v) {
$redis->sAdd('misp:feed_cache:' . $feedId, $v[0]);
$redis->sAdd('misp:feed_cache:combined', $v[0]);
$redis->sAdd('misp:feed_cache:event_uuid_lookup:' . $v[0], $feed['Feed']['id'] . '/' . $v[1]);
$redis->sAdd('misp:feed_cache:event_uuid_lookup:' . $v[0], $feedId . '/' . $v[1]);
}
$pipe->exec();
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->id = $jobId;
$job->saveField('message', 'Feed ' . $feed['Feed']['id'] . ': cached via quick cache.');
}
$this->jobProgress($jobId, "Feed $feedId: cached via quick cache.");
return true;
}

private function __cacheMISPFeed($feed, $redis, $HttpSocket, $jobId = false)
{
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->id = $jobId;
if (!$job->exists()) {
$jobId = false;
}
}
$result = true;
if (!$this->__cacheMISPFeedCache($feed, $redis, $HttpSocket, $jobId)) {
$this->__cacheMISPFeedTraditional($feed, $redis, $HttpSocket, $jobId);
$result = $this->__cacheMISPFeedTraditional($feed, $redis, $HttpSocket, $jobId);
};
$redis->set('misp:feed_cache_timestamp:' . $feed['Feed']['id'], time());
return true;
if ($result) {
$redis->set('misp:feed_cache_timestamp:' . $feed['Feed']['id'], time());
}
return $result;
}

public function compareFeeds($id = false)
@@ -1596,4 +1503,143 @@ public function searchCaches($value)
}
return $hits;
}

/**
* Download and parse event from feed.
* @param array $feed
* @param string $eventUuid
* @param HttpSocket $HttpSocket
* @return array
* @throws Exception
*/
private function downloadAndParseEventFromFeed($feed, $eventUuid, $HttpSocket)
{
if (!Validation::uuid($eventUuid)) {
throw new InvalidArgumentException("Given event UUID '$eventUuid' is invalid.");
}

$path = $feed['Feed']['url'] . '/' . $eventUuid . '.json';
$data = $this->feedGetUri($feed, $path, $HttpSocket);
$event = json_decode($data, true);
if ($event === null) {
throw new Exception('Could not parse event JSON: ' . json_last_error_msg(), json_last_error());
}

return $event;
}

/**
* @param array $feed
* @param string $uri
* @param HttpSocket $HttpSocket
* @param bool $followRedirect
* @return string
* @throws Exception
*/
private function feedGetUri($feed, $uri, $HttpSocket, $followRedirect = false)
{
if ($this->isFeedLocal($feed)) {
if (file_exists($uri)) {
$data = file_get_contents($uri);
if ($data === false) {
throw new Exception("Could not read local file '$uri'.");
}
} else {
throw new Exception("Local file '$uri' doesn't exists.");
}
} else {
$request = $this->__createFeedRequest($feed['Feed']['headers']);

if ($followRedirect) {
$response = $this->getFollowRedirect($HttpSocket, $uri, $request);
} else {
$response = $HttpSocket->get($uri, array(), $request);
}

if ($response === false) {
throw new Exception("Could not reach '$uri'.");
} else if ($response->code != 200) { // intentionally !=
throw new Exception("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
}
$data = $response->body;
}

return $data;
}

/**
* It should be possible to use 'redirect' $request attribute, but because HttpSocket contains bug that require
* certificate for first domain even when redirect to another domain, we need to use own solution.
*
* @param HttpSocket $HttpSocket
* @param string $url
* @param array $request
* @param int $iterations
* @return false|HttpSocketResponse
* @throws Exception
*/
private function getFollowRedirect(HttpSocket $HttpSocket, $url, $request, $iterations = 5)
{
for ($i = 0; $i < $iterations; $i++) {
$response = $HttpSocket->get($url, array(), $request);
if ($response->isRedirect()) {
$HttpSocket = $this->__setupHttpSocket(null); // Replace $HttpSocket with fresh instance
$url = trim($response->getHeader('Location'), '=');
} else {
return $response;
}
}

throw new Exception("Maximum number of iteration reached.");
}

/**
* @param array $feed
* @return bool
*/
private function isFeedLocal($feed)
{
return isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] === 'local';
}

/**
* @param int|null $jobId
* @param string|null $message
* @param int|null $progress
*/
private function jobProgress($jobId = null, $message = null, $progress = null)
{
if ($jobId) {
$job = ClassRegistry::init('Job');

$jobData = array($job->primaryKey => $jobId);
if ($message) {
$jobData['message'] = $message;
}
if ($progress) {
$jobData['progress'] = $progress;
}
try {
$job->save($jobData);
} catch (Exception $e) {
// ignore error during saving information about job
}
}
}

/**
* @param string $message
* @param Exception $exception
* @return string
*/
private function exceptionAsMessage($message, $exception)
{
$message = sprintf("%s\n[%s] %s",
$message,
get_class($exception),
$exception->getMessage()
);
$message .= "\nStack Trace:\n" . $exception->getTraceAsString();
return $message;
}
}
@@ -48,6 +48,7 @@ class Log extends AppModel
'request',
'request_delegation',
'reset_auth_key',
'security',
'serverSettingsEdit',
'tag',
'undelete',
@@ -138,8 +138,8 @@ public function afterFind($results, $primary = false)
foreach ($results as $k => $organisation) {
if (!empty($organisation['Organisation']['restricted_to_domain'])) {
$results[$k]['Organisation']['restricted_to_domain'] = json_decode($organisation['Organisation']['restricted_to_domain'], true);
foreach ($results[$k]['Organisation']['restricted_to_domain'] as $k => $v) {
$results[$k]['Organisation']['restricted_to_domain'][$k] = trim($v);
foreach ($results[$k]['Organisation']['restricted_to_domain'] as $k2 => $v) {
$results[$k]['Organisation']['restricted_to_domain'][$k2] = trim($v);
}
} else if (isset($organisation['Organisation']['restricted_to_domain'])){
$results[$k]['Organisation']['restricted_to_domain'] = array();
@@ -2528,6 +2528,7 @@ public function getEventIdsFromServer($server, $all = false, $HttpSocket=null, $
$request = $this->setupSyncRequest($server);
$uri = $url . '/events/index';
$filter_rules['minimal'] = 1;
$filter_rules['published'] = 1;
try {
$response = $HttpSocket->post($uri, json_encode($filter_rules), $request);
if ($response->isOk()) {
@@ -2549,9 +2550,38 @@ public function getEventIdsFromServer($server, $all = false, $HttpSocket=null, $
} else {
// multiple events, iterate over the array
$this->Event = ClassRegistry::init('Event');
$blacklisting = array();
if (Configure::read('MISP.enableEventBlacklisting') !== false) {
$this->EventBlacklist = ClassRegistry::init('EventBlacklist');
$blacklisting['EventBlacklist'] = array(
'index_field' => 'uuid',
'blacklist_field' => 'event_uuid'
);
}
if (Configure::read('MISP.enableOrgBlacklisting') !== false) {
$this->OrgBlacklist = ClassRegistry::init('OrgBlacklist');
$blacklisting['OrgBlacklist'] = array(
'index_field' => 'orgc_uuid',
'blacklist_field' => 'org_uuid'
);
}
foreach ($eventArray as $k => $event) {
if (1 != $event['published']) {
unset($eventArray[$k]); // do not keep non-published events
continue;
}
foreach ($blacklisting as $type => $blacklist) {
if (!empty($eventArray[$k][$blacklist['index_field']])) {
$blacklist_hit = $this->{$type}->find('first', array(
'conditions' => array($blacklist['blacklist_field'] => $eventArray[$k][$blacklist['index_field']]),
'recursive' => -1,
'fields' => array($type . '.id')
));
if (!empty($blacklist_hit)) {
unset($eventArray[$k]);
continue 2;
}
}
}
}
$this->Event->removeOlder($eventArray);
@@ -2565,20 +2595,6 @@ public function getEventIdsFromServer($server, $all = false, $HttpSocket=null, $
}
}
}
if (!empty($eventIds) && Configure::read('MISP.enableEventBlacklisting') !== false) {
$this->EventBlacklist = ClassRegistry::init('EventBlacklist');
foreach ($eventIds as $k => $eventUuid) {
$blacklistEntry = $this->EventBlacklist->find('first', array(
'conditions' => array('event_uuid' => $eventUuid),
'recursive' => -1,
'fields' => array('EventBlacklist.id')
));
if (!empty($blacklistEntry)) {
unset($eventIds[$k]);
}
}
}
$eventIds = array_values($eventIds);
return $eventIds;
}
if ($response->code == '403') {
@@ -4193,7 +4209,7 @@ public function dbSpaceUsage()
}
return $result;
}

}

public function writeableDirsDiagnostics(&$diagnostic_errors)
@@ -4315,8 +4331,17 @@ public function gpgDiagnostics(&$diagnostic_errors)
if (Configure::read('GnuPG.email') && Configure::read('GnuPG.homedir')) {
$continue = true;
try {
require_once 'Crypt/GPG.php';
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'), 'gpgconf' => Configure::read('GnuPG.gpgconf'), 'binary' => (Configure::read('GnuPG.binary') ? Configure::read('GnuPG.binary') : '/usr/bin/gpg')));
if (!class_exists('Crypt_GPG')) {
if (!stream_resolve_include_path('Crypt/GPG.php')) {
throw new Exception("Crypt_GPG is not installed");
}
require_once 'Crypt/GPG.php';
}
$gpg = new Crypt_GPG(array(
'homedir' => Configure::read('GnuPG.homedir'),
'gpgconf' => Configure::read('GnuPG.gpgconf'),
'binary' => Configure::read('GnuPG.binary') ?: '/usr/bin/gpg'
));
} catch (Exception $e) {
$gpgStatus = 2;
$continue = false;
@@ -5172,4 +5197,65 @@ public function updateJSON()
}
return $results;
}

public function resetRemoteAuthKey($id)
{
$server = $this->find('first', array(
'recursive' => -1,
'conditions' => array('Server.id' => $id)
));
if (empty($server)) {
return __('Invalid server');
}
$HttpSocket = $this->setupHttpSocket($server);
$request = $this->setupSyncRequest($server);
$uri = $server['Server']['url'] . '/users/resetauthkey/me';
try {
$response = $HttpSocket->post($uri, '{}', $request);
} catch (Exception $e) {
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$message = 'Could not reset the remote authentication key.';
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => $id,
'email' => 'SYSTEM',
'action' => 'error',
'user_id' => 0,
'title' => 'Error: ' . $message,
));
return $message;
}
if ($response->isOk()) {
try {
$response = json_decode($response->body, true);
} catch (Exception $e) {
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$message = 'Invalid response received from the remote instance.';
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => $id,
'email' => 'SYSTEM',
'action' => 'error',
'user_id' => 0,
'title' => 'Error: ' . $message,
));
return $message;
}
if (!empty($response['message'])) {
$authkey = $response['message'];
}
if (substr($authkey, 0, 17) === 'Authkey updated: ') {
$authkey = substr($authkey, 17, 57);
}
$server['Server']['authkey'] = $authkey;
$this->save($server);
return true;
} else {
return __('Could not reset the remote authentication key.');
}
}
}
@@ -216,26 +216,6 @@ class User extends AppModel
'Containable'
);

private function __generatePassword()
{
$groups = array(
'0123456789',
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLOMNOPQRSTUVWXYZ',
'!@#$%^&*()_-'
);
$passwordLength = (Configure::read('Security.password_policy_length') && Configure::read('Security.password_policy_length') >= 12) ? Configure::read('Security.password_policy_length') : 12;
$pw = '';
for ($i = 0; $i < $passwordLength; $i++) {
$chars = implode('', $groups);
$pw .= $chars[mt_rand(0, strlen($chars)-1)];
}
foreach ($groups as $group) {
$pw .= $group[mt_rand(0, strlen($group)-1)];
}
return $pw;
}

public function beforeValidate($options = array())
{
if (!isset($this->data['User']['id'])) {
@@ -326,8 +306,7 @@ public function validateGpgkey($check)

// we have a clean, hopefully public, key here
try {
require_once 'Crypt/GPG.php';
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'), 'gpgconf' => Configure::read('GnuPG.gpgconf'), 'binary' => (Configure::read('GnuPG.binary') ? Configure::read('GnuPG.binary') : '/usr/bin/gpg')));
$gpg = $this->initializeGpg();
try {
$keyImportOutput = $gpg->importKey($check['gpgkey']);
if (!empty($keyImportOutput['fingerprint'])) {
@@ -399,7 +378,7 @@ public function complexPassword($check)
return preg_match($regex, $value);
}

public function identicalFieldValues($field=array(), $compareField=null)
public function identicalFieldValues($field = array(), $compareField = null)
{
foreach ($field as $key => $value) {
$v1 = $value;
@@ -470,10 +449,9 @@ public function verifySingleGPG($user, $gpg = false)
{
if (!$gpg) {
try {
require_once 'Crypt/GPG.php';
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'), 'gpgconf' => Configure::read('GnuPG.gpgconf'), 'binary' => (Configure::read('GnuPG.binary') ? Configure::read('GnuPG.binary') : '/usr/bin/gpg')));
$gpg = $this->initializeGpg();
} catch (Exception $e) {
$result[2] ='GnuPG is not configured on this system.';
$result[2] = 'GnuPG is not configured on this system.';
$result[0] = true;
return $result;
}
@@ -519,7 +497,6 @@ public function verifySingleGPG($user, $gpg = false)

public function verifyGPG($id = false)
{
require_once 'Crypt/GPG.php';
$this->Behaviors->detach('Trim');
$results = array();
$conditions = array('not' => array('gpgkey' => ''));
@@ -533,7 +510,7 @@ public function verifyGPG($id = false)
if (empty($users)) {
return $results;
}
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'), 'gpgconf' => Configure::read('GnuPG.gpgconf'), 'binary' => (Configure::read('GnuPG.binary') ? Configure::read('GnuPG.binary') : '/usr/bin/gpg')));
$gpg = $this->initializeGpg();
foreach ($users as $k => $user) {
$results[$user['User']['id']] = $this->verifySingleGPG($user, $gpg);
}
@@ -1049,50 +1026,46 @@ public function fetchPGPKey($email)
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocket();
$response = $HttpSocket->get('https://pgp.circl.lu/pks/lookup?search=' . $email . '&op=index&fingerprint=on');
$response = $HttpSocket->get('https://pgp.circl.lu/pks/lookup?search=' . urlencode($email) . '&op=index&fingerprint=on&options=mr');
if ($response->code != 200) {
return $response->code;
}
$string = str_replace(array("\r", "\n"), "", $response->body);
$result = preg_match_all('/<pre>pub(.*?)<\/pre>/', $string, $matches);
$results = $this->__extractPGPInfo($matches[1]);
return $results;
return $this->__extractPGPInfo($response->body);
}

private function __extractPGPInfo($lines)
private function __extractPGPInfo($body)
{
$extractionRules = array(
'key_id' => array('regex' => '/\">(.*?)<\/a>/', 'all' => false, 'alternate' => false),
'date' => array('regex' => '/([0-9]{4}\-[0-9]{2}\-[0-9]{2})/', 'all' => false, 'alternate' => false),
'fingerprint' => array('regex' => '/Fingerprint=(.*)$/m', 'all' => false, 'alternate' => false),
'uri' => array('regex' => '/<a href=\"(.*?)\">/', 'all' => false, 'alternate' => false),
'address' => array('regex' => '/<a href="\/pks\/lookup\?op=vindex[^>]*>([^\<]*)<\/a>(.*)Fingerprint/s', 'all' => true, 'alternate' => true),
);
$final = array();
$lines = explode("\n", $body);
foreach ($lines as $line) {
if (strpos($line, 'KEY REVOKED')) {
continue;
}
$temp = array();
foreach ($extractionRules as $ruleName => $rule) {
if ($rule['all']) {
preg_match_all($rule['regex'], $line, ${$ruleName});
} else {
preg_match($rule['regex'], $line, ${$ruleName});
}
if ($rule['alternate'] && isset(${$ruleName}[2]) && trim(${$ruleName}[2][0]) != '') {
$temp[$ruleName] = ${$ruleName}[2];
} else {
$temp[$ruleName] = ${$ruleName}[1];
$parts = explode(":", $line);

if ($parts[0] === 'pub') {
if (!empty($temp)) {
$final[] = $temp;
$temp = array();
}
if ($rule['all']) {
$temp[$ruleName] = $temp[$ruleName][0];

if (strpos($parts[6], 'r') !== false || strpos($parts[6], 'd') !== false || strpos($parts[6], 'e') !== false) {
continue; // skip if key is expired, revoked or disabled
}
$temp[$ruleName] = html_entity_decode($temp[$ruleName]);

$temp = array(
'fingerprint' => chunk_split($parts[1], 4, ' '),
'key_id' => substr($parts[1], -8),
'date' => date('Y-m-d', $parts[4]),
'uri' => '/pks/lookup?op=get&search=0x' . $parts[1],
);

} else if ($parts[0] === 'uid' && !empty($temp)) {
$temp['address'] = urldecode($parts[1]);
}
$temp['address'] = preg_replace('/\s{2,}/', PHP_EOL, trim($temp['address']));
}

if (!empty($temp)) {
$final[] = $temp;
}

return $final;
}

@@ -1241,9 +1214,9 @@ public function getOrgAdminsForOrg($org_id, $excludeUserId = false)
public function verifyPassword($user_id, $password)
{
$currentUser = $this->find('first', array(
'conditions' => array('User.id' => $user_id),
'recursive' => -1,
'fields' => array('User.password')
'conditions' => array('User.id' => $user_id),
'recursive' => -1,
'fields' => array('User.password')
));
if (empty($currentUser)) {
return false;
@@ -1277,4 +1250,201 @@ public function createInitialUser($org_id)
$this->save($admin);
return $authKey;
}

public function resetAllSyncAuthKeysRouter($user, $jobId = false)
{
if (Configure::read('MISP.background_jobs')) {
$job = ClassRegistry::init('Job');
$job->create();
$eventModel = ClassRegistry::init('Event');
$data = array(
'worker' => $eventModel->__getPrioWorkerIfPossible(),
'job_type' => __('reset_all_sync_api_keys'),
'job_input' => __('Reseting all API keys'),
'status' => 0,
'retries' => 0,
'org_id' => $user['org_id'],
'org' => $user['Organisation']['name'],
'message' => 'Issuing new API keys to all sync users.',
);
$job->save($data);
$jobId = $job->id;
$process_id = CakeResque::enqueue(
'prio',
'AdminShell',
array('resetSyncAuthkeys', $user['id'], $jobId),
true
);
$job->saveField('process_id', $process_id);
return true;
} else {
return $this->resetAllSyncAuthKeys($user);
}
}

public function resetAllSyncAuthKeys($user, $jobId = false)
{
$affected_users = $this->find('all', array(
'recursive' => -1,
'contain' => array('Role'),
'conditions' => array(
'OR' => array(
'Role.perm_sync' => 1,
'Role.perm_admin' => 1
),
'Role.perm_site_admin' => 0
)
));
$results = array('success' => 0, 'fails' => 0);
$user_count = count($affected_users);
if ($jobId) {
$job = ClassRegistry::init('Job');
$existingJob = $job->find('first', array(
'conditions' => array('Job.id' => $jobId),
'recursive' => -1
));
if (empty($existingJob)) {
$jobId = false;
}
}
foreach ($affected_users as $k => $affected_user) {
try {
$reset_result = $this->resetauthkey($user, $affected_user['User']['id'], true);
if ($reset_result) {
$results['success'] += 1;
} else {
$results['fails'] += 1;
}
} catch (Exception $e) {
$results['fails'] += 1;
}
if ($jobId) {
if ($k % 100 == 0) {
$job->id = $jobId;
$job->saveField('progress', 100 * (($k + 1) / count($user_count)));
$job->saveField('message', __('Reset in progress - %s/%s.', $k, $user_count));
}
}
}
if ($jobId) {
$message = __('%s authkeys reset, %s could not be reset', $results['success'], $results['fails']);
$job->saveField('progress', 100);
$job->saveField('message', $message);
$job->saveField('status', 4);
}
return $results;
}

public function resetauthkey($user, $id, $alert = false)
{
$this->id = $id;
if (!$id || !$this->exists($id)) {
return false;
}
$updatedUser = $this->read();
$oldKey = $this->data['User']['authkey'];
if (empty($user['Role']['perm_site_admin']) && !($user['Role']['perm_admin'] && $user['org_id'] == $updatedUser['User']['org_id']) && ($user['id'] != $id)) {
return false;
}
$newkey = $this->generateAuthKey();
$this->saveField('authkey', $newkey);
$this->extralog(
$user,
'reset_auth_key',
sprintf(
__('Authentication key for user %s (%s) updated.'),
$updatedUser['User']['id'],
$updatedUser['User']['email']
),
$fieldsResult = 'authkey(' . $oldKey . ') => (' . $newkey . ')',
$updatedUser
);
if ($alert) {
$baseurl = Configure::read('MISP.external_baseurl');
if (empty($baseurl)) {
$baseurl = Configure::read('MISP.baseurl');
}
$body = __(
"Dear user,\n\nan API key reset has been triggered by an administrator for your user account on %s.\n\nYour new API key is: %s\n\nPlease update your server's sync setup to reflect this change.\n\nWe apologise for the inconvenience.",
$baseurl,
$newkey
);
$bodyNoEnc = __(
"Dear user,\n\nan API key reset has been triggered by an administrator for your user account on %s.\n\nYour new API key can be retrieved by logging in using this sync user's account.\n\nPlease update your server's sync setup to reflect this change.\n\nWe apologise for the inconvenience.",
$baseurl,
$newkey
);
$this->sendEmail(
$updatedUser,
$body,
$bodyNoEnc,
__('API key reset by administrator')
);
}
return $newkey;
}

public function extralog($user, $action = null, $description = null, $fieldsResult = null, $modifiedUser = null)
{
// new data
$model = 'User';
$modelId = $user['id'];
if (!empty($modifiedUser)) {
$modelId = $modifiedUser['User']['id'];
}
if ($action == 'login') {
$description = "User (" . $user['id'] . "): " . $user['email'];
} elseif ($action == 'logout') {
$description = "User (" . $user['id'] . "): " . $user['email'];
} elseif ($action == 'edit') {
$description = "User (" . $modifiedUser['User']['id'] . "): " . $modifiedUser['User']['email'];
} elseif ($action == 'change_pw') {
$description = "User (" . $modifiedUser['User']['id'] . "): " . $modifiedUser['User']['email'];
$fieldsResult = "Password changed.";
}

// query
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => $model,
'model_id' => $modelId,
'email' => $user['email'],
'action' => $action,
'title' => $description,
'change' => isset($fieldsResult) ? $fieldsResult : ''));

// write to syslogd as well
App::import('Lib', 'SysLog.SysLog');
$syslog = new SysLog();
$syslog->write('notice', $description . ' -- ' . $action . (empty($fieldResult) ? '' : '-- ' . $fieldResult));
}

/**
* @return Crypt_GPG
* @throws Exception
*/
private function initializeGpg()
{
if (!class_exists('Crypt_GPG')) {
if (!stream_resolve_include_path('Crypt/GPG.php')) {
throw new Exception("Crypt_GPG is not installed.");
}
require_once 'Crypt/GPG.php';
}

$homedir = Configure::read('GnuPG.homedir');
if ($homedir === null) {
throw new Exception("Configuration option 'GnuPG.homedir' is not set, Crypt_GPG cannot be initialized.");
}

$options = array(
'homedir' => $homedir,
'gpgconf' => Configure::read('GnuPG.gpgconf'),
'binary' => Configure::read('GnuPG.binary') ?: '/usr/bin/gpg',
);

return new Crypt_GPG($options);
}
}
@@ -85,7 +85,7 @@
<?php
if (($isAclAdmin && (($user['User']['org_id'] == $me['org_id'])) || ('1' == $me['id'])) || ($isSiteAdmin)):
?>
<span role="button" tabindex="0" aria-label="Initiate password refresh" title="<?php echo __('Initiate password refresh');?>" class="fa fa-sync useCursorPointer" onClick="initiatePasswordReset('<?php echo $user['User']['id']; ?>');" title="<?php echo __('Create new credentials and inform user');?>" role="button" tabindex="0" aria-label="<?php echo __('Create new credentials and inform user');?>"></span>
<span role="button" tabindex="0" class="fa fa-sync useCursorPointer" onClick="initiatePasswordReset('<?php echo $user['User']['id']; ?>');" title="<?php echo __('Create new credentials and inform user');?>" role="button" tabindex="0" aria-label="<?php echo __('Create new credentials and inform user');?>"></span>
<?php
echo $this->Html->link('', array('admin' => true, 'action' => 'edit', $user['User']['id']), array('class' => 'fa fa-edit', 'title' => __('Edit'), 'aria-label' => __('Edit')));
echo $this->Form->postLink('', array('admin' => true, 'action' => 'delete', $user['User']['id']), array('class' => 'fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to delete # %s? It is highly recommended to never delete users but to disable them instead.', $user['User']['id']));
@@ -21,6 +21,7 @@
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo $this->Paginator->sort('name');?></th>
<th><?php echo __('Connection test');?></th>
<th><?php echo __('Reset API key');?></th>
<th><?php echo $this->Paginator->sort('internal');?></th>
<th><?php echo $this->Paginator->sort('push');?></th>
<th><?php echo $this->Paginator->sort('pull');?></th>
@@ -70,6 +71,20 @@ foreach ($servers as $server):
?>
</td>
<td id="connection_test_<?php echo $server['Server']['id'];?>"><span role="button" tabindex="0" aria-label="<?php echo __('Test the connection to the remote instance');?>" title="<?php echo __('Test the connection to the remote instance');?>" class="btn btn-primary" style="line-height:10px; padding: 4px 4px;" onClick="testConnection('<?php echo $server['Server']['id'];?>');"><?php echo __('Run');?></span></td>
<td id="reset_api_key_<?php echo $server['Server']['id'];?>">
<?php
echo $this->Form->postLink(
__('Reset'),
$baseurl . '/servers/resetRemoteAuthKey/' . $server['Server']['id'],
array(
'style' => 'line-height:10px; padding: 4px 4px;',
'title' => __('Remotely reset API key'),
'aria-label' => __('Remotely reset API key'),
'class' => 'btn btn-primary'
)
);
?>
</td>

<td><span class="<?php echo ($server['Server']['internal']? 'icon-ok' : 'icon-remove'); ?>" role="img" aria-label="<?php echo ($server['Server']['internal']? __('Yes') : __('No')); ?>" title="<?php echo ($server['Server']['internal']? __('Internal instance that ignores distribution level degradation *WARNING: Only use this setting if you have several internal instances and the sync link is to an internal extension of the current MISP community*') : __('Normal sync link to an external MISP instance. Distribution degradation will follow the normal rules.')); ?>"></span></td>
<td><span class="<?php echo ($server['Server']['push']? 'icon-ok' : 'icon-remove'); ?>" role="img" aria-label="<?php echo ($server['Server']['push']? __('Yes') : __('No')); ?>"></span><span class="short <?php if (!$server['Server']['push'] || empty($ruleDescription['push'])) echo "hidden"; ?>" data-toggle="popover" title="Distribution List" data-content="<?php echo $ruleDescription['push']; ?>"> (<?php echo __('Rules');?>)</span></td>
@@ -67,21 +67,3 @@ $disabledBtnText = $updateLocked ? 'title="' . __('An action is already in progr
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'adminTools'));
?>

<script type="text/javascript">
$(document).ready(function(){
$('.submitButtonToUpdateProgress').click(function() {
var form = $(this).closest("form");
$.ajax({
data: form.serialize(),
cache: false,
timeout: 100,
complete: function (data, textStatus) {
window.location.href = $('#btnShowProgress').prop('href');
},
type:"post",
url: form.prop('action')
});
});
});
</script>
@@ -67,7 +67,7 @@
?>
</div>
<?php
echo $this->Form->input('gpgkey', array('label' => __('GnuPG key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s GnuPG key here or try to retrieve it from the MIT key server by clicking on "Fetch GnuPG key" below.')));
echo $this->Form->input('gpgkey', array('label' => __('GnuPG key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s GnuPG key here or try to retrieve it from the CIRCL key server by clicking on "Fetch GnuPG key" below.')));
?>
<div class="clear"><span role="button" tabindex="0" aria-label="<?php echo __('Fetch the user\'s GnuPG key');?>" onClick="lookupPGPKey('UserEmail');" class="btn btn-inverse" style="margin-bottom:10px;"><?php echo __('Fetch GnuPG key');?></span></div>
<?php
@@ -61,7 +61,7 @@
?>
</div>
<?php
echo $this->Form->input('gpgkey', array('label' => __('GnuPG key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s GnuPG key here or try to retrieve it from the MIT key server by clicking on "Fetch GnuPG key" below.')));
echo $this->Form->input('gpgkey', array('label' => __('GnuPG key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s GnuPG key here or try to retrieve it from the CIRCL key server by clicking on "Fetch GnuPG key" below.')));
?>
<div class="clear"><span role="button" tabindex="0" aria-label="<?php echo __('Fetch the user\'s GnuPG key');?>" onClick="lookupPGPKey('UserEmail');" class="btn btn-inverse" style="margin-bottom:10px;"><?php echo __('Fetch GnuPG key');?></span></div>
<?php
@@ -1,5 +1,25 @@
<div class="users index">
<h2><?php echo __('Users');?></h2>
<?php
if ($isSiteAdmin) {
echo sprintf(
'<span>%s</span>',
__(
'Click %s to reset the API keys of all sync and org admin users in one shot. This will also automatically inform them of their new API keys.',
$this->Form->postLink(
__('here'),
$baseurl . '/users/resetAllSyncAuthKeys',
array(
'title' => __('Reset all sync user API keys'),
'aria-label' => __('Reset all sync user API keys'),
'class' => 'bold'
),
__('Are you sure you wish to reset the API keys of all users with sync privileges?')
)
)
);
}
?>
<div class="pagination">
<ul>
<?php
@@ -20,7 +20,7 @@
?>
<div class="input clear"></div>
<?php
echo $this->Form->input('gpgkey', array('label' => __('GnuPG key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s GnuPG key here or try to retrieve it from the MIT key server by clicking on "Fetch GnuPG key" below.')));
echo $this->Form->input('gpgkey', array('label' => __('GnuPG key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s GnuPG key here or try to retrieve it from the CIRCL key server by clicking on "Fetch GnuPG key" below.')));
?>
<div class="clear"><span role="button" tabindex="0" aria-label="<?php echo __('Fetch GnuPG key');?>" onClick="lookupPGPKey('UserEmail');" class="btn btn-inverse" style="margin-bottom:10px;"><?php echo __('Fetch GnuPG key');?></span></div>
<?php
@@ -2,7 +2,8 @@
"prefer-stable": true,
"minimum-stability": "dev",
"require": {
"kamisama/cake-resque": "4.1.2"
"kamisama/cake-resque": "4.1.2",
"pear/crypt_gpg": "1.6.3"
},
"suggest": {
"elasticsearch/elasticsearch": "For logging to elasticsearch",
@@ -89,7 +89,8 @@ def __init__(self, args):
idgen.set_id_namespace(Namespace(self.baseurl, self.orgname, "MISP"))
self.namespace_prefix = idgen.get_id_namespace_alias()
## MAPPING FOR ATTRIBUTES
self.simple_type_to_method = {"port": self.generate_port_observable, "domain|ip": self.generate_domain_ip_observable}
self.simple_type_to_method = {"port": self.generate_port_observable, "domain|ip": self.generate_domain_ip_observable,
"named pipe": self.generate_pipe_observable}
self.simple_type_to_method.update(dict.fromkeys(list(hash_type_attributes["single"]) + list(hash_type_attributes["composite"]) + ["filename"], self.resolve_file_observable))
self.simple_type_to_method.update(dict.fromkeys(["ip-src", "ip-dst"], self.generate_ip_observable))
self.simple_type_to_method.update(dict.fromkeys(["ip-src|port", "ip-dst|port", "hostname|port"], self.generate_socket_address_observable))
@@ -531,6 +532,17 @@ def generate_observable(self, attribute):
observable.id_ = "{}:Observable-{}".format(self.namespace_prefix, attribute_uuid)
return observable

def generate_pipe_observable(self, attribute):
attribute_uuid = attribute['uuid']
pipe_object = Pipe()
pipe.named = True
pipe.name = attribute['value']
pipe.name.condition = 'Equals'
pipe.parent.id_ = "{}PipeObject-{}".format(self.namesapce_prefix, attribute_uuid)
observable = Observable(pipe_object)
observable.id_ = "{}:Pipe-{}".format(self.namespace_prefix, attribute_uuid)
return observable

def generate_port_observable(self, attribute):
attribute_uuid = attribute['uuid']
port_object = self.create_port_object(attribute['value'])
@@ -436,7 +436,7 @@ def add_coa_stix_object(self, coa_args, coa_id):

def add_custom(self, attribute):
custom_object_id = "x-misp-object--{}".format(attribute['uuid'])
custom_object_type = "x-misp-object-{}".format(attribute['type'].replace('|', '-').lower())
custom_object_type = "x-misp-object-{}".format(attribute['type'].replace('|', '-').replace(' ', '-').lower())
labels, markings = self.create_labels(attribute)
custom_object_args = {'id': custom_object_id, 'x_misp_category': attribute['category'], 'labels': labels,
'x_misp_timestamp': self.get_datetime_from_timestamp(attribute['timestamp']),
@@ -216,7 +216,7 @@ def attributes_from_file_pattern(types, values):
attributes = []
for type_, value in zip(types, values):
if 'hashes' in type_:
hash_type = type_.split('.')[1]
hash_type = type_.split('.')[1].strip("'").replace('-', '').lower()
attributes.append({'type': hash_type, 'value': value,
'object_relation': hash_type, 'to_ids': True})
else:
@@ -578,7 +578,8 @@ def parse_custom_object(self, o, labels):
def parse_custom_attribute(self, o, labels):
attribute_type = o['type'].split('x-misp-object-')[1]
if attribute_type not in misp_types:
attribute_type = attribute_type.replace('-', '|')
replacement = ' ' if attribute_type == 'named-pipe' else '|'
attribute_type = attribute_type.replace('-', replacement)
attribute = {'type': attribute_type,
'timestamp': self.getTimestampfromDate(o['x_misp_timestamp']),
'to_ids': bool(labels[1].split('=')[1]),
@@ -108,6 +108,7 @@ def load_mapping(self):
'NetworkConnectionObjectType': self.handle_network_connection,
'NetworkSocketObjectType': self.handle_network_socket,
'PDFFileObjectType': self.handle_file,
'PipeObjectType': self.handle_pipe,
'PortObjectType': self.handle_port,
'ProcessObjectType': self.handle_process,
'SocketAddressObjectType': self.handle_socket_address,
@@ -419,6 +420,11 @@ def handle_network_socket(self, properties):
if attributes:
return "network-socket", self.return_attributes(attributes), ""

# Return type & value of a names pipe attribute
@staticmethod
def handle_pipe(properties):
return "named pipe", properties.name.value, ""

# Return type & value of a port attribute
@staticmethod
def handle_port(*kwargs):
@@ -216,11 +216,6 @@ installCoreRHEL () {
# Make git ignore filesystem permission differences
$SUDO_WWW git config core.filemode false

# Install packaged pears
sudo $RUN_PHP -- pear channel-update pear.php.net
sudo $RUN_PHP -- pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml
sudo $RUN_PHP -- pear install ${PATH_TO_MISP}/INSTALL/dependencies/Crypt_GPG/package.xml

# Create a python3 virtualenv
$SUDO_WWW $RUN_PYTHON -- virtualenv -p python3 $PATH_TO_MISP/venv
sudo mkdir /usr/share/httpd/.cache
@@ -142,7 +142,7 @@ yumInstallCoreDeps () {
sudo systemctl enable --now redis.service

PHP_INI=/etc/php.ini
sudo yum install php php-fpm php-devel php-pear \
sudo yum install php php-fpm php-devel \
php-mysqlnd \
php-mbstring \
php-xml \
@@ -200,11 +200,6 @@ installCoreRHEL () {
# Make git ignore filesystem permission differences
$SUDO_WWW git config core.filemode false

# Install packaged pears
sudo $RUN_PHP -- pear channel-update pear.php.net
sudo $RUN_PHP -- pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml
sudo $RUN_PHP -- pear install ${PATH_TO_MISP}/INSTALL/dependencies/Crypt_GPG/package.xml

# Create a python3 virtualenv
$SUDO_WWW virtualenv-3 -p python3 $PATH_TO_MISP/venv
sudo mkdir /usr/share/httpd/.cache
@@ -116,7 +116,6 @@ installDepsPhp72 () {
php php-cli \
php-dev \
php-json php-xml php-mysql php7.2-opcache php-readline php-mbstring \
php-pear \
php-redis php-gnupg \
php-gd

@@ -190,10 +189,6 @@ installCore () {

# install plyara
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install plyara

# Install Crypt_GPG and Console_CommandLine
sudo pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml
sudo pear install ${PATH_TO_MISP}/INSTALL/dependencies/Crypt_GPG/package.xml
}
# <snippet-end 1_mispCoreInstall.sh>
```
@@ -605,7 +605,6 @@ installDepsPhp70 () {
php php-cli \
php-dev \
php-json php-xml php-mysql php-opcache php-readline php-mbstring \
php-pear \
php-redis php-gnupg \
php-gd

@@ -629,7 +628,6 @@ installDepsPhp73 () {
php7.3 php7.3-cli \
php7.3-dev \
php7.3-json php7.3-xml php7.3-mysql php7.3-opcache php7.3-readline php7.3-mbstring \
php-pear \
php-redis php-gnupg \
php-gd
}
@@ -145,11 +145,6 @@ $SUDO_WWW git submodule update --init --recursive
# Make git ignore filesystem permission differences for submodules
$SUDO_WWW git submodule foreach --recursive git config core.filemode false

# Install packaged pears
sudo $RUN_PHP "pear channel-update pear.php.net"
sudo $RUN_PHP "pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml"
sudo $RUN_PHP "pear install ${PATH_TO_MISP}/INSTALL/dependencies/Crypt_GPG/package.xml"

# Create a python3 virtualenv
$SUDO_WWW $RUN_PYTHON "virtualenv -p python3 $PATH_TO_MISP/venv"
sudo mkdir /var/www/.cache
@@ -137,11 +137,6 @@ $SUDO_WWW git submodule foreach --recursive git config core.filemode false
# Make git ignore filesystem permission differences
$SUDO_WWW git config core.filemode false

# Install packaged pears
sudo $RUN_PHP "pear channel-update pear.php.net"
sudo $RUN_PHP "pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml"
sudo $RUN_PHP "pear install ${PATH_TO_MISP}/INSTALL/dependencies/Crypt_GPG/package.xml"

# Create a python3 virtualenv
$SUDO_WWW $RUN_PYTHON "virtualenv -p python3 $PATH_TO_MISP/venv"
sudo mkdir /usr/share/httpd/.cache
@@ -180,10 +180,6 @@ $SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install python-magic

# install plyara
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install plyara

# Install Crypt_GPG and Console_CommandLine
sudo pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml
sudo pear install ${PATH_TO_MISP}/INSTALL/dependencies/Crypt_GPG/package.xml
```

### 4/ CakePHP
@@ -205,10 +205,6 @@ $SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install python-magic

# install plyara
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install plyara

# Install Crypt_GPG and Console_CommandLine
sudo pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml
sudo pear install ${PATH_TO_MISP}/INSTALL/dependencies/Crypt_GPG/package.xml
```

### 4/ CakePHP
@@ -172,8 +172,6 @@ function installMISPonTsurugi() {
a2dissite 000-default
a2ensite default-ssl

pear channel-update pear.php.net
pear install Crypt_GPG
pecl channel-update pecl.php.net

yes '' |pecl install redis