chg: merge develop, fix conflicts.

pull/7939/head
Luciano Righetti 2021-11-08 11:35:20 +01:00
commit f2c9d12eae
52 changed files with 1488 additions and 932 deletions

View File

@ -117,7 +117,8 @@ MISPvars () {
# MISP configuration variables
PATH_TO_MISP="${PATH_TO_MISP:-/var/www/MISP}"
PATH_TO_MISP_SCRIPTS="${PATH_TO_MISP}/app/files/scripts"
## For future use
# TMPDIR="${TMPDIR:-$PATH_TO_MISP/app/tmp}"
FQDN="${FQDN:-misp.local}"
@ -1541,6 +1542,9 @@ coreCAKE () {
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Session.autoRegenerate" 0
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Session.timeout" 600
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Session.cookieTimeout" 3600
# Set the default temp dir
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.tmpdir" "${PATH_TO_MISP}/app/tmp"
# Change base url, either with this CLI command or in the UI
[[ ! -z ${MISP_BASEURL} ]] && ${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Baseurl $MISP_BASEURL
@ -1562,7 +1566,7 @@ coreCAKE () {
# Enable installer org and tune some configurables
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.host_org_id" 1
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.email" "info@admin.test"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.disable_emailing" true
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.disable_emailing" true --force
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.contact" "info@admin.test"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.disablerestalert" true
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.showCorrelationsOnIndex" true
@ -1573,7 +1577,7 @@ coreCAKE () {
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_services_url" "http://127.0.0.1"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_services_port" 9000
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_timeout" 120
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_authkey" ""
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_authkey" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_ssl_verify_peer" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_ssl_verify_host" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_ssl_allow_self_signed" true
@ -1632,7 +1636,7 @@ coreCAKE () {
Plugin.ElasticSearch_logging_enable
Plugin.S3_enable)
for PLUG in "${PLUGS[@]}"; do
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting ${PLUG} false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting ${PLUG} false 2> /dev/null
done
# Plugin CustomAuth tuneable
@ -1648,7 +1652,7 @@ coreCAKE () {
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_minimum_ttl" "1h"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_ttl" "1w"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_ns" "localhost."
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_ns_alt" ""
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_ns_alt" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_email" "root.localhost"
# Kafka settings
@ -1899,6 +1903,7 @@ mispmodules () {
# If you build an egg, the user you build it as need write permissions in the CWD
sudo chgrp $WWW_USER .
sudo chmod og+w .
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install pillow
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install -I -r REQUIREMENTS
sudo chgrp staff .
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install -I .
@ -3045,10 +3050,6 @@ installSupported () {
echo "Proceeding with the installation of MISP core"
space
# Set Base URL - functionLocation('generic/supportFunctions.md')
[[ -n $CORE ]] || [[ -n $ALL ]] && setBaseURL
progress 4
# Check if sudo is installed and etckeeper - functionLocation('generic/sudo_etckeeper.md')
[[ -n $CORE ]] || [[ -n $ALL ]] && checkSudoKeeper
[[ ! -z ${MISP_USER} ]] && [[ ! -f /etc/sudoers.d/misp ]] && echo "%${MISP_USER} ALL=(ALL:ALL) NOPASSWD:ALL" |sudo tee /etc/sudoers.d/misp

View File

@ -1,5 +1,5 @@
; Generated by RHash v1.3.9 on 2021-10-18 at 10:56.53
; Generated by RHash v1.4.2 on 2021-11-04 at 15:44.11
; Written by Kravchenko Aleksey (Akademgorodok) - http://rhash.sf.net/
;
; 160201 10:56.53 2021-10-18 INSTALL.sh
INSTALL.sh 8F59974F7AE69DFBF7B1C492E35F0B421AAC10C1 6F9E9C2C24880D2E69E04AB6AE490F72D8B5CBE5BB98596F4FA50C1CFEAA632F CBCFBA692B57E027A9861C4D4FB1D4808511A23148516946802B0364D428638E60087AD6EA7E2F016B2F65CD216DE288 7221893A49C924974F7D28C094C6CB27FC8ACA6E07FECD7B8DE4D55D283C9D6A5FF63409F55EEC110BF6612E8578BD1373E39B83A7986A6369ACF32A6A92F538
; 160342 15:44.11 2021-11-04 INSTALL.sh
INSTALL.sh E10075FB44DD06A1C4248264085BDC8217B900CC 30E5EDCE721AF81B18744CA7B2062147BCF873FB5FE71798B8543EBA52F4FB4C 1E68603F4304D5B4EAA456A6B8A9A79C2CE86C48D595C9DCCD341A0D8959C52A7A9EEF0B3ABDB1C3534023350BC18B64 FAFAE6A7E6BD81C87AA1C90CD52721BF314BAD6BB41B33CF3E1E8070E5DDCA786761A6205AD104BF565DE68E4FF100EC7D55837D4F9CAD60A72825BCFFBE5D65

View File

@ -1 +1 @@
8f59974f7ae69dfbf7b1c492e35f0b421aac10c1 INSTALL.sh
e10075fb44dd06a1c4248264085bdc8217b900cc INSTALL.sh

View File

@ -1 +1 @@
6f9e9c2c24880d2e69e04ab6ae490f72d8b5cbe5bb98596f4fa50c1cfeaa632f INSTALL.sh
30e5edce721af81b18744ca7b2062147bcf873fb5fe71798b8543eba52f4fb4c INSTALL.sh

View File

@ -1 +1 @@
cbcfba692b57e027a9861c4d4fb1d4808511a23148516946802b0364d428638e60087ad6ea7e2f016b2f65cd216de288 INSTALL.sh
1e68603f4304d5b4eaa456a6b8a9a79c2ce86c48d595c9dccd341a0d8959c52a7a9eef0b3abdb1c3534023350bc18b64 INSTALL.sh

View File

@ -1 +1 @@
7221893a49c924974f7d28c094c6cb27fc8aca6e07fecd7b8de4d55d283c9d6a5ff63409f55eec110bf6612e8578bd1373e39b83a7986a6369acf32a6a92f538 INSTALL.sh
fafae6a7e6bd81c87aa1c90cd52721bf314bad6bb41b33cf3e1e8070e5ddca786761a6205ad104bf565de68e4ff100ec7d55837d4f9cad60a72825bcffbe5d65 INSTALL.sh

View File

@ -260,10 +260,6 @@ installSupported () {
echo "Proceeding with the installation of MISP core"
space
# Set Base URL - functionLocation('generic/supportFunctions.md')
[[ -n $CORE ]] || [[ -n $ALL ]] && setBaseURL
progress 4
# Check if sudo is installed and etckeeper - functionLocation('generic/sudo_etckeeper.md')
[[ -n $CORE ]] || [[ -n $ALL ]] && checkSudoKeeper
[[ ! -z ${MISP_USER} ]] && [[ ! -f /etc/sudoers.d/misp ]] && echo "%${MISP_USER} ALL=(ALL:ALL) NOPASSWD:ALL" |sudo tee /etc/sudoers.d/misp

View File

@ -51,7 +51,6 @@ $config = array(
'unpublishedprivate' => false,
'disable_emailing' => false,
'manage_workers' => true,
'Attributes_Values_Filter_In_Event' => 'id, uuid, value, comment, type, category, Tag.name',
'python_bin' => null,
'external_baseurl' => '',
'forceHTTPSforPreLoginRequestedURL' => false,

View File

@ -1,34 +0,0 @@
<?php
/**
* AppShell file
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::uses('Shell', 'Console', 'AppModel', 'Model');
/**
* Application Shell
*
* Add your application-wide methods in the class below, your shells
* will inherit them.
*
* @package app.Console.Command
*/
class AppShell extends Shell {
public function perform() {
$this->initialize();
$this->{array_shift($this->args)}();
}
}

View File

@ -22,12 +22,18 @@ class AdminShell extends AppShell
'value' => ['help' => __('Setting value'), 'required' => true],
],
'options' => [
'force' => array(
'force' => [
'short' => 'f',
'help' => 'Force the command.',
'default' => false,
'boolean' => true
)
],
'null' => [
'short' => 'n',
'help' => 'Set the value to null.',
'default' => false,
'boolean' => true
],
]
],
]);
@ -375,14 +381,13 @@ class AdminShell extends AppShell
public function getSetting()
{
$this->ConfigLoad->execute();
$param = empty($this->args[0]) ? 'all' : $this->args[0];
$settings = $this->Server->serverSettingsRead();
$result = $settings;
if ($param != 'all') {
if ($param !== 'all') {
$result = 'No valid setting found for ' . $param;
foreach ($settings as $setting) {
if ($setting['setting'] == $param) {
if ($setting['setting'] === $param) {
$result = $setting;
break;
}
@ -393,15 +398,17 @@ class AdminShell extends AppShell
public function setSetting()
{
$setting_name = !isset($this->args[0]) ? null : $this->args[0];
$value = !isset($this->args[1]) ? null : $this->args[1];
list($setting_name, $value) = $this->args;
if ($value === 'false') {
$value = 0;
} elseif ($value === 'true') {
$value = 1;
}
if ($this->params['null']) {
$value = null;
}
$cli_user = array('id' => 0, 'email' => 'SYSTEM', 'Organisation' => array('name' => 'SYSTEM'));
if (empty($setting_name) || $value === null) {
if (empty($setting_name) || ($value === null && !$this->params['null'])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Set setting'] . PHP_EOL);
}
$setting = $this->Server->getSettingData($setting_name);
@ -411,7 +418,7 @@ class AdminShell extends AppShell
}
$result = $this->Server->serverSettingsEditValue($cli_user, $setting, $value, $this->params['force']);
if ($result === true) {
echo 'Setting "' . $setting_name . '" changed to ' . $value . PHP_EOL;
$this->out(__('Setting "%s" changed to %s', $setting_name, is_string($value) ? '"' . $value . '"' : (string)$value));
} else {
$message = __("The setting change was rejected. MISP considers the requested setting value as invalid and would lead to the following error:\n\n\"%s\"\n\nIf you still want to force this change, please supply the --force argument.\n", $result);
$this->error(__('Setting change rejected.'), $message);

View File

@ -30,10 +30,10 @@ class AppShell extends Shell
{
public $tasks = array('ConfigLoad');
public function perform()
public function initialize()
{
$this->initialize();
$this->{array_shift($this->args)}();
parent::initialize();
$this->ConfigLoad->execute();
}
protected function _welcome()

View File

@ -1,7 +1,13 @@
<?php
class ConfigLoadTask extends Shell {
public function execute() {
Configure::load('config');
class ConfigLoadTask extends Shell
{
public function execute()
{
Configure::load('config');
if (Configure::read('MISP.system_setting_db')) {
App::uses('SystemSetting', 'Model');
SystemSetting::setGlobalSetting();
}
}
?>
}

View File

@ -31,7 +31,7 @@ class AppController extends Controller
*/
public $defaultModel = '';
public $helpers = array('OrgImg', 'FontAwesome', 'UserName', 'DataPathCollector');
public $helpers = array('OrgImg', 'FontAwesome', 'UserName');
private $__queryVersion = '131';
public $pyMispVersion = '2.4.148';
@ -69,34 +69,39 @@ class AppController extends Controller
}
public $components = array(
'Session',
'Auth' => array(
'authError' => 'Unauthorised access.',
'authenticate' => array(
'Form' => array(
'passwordHasher' => 'BlowfishConstant',
'fields' => array(
'username' => 'email'
)
'Session',
'Auth' => array(
'authError' => 'Unauthorised access.',
'authenticate' => array(
'Form' => array(
'passwordHasher' => 'BlowfishConstant',
'fields' => array(
'username' => 'email'
)
)
),
'Security',
'ACL',
'CompressedRequestHandler',
'RestResponse',
'Flash',
'Toolbox',
'RateLimit',
'IndexFilter',
'Deprecation',
'RestSearch',
'CRUD'
//,'DebugKit.Toolbar'
)
),
'Security',
'ACL',
'CompressedRequestHandler',
'RestResponse',
'Flash',
'Toolbox',
'RateLimit',
'IndexFilter',
'Deprecation',
'RestSearch',
'CRUD'
//,'DebugKit.Toolbar'
);
public function beforeFilter()
{
if (Configure::read('MISP.system_setting_db')) {
App::uses('SystemSetting', 'Model');
SystemSetting::setGlobalSetting();
}
$this->_setupBaseurl();
$this->Auth->loginRedirect = $this->baseurl . '/users/routeafterlogin';
@ -342,7 +347,7 @@ class AppController extends Controller
$this->User->Server->updateDatabase('cleanSessionTable');
}
}
if (Configure::read('site_admin_debug') && (Configure::read('debug') < 2)) {
if (Configure::read('site_admin_debug') && Configure::read('debug') < 2) {
Configure::write('debug', 1);
}
}
@ -373,7 +378,7 @@ class AppController extends Controller
if (!empty($homepage)) {
$this->set('homepage', $homepage);
}
if (version_compare(phpversion(), '8.0') >= 0) {
if (PHP_MAJOR_VERSION >= 8) {
$this->Flash->error(__('WARNING: MISP is currently running under PHP 8.0, which is unsupported. Background jobs will fail, so please contact your administrator to run a supported PHP version (such as 7.4)'));
}
}

View File

@ -11,12 +11,23 @@ class AttributesController extends AppController
{
public $components = array('Security', 'RequestHandler');
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999,
'conditions' => array('AND' => array('Attribute.deleted' => 0)),
'order' => 'Attribute.event_id DESC'
);
public $paginate = [
'limit' => 60,
'maxLimit' => 9999,
'conditions' => array('AND' => array('Attribute.deleted' => 0)),
'order' => 'Attribute.event_id DESC',
'recursive' => -1,
'contain' => array(
'Event' => array(
'fields' => array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.info', 'Event.user_id', 'Event.date'),
),
'AttributeTag',
'Object' => array(
'fields' => array('Object.id', 'Object.distribution', 'Object.sharing_group_id')
),
'SharingGroup' => ['fields' => ['SharingGroup.name']],
),
];
public function beforeFilter()
{
@ -53,36 +64,19 @@ class AttributesController extends AppController
$this->params->addParams(array('pass' => array($id))); // FIXME find better way to change id variable if uuid is found. params->url and params->here is not modified accordingly now
}
}
// do not show private to other orgs
if (!$this->_isSiteAdmin()) {
$this->paginate = Set::merge($this->paginate, array('conditions' => $this->Attribute->buildConditions($this->Auth->user())));
}
}
public function index()
{
$this->Attribute->recursive = -1;
$this->paginate['recursive'] = -1;
$this->paginate['contain'] = array(
'Event' => array(
'fields' => array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.info', 'Event.user_id', 'Event.date'),
),
'AttributeTag' => array('Tag'),
'Object' => array(
'fields' => array('Object.id', 'Object.distribution', 'Object.sharing_group_id')
),
'SharingGroup' => ['fields' => ['SharingGroup.name']],
);
$this->Attribute->contain(array('AttributeTag' => array('Tag')));
$this->set('isSearch', 0);
$this->paginate['conditions']['AND'][] = $this->Attribute->buildConditions($this->Auth->user());
$attributes = $this->paginate();
if ($this->_isRest()) {
foreach ($attributes as $k => $attribute) {
$attributes[$k] = $attribute['Attribute'];
}
$attributes = array_column($attributes, 'Attribute');
return $this->RestResponse->viewData($attributes, $this->response->type());
}
$this->Attribute->attachTagsToAttributes($attributes, ['includeAllTags' => true]);
$orgTable = $this->Attribute->Event->Orgc->find('all', [
'fields' => ['Orgc.id', 'Orgc.name', 'Orgc.uuid'],
]);
@ -94,6 +88,7 @@ class AttributesController extends AppController
}
list($attributes, $sightingsData) = $this->__searchUI($attributes);
$this->set('isSearch', 0);
$this->set('sightingsData', $sightingsData);
$this->set('orgTable', array_column($orgTable, 'name', 'id'));
$this->set('shortDist', $this->Attribute->shortDist);
@ -920,28 +915,25 @@ class AttributesController extends AppController
if (empty($attribute)) {
return new CakeResponse(array('body'=> json_encode(array('fail' => false, 'errors' => 'Invalid attribute')), 'status' => 200, 'type' => 'json'));
}
$this->Attribute->data = $attribute;
$this->Attribute->id = $attribute['Attribute']['id'];
if (!$this->__canModifyEvent($attribute)) {
return new CakeResponse(array('body' => json_encode(array('fail' => false, 'errors' => 'You do not have permission to do that')), 'status' => 200, 'type' => 'json'));
}
if (!$this->_isRest()) {
$this->Attribute->Event->insertLock($this->Auth->user(), $attribute['Attribute']['event_id']);
}
$validFields = array('value', 'category', 'type', 'comment', 'to_ids', 'distribution', 'first_seen', 'last_seen');
$changed = false;
if (empty($this->request->data['Attribute'])) {
$this->request->data = array('Attribute' => $this->request->data);
if (empty($this->request->data['Attribute'])) {
throw new MethodNotAllowedException(__('Invalid input.'));
}
}
$validFields = array('value', 'category', 'type', 'comment', 'to_ids', 'distribution', 'first_seen', 'last_seen');
$changed = false;
foreach ($this->request->data['Attribute'] as $changedKey => $changedField) {
if (!in_array($changedKey, $validFields)) {
if (!in_array($changedKey, $validFields, true)) {
throw new MethodNotAllowedException(__('Invalid field.'));
}
if ($attribute['Attribute'][$changedKey] == $changedField) {
$this->autoRender = false;
return new CakeResponse(array('body'=> json_encode(array('errors'=> array('value' => 'nochange'))), 'status'=>200, 'type' => 'json'));
}
$attribute['Attribute'][$changedKey] = $changedField;
@ -952,16 +944,23 @@ class AttributesController extends AppController
}
$date = new DateTime();
$attribute['Attribute']['timestamp'] = $date->getTimestamp();
if ($this->Attribute->save($attribute)) {
$this->Attribute->Event->unpublishEvent($attribute['Attribute']['event_id']);
$fieldsToSave = ['timestamp'];
if ($changedKey === 'value') {
$fieldsToSave[] = 'value1';
$fieldsToSave[] = 'value2';
} else {
$fieldsToSave[] = $changedKey;
}
if ($this->Attribute->save($attribute, true, $fieldsToSave)) {
$this->Attribute->Event->unpublishEvent($attribute['Attribute']['event_id'], false, $date->getTimestamp());
if ($attribute['Attribute']['object_id'] != 0) {
$this->Attribute->Object->updateTimestamp($attribute['Attribute']['object_id'], $date->getTimestamp());
}
$this->autoRender = false;
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Field updated.', 'check_publish' => true)), 'status'=>200, 'type' => 'json'));
} else {
$this->autoRender = false;
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $this->Attribute->validationErrors)), 'status'=>200, 'type' => 'json'));
}
}
@ -1577,25 +1576,9 @@ class AttributesController extends AppController
if (!isset($params['conditions']['Attribute.deleted'])) {
$params['conditions']['Attribute.deleted'] = 0;
}
$this->paginate = $params;
if (empty($this->paginate['limit'])) {
$this->paginate['limit'] = 60;
}
if (empty($this->paginate['page'])) {
$this->paginate['page'] = 1;
}
$this->paginate['recursive'] = -1;
$this->paginate['contain'] = array(
'Event' => array(
'fields' => array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.info', 'Event.user_id', 'Event.date'),
),
'AttributeTag' => array('Tag'),
'Object' => array(
'fields' => array('Object.id', 'Object.distribution', 'Object.sharing_group_id')
),
'SharingGroup' => ['fields' => ['SharingGroup.name']],
);
$this->paginate['conditions'] = $params['conditions'];
$attributes = $this->paginate();
$this->Attribute->attachTagsToAttributes($attributes, ['includeAllTags' => true]);
$orgTable = $this->Attribute->Event->Orgc->find('all', [
'fields' => ['Orgc.id', 'Orgc.name', 'Orgc.uuid'],
@ -1649,7 +1632,7 @@ class AttributesController extends AppController
}
}
private function __searchUI($attributes)
private function __searchUI(array $attributes)
{
if (empty($attributes)) {
return [[], []];
@ -1661,9 +1644,9 @@ class AttributesController extends AppController
$this->loadModel('AttachmentScan');
$user = $this->Auth->user();
$attributeIds = [];
foreach ($attributes as $k => $attribute) {
$attributeId = $attribute['Attribute']['id'];
$attributeIds[] = $attributeId;
$galaxyTags = [];
foreach ($attributes as &$attribute) {
$attributeIds[] = $attribute['Attribute']['id'];
if ($this->Attribute->isImage($attribute['Attribute'])) {
if (extension_loaded('gd')) {
// if extension is loaded, the data is not passed to the view because it is asynchronously fetched
@ -1671,20 +1654,33 @@ class AttributesController extends AppController
} else {
$attribute['Attribute']['image'] = $this->Attribute->base64EncodeAttachment($attribute['Attribute']);
}
$attributes[$k] = $attribute;
}
if ($attribute['Attribute']['type'] === 'attachment' && $this->AttachmentScan->isEnabled()) {
$infected = $this->AttachmentScan->isInfected(AttachmentScan::TYPE_ATTRIBUTE, $attribute['Attribute']['id']);
$attributes[$k]['Attribute']['infected'] = $infected;
$attribute['Attribute']['infected'] = $infected;
}
if ($attribute['Attribute']['distribution'] == 4) {
$attributes[$k]['Attribute']['SharingGroup'] = $attribute['SharingGroup'];
$attribute['Attribute']['SharingGroup'] = $attribute['SharingGroup'];
}
$attributes[$k]['Attribute']['AttributeTag'] = $attributes[$k]['AttributeTag'];
$attributes[$k]['Attribute'] = $this->Attribute->Event->massageTags($this->Auth->user(), $attributes[$k]['Attribute'], 'Attribute', $excludeGalaxy = false, $cullGalaxyTags = true);
unset($attributes[$k]['AttributeTag']);
$attribute['Attribute']['AttributeTag'] = $attribute['AttributeTag'];
foreach ($attribute['Attribute']['AttributeTag'] as $at) {
if (substr($at['Tag']['name'], 0, 12) === 'misp-galaxy:') {
$galaxyTags[] = $at['Tag']['name'];
}
}
unset($attribute['AttributeTag']);
}
unset($attribute);
// Fetch galaxy clusters in one query
if (!empty($galaxyTags)) {
$this->loadModel('GalaxyCluster');
$clusters = $this->GalaxyCluster->getClusters($galaxyTags, $user, true, false);
$clusters = array_column(array_column($clusters, 'GalaxyCluster'), null, 'tag_id');
} else {
$clusters = [];
}
// Fetch correlations in one query
@ -1696,6 +1692,27 @@ class AttributesController extends AppController
$attributesWithFeedCorrelations = $this->Feed->attachFeedCorrelations(array_column($attributes, 'Attribute'), $user, $fakeEventArray);
foreach ($attributes as $k => $attribute) {
// Assign galaxies
$galaxies = [];
foreach ($attribute['Attribute']['AttributeTag'] as $k2 => $attributeTag) {
if (!isset($clusters[$attributeTag['Tag']['id']])) {
continue;
}
$cluster = $clusters[$attributeTag['Tag']['id']];
$galaxyId = $cluster['Galaxy']['id'];
$cluster['local'] = isset($attributeTag['local']) ? $attributeTag['local'] : false;
if (isset($attribute['Attribute']['Galaxy'][$galaxyId])) {
unset($cluster['Galaxy']);
$galaxies[$galaxyId]['GalaxyCluster'][] = $cluster;
} else {
$galaxies[$galaxyId] = $cluster['Galaxy'];
unset($cluster['Galaxy']);
$galaxies[$galaxyId]['GalaxyCluster'] = [$cluster];
}
unset($attributes[$k]['Attribute']['AttributeTag'][$k2]); // remove galaxy tag
}
$attributes[$k]['Attribute']['Galaxy'] = array_values($galaxies);
if (isset($attributesWithFeedCorrelations[$k]['Feed'])) {
$attributes[$k]['Attribute']['Feed'] = $attributesWithFeedCorrelations[$k]['Feed'];
}
@ -1707,65 +1724,6 @@ class AttributesController extends AppController
return array($attributes, $sightingsData);
}
// If the checkbox for the alternate search is ticked, then this method is called to return the data to be represented
// This alternate view will show a list of events with matching search results and the percentage of those matched attributes being marked as to_ids
// events are sorted based on relevance (as in the percentage of matches being flagged as indicators for IDS)
public function searchAlternate($data)
{
$attributes = $this->Attribute->fetchAttributes(
$this->Auth->user(),
array(
'conditions' => array(
'AND' => $data
),
'contain' => array('Event' => array('Orgc' => array('fields' => array('Orgc.name')))),
'fields' => array(
'Attribute.id', 'Attribute.event_id', 'Attribute.type', 'Attribute.category', 'Attribute.to_ids', 'Attribute.value', 'Attribute.distribution',
'Event.id', 'Event.org_id', 'Event.orgc_id', 'Event.info', 'Event.distribution', 'Event.attribute_count', 'Event.date',
)
)
);
$events = array();
foreach ($attributes as $attribute) {
if (isset($events[$attribute['Event']['id']])) {
if ($attribute['Attribute']['to_ids']) {
$events[$attribute['Event']['id']]['to_ids']++;
} else {
$events[$attribute['Event']['id']]['no_ids']++;
}
} else {
$events[$attribute['Event']['id']]['Event'] = $attribute['Event'];
$events[$attribute['Event']['id']]['to_ids'] = 0;
$events[$attribute['Event']['id']]['no_ids'] = 0;
if ($attribute['Attribute']['to_ids']) {
$events[$attribute['Event']['id']]['to_ids']++;
} else {
$events[$attribute['Event']['id']]['no_ids']++;
}
}
}
foreach ($events as $key => $event) {
$events[$key]['relevance'] = 100 * $event['to_ids'] / ($event['no_ids'] + $event['to_ids']);
}
if (!empty($events)) {
$events = $this->__subval_sort($events, 'relevance');
}
return $events;
}
// Sort the array of arrays based on a value of a sub-array
private function __subval_sort($a, $subkey)
{
foreach ($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
arsort($b);
foreach ($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
public function checkComposites()
{
if (!self::_isAdmin()) {

View File

@ -37,6 +37,7 @@ class AuditLogsController extends AppController
'Server',
'ShadowAttribute',
'SharingGroup',
'SystemSetting',
'Tag',
'TagCollection',
'TagCollectionTag',
@ -484,10 +485,12 @@ class AuditLogsController extends AppController
if (!empty($eventIds)) {
$this->loadModel('Event');
$events = $this->Event->fetchSimpleEvents($this->Auth->user(), [
'conditions' => ['Event.id' => array_unique($eventIds)],
$conditions = $this->Event->createEventConditions($this->Auth->user());
$conditions['Event.id'] = array_unique($eventIds);
$events = $this->Event->find('list', [
'conditions' => $conditions,
'fields' => ['Event.id', 'Event.info'],
]);
$events = array_column(array_column($events, 'Event'), null, 'id');
}
$links = [
@ -525,7 +528,7 @@ class AuditLogsController extends AppController
case 'Event':
if (isset($events[$modelId])) {
$url = '/events/view/' . $modelId;
$eventInfo = $events[$modelId]['info'];
$eventInfo = $events[$modelId];
}
break;
case 'ObjectReference':
@ -535,7 +538,7 @@ class AuditLogsController extends AppController
$url .= '/deleted:2';
}
if (isset($events[$objects[$objectReferences[$modelId]]['event_id']])) {
$eventInfo = $events[$objects[$objectReferences[$modelId]]['event_id']]['info'];
$eventInfo = $events[$objects[$objectReferences[$modelId]]['event_id']];
}
}
break;
@ -546,7 +549,7 @@ class AuditLogsController extends AppController
$url .= '/deleted:2';
}
if (isset($events[$objects[$modelId]['event_id']])) {
$eventInfo = $events[$objects[$modelId]['event_id']]['info'];
$eventInfo = $events[$objects[$modelId]['event_id']];
}
}
break;
@ -557,7 +560,7 @@ class AuditLogsController extends AppController
$url .= '/deleted:2';
}
if (isset($events[$attributes[$modelId]['event_id']])) {
$eventInfo = $events[$attributes[$modelId]['event_id']]['info'];
$eventInfo = $events[$attributes[$modelId]['event_id']];
}
}
break;
@ -565,7 +568,7 @@ class AuditLogsController extends AppController
if (isset($shadowAttributes[$modelId])) {
$url = '/events/view/' . $shadowAttributes[$modelId]['event_id'] . '/focus:' . $shadowAttributes[$modelId]['uuid'];
if (isset($events[$shadowAttributes[$modelId]['event_id']])) {
$eventInfo = $events[$shadowAttributes[$modelId]['event_id']]['info'];
$eventInfo = $events[$shadowAttributes[$modelId]['event_id']];
}
}
break;

View File

@ -54,7 +54,6 @@ class ACLComponent extends Component
'returnAttributes' => array('*'),
'rpz' => array('*'),
'search' => array('*'),
'searchAlternate' => array('*'),
'toggleCorrelation' => array('perm_add'),
'text' => array('*'),
'toggleToIDS' => array('perm_add'),

View File

@ -372,19 +372,15 @@ class OrganisationsController extends AppController
}
$idList = json_decode($idList, true);
$id_exclusion_list = array_merge($idList, array($this->Auth->user('Organisation')['id']));
$temp = $this->Organisation->find('all', array(
'conditions' => array(
'local' => $local,
'id !=' => $id_exclusion_list,
),
'recursive' => -1,
'fields' => array('id', 'name'),
'order' => array('lower(name) ASC')
$orgs = $this->Organisation->find('list', array(
'conditions' => array(
'local' => $local,
'id !=' => $id_exclusion_list,
),
'recursive' => -1,
'fields' => array('id', 'name'),
'order' => array('lower(name) ASC')
));
$orgs = array();
foreach ($temp as $org) {
$orgs[] = array('id' => $org['Organisation']['id'], 'name' => $org['Organisation']['name']);
}
$this->set('local', $local);
$this->layout = false;
$this->autoRender = false;
@ -402,10 +398,13 @@ class OrganisationsController extends AppController
$this->render('ajax/sg_org_row_empty');
}
/**
* @deprecated Probably not used anywhere.
*/
public function getUUIDs()
{
if (!$this->Auth->user('Role')['perm_sync']) {
throw new MethodNotAllowedException(__('This action is restricted to sync users'));
if (Configure::read('Security.hide_organisation_index_from_users')) {
throw new MethodNotAllowedException(__('This action is not enabled on this instance.'));
}
$temp = $this->Organisation->find('all', array(
'recursive' => -1,

View File

@ -1485,18 +1485,18 @@ class ServersController extends AppController
}
}
$this->autoRender = false;
$this->loadModel('Log');
if (!is_writeable(APP . 'Config/config.php')) {
if (!Configure::read('MISP.system_setting_db') && !is_writeable(APP . 'Config/config.php')) {
$this->loadModel('Log');
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Server',
'model_id' => 0,
'email' => $this->Auth->user('email'),
'action' => 'serverSettingsEdit',
'user_id' => $this->Auth->user('id'),
'title' => 'Server setting issue',
'change' => 'There was an issue witch changing ' . $setting['name'] . ' to ' . $this->request->data['Server']['value'] . '. The error message returned is: app/Config.config.php is not writeable to the apache user. No changes were made.',
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Server',
'model_id' => 0,
'email' => $this->Auth->user('email'),
'action' => 'serverSettingsEdit',
'user_id' => $this->Auth->user('id'),
'title' => 'Server setting issue',
'change' => 'There was an issue witch changing ' . $setting['name'] . ' to ' . $this->request->data['Server']['value'] . '. The error message returned is: app/Config.config.php is not writeable to the apache user. No changes were made.',
));
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, 'app/Config.config.php is not writeable to the apache user.', $this->response->type());

View File

@ -1210,8 +1210,7 @@ class UsersController extends AppController
}
}
// populate the DB with the first role (site admin) if it's empty
$this->loadModel('Role');
if ($this->Role->find('count') == 0) {
if (!$this->User->Role->hasAny()) {
$siteAdmin = array('Role' => array(
'id' => 1,
'name' => 'Site Admin',
@ -1230,14 +1229,14 @@ class UsersController extends AppController
'perm_template' => 1,
'perm_tagger' => 1,
));
$this->Role->save($siteAdmin);
$this->User->Role->save($siteAdmin);
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
if ($dataSource == 'Database/Postgres') {
if ($dataSource === 'Database/Postgres') {
$sql = "SELECT setval('roles_id_seq', (SELECT MAX(id) FROM roles));";
$this->Role->query($sql);
$this->User->Role->query($sql);
}
}
if ($this->User->Organisation->find('count', array('conditions' => array('Organisation.local' => true))) == 0) {
if (!$this->User->Organisation->hasAny(array('Organisation.local' => true))) {
$this->User->runUpdates();
$date = date('Y-m-d H:i:s');
$org = array('Organisation' => array(
@ -1253,23 +1252,25 @@ class UsersController extends AppController
));
$this->User->Organisation->save($org);
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
if ($dataSource == 'Database/Postgres') {
if ($dataSource === 'Database/Postgres') {
$sql = "SELECT setval('organisations_id_seq', (SELECT MAX(id) FROM organisations));";
$this->User->Organisation->query($sql);
}
$org_id = $this->User->Organisation->id;
} else {
$hostOrg = $this->User->Organisation->find('first', array('conditions' => array('Organisation.name' => Configure::read('MISP.org'), 'Organisation.local' => true), 'recursive' => -1));
if (!empty($hostOrg)) {
$org_id = $hostOrg['Organisation']['id'];
} else {
$firstOrg = $this->User->Organisation->find('first', array('conditions' => array('Organisation.local' => true), 'order' => 'Organisation.id ASC'));
$org_id = $firstOrg['Organisation']['id'];
}
}
// populate the DB with the first user if it's empty
if ($this->User->find('count') == 0) {
if (!$this->User->hasAny()) {
if (!isset($org_id)) {
$hostOrg = $this->User->Organisation->find('first', array('conditions' => array('Organisation.name' => Configure::read('MISP.org'), 'Organisation.local' => true), 'recursive' => -1));
if (!empty($hostOrg)) {
$org_id = $hostOrg['Organisation']['id'];
} else {
$firstOrg = $this->User->Organisation->find('first', array('conditions' => array('Organisation.local' => true), 'order' => 'Organisation.id ASC'));
$org_id = $firstOrg['Organisation']['id'];
}
}
$this->User->runUpdates();
$this->User->createInitialUser($org_id);
}

View File

@ -0,0 +1,66 @@
<?php
class BetterSecurity
{
const METHOD = 'AES-256-GCM';
const TAG_SIZE = 16;
/**
* @param string $plain
* @param string $key
* @return string
* @throws Exception
*/
public static function encrypt($plain, $key)
{
if (strlen($key) < 32) {
throw new Exception('Invalid key, key must be at least 256 bits (32 bytes) long.');
}
// Generate the encryption key.
$key = hash('sha256', $key, true);
$ivlen = openssl_cipher_iv_length(self::METHOD);
$iv = openssl_random_pseudo_bytes($ivlen);
if ($iv === false) {
throw new Exception('Could not generate random bytes.');
}
$ciphertext = openssl_encrypt($plain, self::METHOD, $key, OPENSSL_RAW_DATA, $iv, $tag);
if ($ciphertext === false) {
throw new Exception('Could not encrypt.');
}
return $iv . $tag . $ciphertext;
}
/**
* @param string $cipher
* @param string $key
* @return string
* @throws Exception
*/
public static function decrypt($cipher, $key)
{
if (strlen($key) < 32) {
throw new Exception('Invalid key, key must be at least 256 bits (32 bytes) long.');
}
if (empty($cipher)) {
throw new Exception('The data to decrypt cannot be empty.');
}
// Generate the encryption key.
$key = hash('sha256', $key, true);
$ivSize = openssl_cipher_iv_length(self::METHOD);
// Split out hmac for comparison
$iv = substr($cipher, 0, $ivSize);
$tag = substr($cipher, $ivSize, self::TAG_SIZE);
$cipher = substr($cipher, $ivSize + self::TAG_SIZE);
$decrypted = openssl_decrypt($cipher, self::METHOD, $key, true, $iv, $tag);
if ($decrypted === false) {
throw new Exception('Could not decrypt. Maybe invalid encryption key?');
}
return $decrypted;
}
}

View File

@ -23,11 +23,11 @@
App::uses('Model', 'Model');
App::uses('LogableBehavior', 'Assets.models/behaviors');
App::uses('RandomTool', 'Tools');
App::uses('FileAccessTool', 'Tools');
App::uses('JsonTool', 'Tools');
class AppModel extends Model
{
public $name;
/** @var PubSubTool */
private static $loadedPubSubTool;
@ -42,7 +42,7 @@ class AppModel extends Model
private $__profiler = array();
public $elasticSearchClient = false;
public $elasticSearchClient;
/** @var AttachmentTool|null */
private $attachmentTool;
@ -50,8 +50,6 @@ class AppModel extends Model
public function __construct($id = false, $table = null, $ds = null)
{
parent::__construct($id, $table, $ds);
$this->name = get_class($this);
$this->findMethods['column'] = true;
}
@ -87,7 +85,7 @@ class AppModel extends Model
57 => false, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
63 => true, 64 => false, 65 => false, 66 => false, 67 => false, 68 => false,
69 => false, 70 => false, 71 => true, 72 => true, 73 => false, 74 => false,
75 => false, 76 => false
75 => false, 76 => true, 77 => true
);
public $advanced_updates_description = array(
@ -250,13 +248,13 @@ class AppModel extends Model
$result = $this->Feed->addDefaultFeeds($feeds);
$this->Log->create();
$entry = array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => 'Added new default feeds.'
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => 'Added new default feeds.'
);
if ($result) {
$entry['change'] = 'Feeds added: ' . $feedNames;
@ -1537,7 +1535,7 @@ class AppModel extends Model
`value` text NOT NULL,
`from_json` tinyint(1) default 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `value` (`value`(255))
UNIQUE INDEX `value` (`value`(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
break;
case 66:
@ -1597,6 +1595,13 @@ class AppModel extends Model
$this->__dropIndex('object_references', 'referenced_uuid');
break;
case 76:
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `system_settings` (
`setting` varchar(255) NOT NULL,
`value` blob NOT NULL,
PRIMARY KEY (`setting`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
break;
case 77:
$sqlArray[] = "ALTER TABLE `jobs` MODIFY COLUMN `process_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL;";
case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
@ -1787,14 +1792,14 @@ class AppModel extends Model
if ($flagStop && $errorCount > 0) {
$this->Log->create();
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => __('Issues executing the SQL query for %s', $command),
'change' => __('Database updates stopped as some errors occurred and the stop flag is enabled.')
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => __('Issues executing the SQL query for %s', $command),
'change' => __('Database updates stopped as some errors occurred and the stop flag is enabled.')
));
return false;
}
@ -1860,14 +1865,14 @@ class AppModel extends Model
}
$this->Log->create();
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => ($result ? 'Removed index ' : 'Failed to remove index ') . $icr['STATISTICS']['INDEX_NAME'] . ' from ' . $table,
'change' => ($result ? 'Removed index ' : 'Failed to remove index ') . $icr['STATISTICS']['INDEX_NAME'] . ' from ' . $table,
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => ($result ? 'Removed index ' : 'Failed to remove index ') . $icr['STATISTICS']['INDEX_NAME'] . ' from ' . $table,
'change' => ($result ? 'Removed index ' : 'Failed to remove index ') . $icr['STATISTICS']['INDEX_NAME'] . ' from ' . $table,
));
}
}
@ -1899,14 +1904,14 @@ class AppModel extends Model
}
$this->Log->create();
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => ($result ? 'Added index ' : 'Failed to add index ') . $field . ' to ' . $table . ($duplicate ? ' (index already set)' : $errorMessage),
'change' => ($result ? 'Added index ' : 'Failed to add index ') . $field . ' to ' . $table . ($duplicate ? ' (index already set)' : $errorMessage),
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => ($result ? 'Added index ' : 'Failed to add index ') . $field . ' to ' . $table . ($duplicate ? ' (index already set)' : $errorMessage),
'change' => ($result ? 'Added index ' : 'Failed to add index ') . $field . ' to ' . $table . ($duplicate ? ' (index already set)' : $errorMessage),
));
$additionResult = array('success' => $result || $duplicate);
if (!$result) {
@ -1989,7 +1994,8 @@ class AppModel extends Model
}
// Try to create a table with a BIGINT(20)
public function seenOnAttributeAndObjectPreUpdate() {
public function seenOnAttributeAndObjectPreUpdate()
{
$sqlArray[] = "CREATE TABLE IF NOT EXISTS testtable (
`testfield` BIGINT(6) NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
@ -2007,10 +2013,6 @@ class AppModel extends Model
}
}
public function failingPreUpdate() {
throw new Exception('Yolo fail');
}
public function runUpdates($verbose = false, $useWorker = true, $processId = false)
{
$this->AdminSetting = ClassRegistry::init('AdminSetting');
@ -2057,14 +2059,14 @@ class AppModel extends Model
if ($this->isUpdateLocked()) { // prevent creation of useless workers
$this->Log->create();
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_db_worker',
'user_id' => 0,
'title' => __('Issues executing run_updates'),
'change' => __('Database updates are locked. Worker not spawned')
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_db_worker',
'user_id' => 0,
'title' => __('Issues executing run_updates'),
'change' => __('Database updates are locked. Worker not spawned')
));
if (!empty($job)) { // if multiple prio worker is enabled, want to mark them as done
$job['Job']['progress'] = 100;
@ -2114,14 +2116,14 @@ class AppModel extends Model
if ($this->isUpdateLocked()) {
$this->Log->create();
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_db_worker',
'user_id' => 0,
'title' => __('Issues executing run_updates'),
'change' => __('Updates are locked. Stopping worker gracefully')
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_db_worker',
'user_id' => 0,
'title' => __('Issues executing run_updates'),
'change' => __('Updates are locked. Stopping worker gracefully')
));
if (!empty($job)) {
$job['Job']['progress'] = 100;
@ -2379,6 +2381,10 @@ class AppModel extends Model
}
}
/**
* @param string $db_version
* @return array
*/
protected function findUpgrades($db_version)
{
$updates = array();
@ -2503,26 +2509,20 @@ class AppModel extends Model
public function getKafkaPubTool()
{
if (!$this->loadedKafkaPubTool) {
$this->loadKafkaPubTool();
App::uses('KafkaPubTool', 'Tools');
$kafkaPubTool = new KafkaPubTool();
$rdkafkaIni = Configure::read('Plugin.Kafka_rdkafka_config');
$kafkaConf = array();
if (!empty($rdkafkaIni)) {
$kafkaConf = parse_ini_file($rdkafkaIni);
}
$brokers = Configure::read('Plugin.Kafka_brokers');
$kafkaPubTool->initTool($brokers, $kafkaConf);
$this->loadedKafkaPubTool = $kafkaPubTool;
}
return $this->loadedKafkaPubTool;
}
public function loadKafkaPubTool()
{
App::uses('KafkaPubTool', 'Tools');
$kafkaPubTool = new KafkaPubTool();
$rdkafkaIni = Configure::read('Plugin.Kafka_rdkafka_config');
$kafkaConf = array();
if (!empty($rdkafkaIni)) {
$kafkaConf = parse_ini_file($rdkafkaIni);
}
$brokers = Configure::read('Plugin.Kafka_brokers');
$kafkaPubTool->initTool($brokers, $kafkaConf);
$this->loadedKafkaPubTool = $kafkaPubTool;
return true;
}
public function publishKafkaNotification($topicName, $data, $action = false)
{
$kafkaTopic = $this->kafkaTopic($topicName);
@ -2671,15 +2671,6 @@ class AppModel extends Model
}
}
public function getRowCount($table = false)
{
if (empty($table)) {
$table = $this->table;
}
$table_data = $this->query("show table status like '" . $table . "'");
return $table_data[0]['TABLES']['Rows'];
}
public function benchmarkCustomAdd($valueToAdd = 0, $name = 'default', $customName = 'custom')
{
if (empty($this->__profiler[$name]['custom'][$customName])) {
@ -2742,9 +2733,8 @@ class AppModel extends Model
{
static $versionArray;
if ($versionArray === null) {
$file = new File(ROOT . DS . 'VERSION.json');
$versionArray = $this->jsonDecode($file->read());
$file->close();
$content = FileAccessTool::readFromFile(ROOT . DS . 'VERSION.json');
$versionArray = JsonTool::decode($content);
}
return $versionArray;
}
@ -2945,14 +2935,14 @@ class AppModel extends Model
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$entry = array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => 'Bumped the timestamps of locked events containing object references.',
'change' => sprintf('Event timestamps updated: %s; Object timestamps updated: %s', count($event_ids), count($object_ids))
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'update_database',
'user_id' => 0,
'title' => 'Bumped the timestamps of locked events containing object references.',
'change' => sprintf('Event timestamps updated: %s; Object timestamps updated: %s', count($event_ids), count($object_ids))
);
$this->Log->save($entry);
}
@ -3110,10 +3100,7 @@ class AppModel extends Model
$message .= "\n";
do {
$message .= sprintf("[%s] %s",
get_class($exception),
$exception->getMessage()
);
$message .= sprintf("[%s] %s", get_class($exception), $exception->getMessage());
$message .= "\nStack Trace:\n" . $exception->getTraceAsString();
$exception = $exception->getPrevious();
} while ($exception !== null);

View File

@ -2031,7 +2031,7 @@ class Attribute extends AppModel
'Event' => array(
'fields' => array('id', 'info', 'org_id', 'orgc_id', 'uuid'),
),
'AttributeTag', // tags are fetched separately, @see Attribute::__attachTagsToAttributes
'AttributeTag', // tags are fetched separately, @see Attribute::attachTagsToAttributes
'Object' => array(
'fields' => array('id', 'distribution', 'sharing_group_id')
)
@ -2232,7 +2232,7 @@ class Attribute extends AppModel
unset($eventsById, $result); // unset result is important, because it is reference
}
$this->__attachTagsToAttributes($results, $options);
$this->attachTagsToAttributes($results, $options);
foreach ($results as $k => $result) {
if (!empty($options['includeSightings'])) {
@ -2361,7 +2361,14 @@ class Attribute extends AppModel
return $eventsById;
}
private function __attachTagsToAttributes(array &$attributes, array $options)
/**
* Options:
* - includeAllTags - if true, include also exportable tags
*
* @param array $attributes
* @param array $options
*/
public function attachTagsToAttributes(array &$attributes, array $options)
{
$tagIdsToFetch = [];
foreach ($attributes as $attribute) {
@ -2382,15 +2389,12 @@ class Attribute extends AppModel
$conditions['Tag.exportable'] = 1;
}
$tagsToModify = $this->AttributeTag->Tag->find('all', [
$tags = $this->AttributeTag->Tag->find('all', [
'conditions' => $conditions,
'fields' => ['id', 'name', 'colour', 'numerical_value'],
'recursive' => -1,
]);
$tags = [];
foreach ($tagsToModify as $tag) {
$tags[$tag['Tag']['id']] = $tag['Tag'];
}
$tags = array_column(array_column($tags, 'Tag'), null, 'id');
foreach ($attributes as $k => $attribute) {
$tagCulled = false;

View File

@ -131,13 +131,17 @@ class AuditLog extends AppModel
if (in_array($auditLog['model'], ['Attribute', 'Object', 'ShadowAttribute'], true)) {
$modelName = $auditLog['model'] === 'ShadowAttribute' ? 'Proposal' : $auditLog['model'];
$title = __('%s from Event #%s', $modelName, $auditLog['event_id']);
} else {
$title = "{$auditLog['model']} #{$auditLog['model_id']}";
}
if (isset($auditLog['model_title']) && $auditLog['model_title']) {
$title .= ": {$auditLog['model_title']}";
if (isset($title)) {
$title .= ": {$auditLog['model_title']}";
return $title;
} else {
return $auditLog['model_title'];
}
}
return $title;
return '';
}
/**

View File

@ -39,6 +39,7 @@ class AuditLogBehavior extends ModelBehavior
'TagCollection' => 'name',
'Taxonomy' => 'namespace',
'Organisation' => 'name',
'SystemSetting' => 'setting',
'AdminSetting' => 'setting',
'UserSetting' => 'setting',
'Galaxy' => 'name',
@ -117,7 +118,7 @@ class AuditLogBehavior extends ModelBehavior
return;
}
$id = $model->id ?: null;
$id = $model->id ?: 0;
$data = $model->data[$model->alias];
if ($created) {
@ -127,7 +128,7 @@ class AuditLogBehavior extends ModelBehavior
if (isset($data['deleted'])) {
if ($data['deleted']) {
$action = AuditLog::ACTION_SOFT_DELETE;
} else if ($this->old[$model->alias]['deleted']) {
} else if (isset($this->old[$model->alias]['deleted']) && $this->old[$model->alias]['deleted']) {
$action = AuditLog::ACTION_UNDELETE;
}
}
@ -177,14 +178,14 @@ class AuditLogBehavior extends ModelBehavior
}
$id = $modelName === 'AttributeTag' ? $data['attribute_id'] : $data['event_id'];
$modelName = $modelName === 'AttributeTag' ? 'Attribute' : 'Event';
}
if ($modelName === 'Event') {
} else if ($modelName === 'Event') {
if (isset($changedFields['published'][1]) && $changedFields['published'][1]) {
$action = AuditLog::ACTION_PUBLISH;
} else if (isset($changedFields['sighting_timestamp'][1]) && $changedFields['sighting_timestamp'][1]) {
$action = AuditLog::ACTION_PUBLISH_SIGHTINGS;
}
} else if ($modelName === 'SystemSetting') {
$id = 0;
}
$this->auditLog()->insert(['AuditLog' => [
@ -253,6 +254,8 @@ class AuditLogBehavior extends ModelBehavior
}
$id = $modelName === 'AttributeTag' ? $model->data[$model->alias]['attribute_id'] : $model->data[$model->alias]['event_id'];
$modelName = $modelName === 'AttributeTag' ? 'Attribute' : 'Event';
} else if ($modelName === 'SystemSetting') {
$id = 0;
}
$this->auditLog()->insert(['AuditLog' => [
@ -350,7 +353,7 @@ class AuditLogBehavior extends ModelBehavior
continue;
}
if ($key === 'password' || $key === 'authkey') {
if ($key === 'password' || $key === 'authkey' || ($key === 'value' && $model->name === 'SystemSetting' && SystemSetting::isSensitive($model->data[$model->alias]['setting']))) {
$value = '*****';
if ($old !== null) {
$old = $value;

View File

@ -1,9 +1,7 @@
<?php
App::uses('AppModel', 'Model');
App::uses('CakeEmail', 'Network/Email');
App::uses('FileAccessTool', 'Tools');
App::uses('AttachmentTool', 'Tools');
App::uses('JsonTool', 'Tools');
App::uses('TmpFileTool', 'Tools');
App::uses('SendEmailTemplate', 'Tools');
@ -2362,10 +2360,7 @@ class Event extends AppModel
return;
}
$clustersByTagNames = [];
foreach ($clusters as $cluster) {
$clustersByTagNames[mb_strtolower($cluster['GalaxyCluster']['tag_name'])] = $cluster['GalaxyCluster'];
}
$clustersByTagIds = array_column(array_column($clusters, 'GalaxyCluster'), null, 'tag_id');
unset($clusters);
if (isset($event['EventTag'])) {
@ -2373,9 +2368,9 @@ class Event extends AppModel
if (!$eventTag['Tag']['is_galaxy']) {
continue;
}
$tagName = mb_strtolower($eventTag['Tag']['name']);
if (isset($clustersByTagNames[$tagName])) {
$cluster = $clustersByTagNames[$tagName];
$tagId = $eventTag['Tag']['id'];
if (isset($clustersByTagIds[$tagId])) {
$cluster = $clustersByTagIds[$tagId];
$galaxyId = $cluster['Galaxy']['id'];
$cluster['local'] = isset($eventTag['local']) ? $eventTag['local'] : false;
if (isset($event['Galaxy'][$galaxyId])) {
@ -2397,9 +2392,9 @@ class Event extends AppModel
if (!$attributeTag['Tag']['is_galaxy']) {
continue;
}
$tagName = mb_strtolower($attributeTag['Tag']['name']);
if (isset($clustersByTagNames[$tagName])) {
$cluster = $clustersByTagNames[$tagName];
$tagId = $attributeTag['Tag']['id'];
if (isset($clustersByTagIds[$tagId])) {
$cluster = $clustersByTagIds[$tagId];
$galaxyId = $cluster['Galaxy']['id'];
$cluster['local'] = isset($attributeTag['local']) ? $attributeTag['local'] : false;
if (isset($attribute['Galaxy'][$galaxyId])) {
@ -5848,7 +5843,14 @@ class Event extends AppModel
}
}
public function unpublishEvent($id, $proposalLock = false)
/**
* @param int $id
* @param bool $proposalLock
* @param int|null $timestamp If not provided, current time will be used
* @return array|bool|mixed|null
* @throws Exception
*/
public function unpublishEvent($id, $proposalLock = false, $timestamp = null)
{
$event = $this->find('first', array(
'recursive' => -1,
@ -5860,7 +5862,7 @@ class Event extends AppModel
}
$fields = ['published', 'timestamp'];
$event['Event']['published'] = 0;
$event['Event']['timestamp'] = time();
$event['Event']['timestamp'] = $timestamp ?: time();
if ($proposalLock) {
$event['Event']['proposal_email_lock'] = 0;
$fields[] = 'proposal_email_lock';

View File

@ -2,7 +2,6 @@
App::uses('AppModel', 'Model');
App::uses('RandomTool', 'Tools');
App::uses('TmpFileTool', 'Tools');
App::uses('FileAccessTool', 'Tools');
App::uses('AttributeValidationTool', 'Tools');
class Feed extends AppModel

View File

@ -1088,18 +1088,23 @@ class GalaxyCluster extends AppModel
if (isset($options['list']) && $options['list']) {
return $this->find('list', $params);
}
if (isset($options['first']) && $options['first']) {
$clusters = $this->find('first', $params);
} else if (isset($options['count']) && $options['count']) {
$clusterCount = $this->find('count', $params);
return $clusterCount;
return $this->find('count', $params);
} else {
$clusters = $this->find('all', $params);
}
if (empty($clusters)) {
return $clusters;
}
if (isset($options['first']) && $options['first']) {
$clusters = [$clusters];
}
if ($full) {
$clusterIds = array_column(array_column($clusters, 'GalaxyCluster'), 'id');
$targetingClusterRelations = $this->TargetingClusterRelation->fetchRelations($user, array(
@ -1115,11 +1120,15 @@ class GalaxyCluster extends AppModel
$tagsToFetch = Hash::extract($clusters, "{n}.GalaxyClusterRelation.{n}.GalaxyClusterRelationTag.{n}.tag_id");
$tagsToFetch = array_merge($tagsToFetch, Hash::extract($targetingClusterRelations, "GalaxyClusterRelationTag.{n}.tag_id"));
$tags = $this->GalaxyClusterRelation->GalaxyClusterRelationTag->Tag->find('all', [
'conditions' => ['id' => array_unique($tagsToFetch)],
'recursive' => -1,
]);
$tags = array_column(array_column($tags, 'Tag'), null, 'id');
if (!empty($tagsToFetch)) {
$tags = $this->GalaxyClusterRelation->GalaxyClusterRelationTag->Tag->find('all', [
'conditions' => ['id' => array_unique($tagsToFetch)],
'recursive' => -1,
]);
$tags = array_column(array_column($tags, 'Tag'), null, 'id');
} else {
$tags = [];
}
foreach ($targetingClusterRelations as $k => $targetingClusterRelation) {
if (!empty($targetingClusterRelation['GalaxyClusterRelationTag'])) {
@ -1165,6 +1174,11 @@ class GalaxyCluster extends AppModel
}
$clusters[$i] = $this->arrangeData($clusters[$i]);
}
if (isset($options['first']) && $options['first']) {
return $clusters[0];
}
return $clusters;
}

View File

@ -79,9 +79,9 @@ class GalaxyClusterRelation extends AppModel
public function buildConditions($user, $clusterConditions = true)
{
$this->Event = ClassRegistry::init('Event');
$conditions = [];
if (!$user['Role']['perm_site_admin']) {
$this->Event = ClassRegistry::init('Event');
$alias = $this->alias;
$sgids = $this->Event->cacheSgids($user, true);
$gcOwnerIds = $this->SourceCluster->cacheGalaxyClusterOwnerIDs($user);

File diff suppressed because it is too large Load Diff

198
app/Model/SystemSetting.php Normal file
View File

@ -0,0 +1,198 @@
<?php
App::uses('AppModel', 'Model');
App::uses('JsonTool', 'Tools');
App::uses('BetterSecurity', 'Tools');
/**
* Class for ondemand encryption of JSON serialized value
*/
class EncryptedValue implements JsonSerializable
{
const ENCRYPTED_MAGIC = "\x1F\x1D";
/** @var string */
private $value;
public function __construct($value)
{
$this->value = $value;
}
/**
* @return mixed
* @throws JsonException
* @throws Exception
*/
public function decrypt()
{
$decrypt = BetterSecurity::decrypt($this->value, Configure::read('Security.encryption_key'));
return JsonTool::decode($decrypt);
}
public function __toString()
{
return $this->decrypt();
}
public function jsonSerialize()
{
return $this->decrypt();
}
}
class SystemSetting extends AppModel
{
public $actsAs = [
'AuditLog'
];
public $primaryKey = 'setting';
// Blocked setting that cannot be saved or fetched from DB. The same as cli_only settings.
const BLOCKED_SETTINGS = [
'Security.encryption_key',
'Security.disable_local_feed_access',
'GnuPG.binary',
'MISP.python_bin',
'MISP.ca_path',
'MISP.tmpdir',
'MISP.system_setting_db',
'MISP.attachments_dir',
];
// Allow to set config values just for these categories
const ALLOWED_CATEGORIES = [
'MISP',
'Security',
'GnuPG',
'SMIME',
'Proxy',
'SecureAuth',
'Session',
'Plugin',
'debug',
'site_admin_debug',
];
/**
* Set config values from database into global Configure class
*/
public static function setGlobalSetting()
{
$systemSetting = ClassRegistry::init('SystemSetting');
$settings = $systemSetting->getSettings();
foreach ($settings as $settingName => $settingValue) {
$firstPart = explode('.', $settingName)[0];
if (in_array($firstPart, self::ALLOWED_CATEGORIES, true) && !in_array($settingName, self::BLOCKED_SETTINGS, true)) {
Configure::write($settingName, $settingValue);
}
}
}
/**
* @return array
*/
public function getSettings()
{
$settings = $this->find('list', [
'fields' => ['SystemSetting.setting', 'SystemSetting.value'],
]);
return array_map([$this, 'decode'], $settings);
}
/**
* @param string $setting Setting name
* @param mixed $value
* @throws Exception
*/
public function setSetting($setting, $value)
{
$firstPart = explode('.', $setting)[0];
if (!in_array($firstPart, self::ALLOWED_CATEGORIES, true) || in_array($setting, self::BLOCKED_SETTINGS, true)) {
return false; // blocked setting
}
if ($value === '' || $value === null) {
if ($this->hasAny(['SystemSetting.setting' => $setting])) {
return $this->delete($setting); // delete the whole setting when value is empty
}
return true;
}
$value = JsonTool::encode($value);
// If encryption is enabled and setting name contains `password` or `apikey` string, encrypt value to protect it
$key = Configure::read('Security.encryption_key');
if ($key && self::isSensitive($setting)) {
$value = EncryptedValue::ENCRYPTED_MAGIC . BetterSecurity::encrypt($value, $key);
}
$valid = $this->save(['SystemSetting' => [
'setting' => $setting,
'value' => $value,
]]);
if (!$valid) {
throw new Exception("Could not save system setting `$setting` because of validation errors: " . JsonTool::encode($this->validationErrors));
}
return true;
}
/**
* @param string|null $old Old (or current) encryption key.
* @param string|null $new New encryption key. If empty, encrypted values will be decrypted.
* @throws JsonException
*/
public function reencrypt($old, $new)
{
$settings = $this->find('list', [
'fields' => ['SystemSetting.setting', 'SystemSetting.value'],
]);
$toSave = [];
foreach ($settings as $setting => $value) {
if (!self::isSensitive($setting)) {
continue;
}
if (substr($value, 0, 2) === EncryptedValue::ENCRYPTED_MAGIC) {
$value = BetterSecurity::decrypt(substr($value, 2), $old);
}
if (!empty($new)) {
$value = EncryptedValue::ENCRYPTED_MAGIC . BetterSecurity::encrypt($value, $new);
}
$toSave[] = ['SystemSetting' => [
'setting' => $setting,
'value' => $value,
]];
}
return $this->saveMany($toSave);
}
/**
* @param string $value
* @return EncryptedValue|mixed
* @throws JsonException
*/
private function decode($value)
{
if (substr($value, 0, 2) === EncryptedValue::ENCRYPTED_MAGIC) {
return new EncryptedValue(substr($value, 2));
} else {
return JsonTool::decode($value);
}
}
/**
* Sensitive setting are passwords or api keys.
* @param string $setting Setting name
* @return bool
*/
public static function isSensitive($setting)
{
if ($setting === 'Security.encryption_key' || $setting === 'Security.salt') {
return true;
}
if (substr($setting, 0, 7) === 'Plugin.' && (strpos($setting, 'apikey') !== false || strpos($setting, 'secret') !== false)) {
return true;
}
return strpos($setting, 'password') !== false;
}
}

View File

@ -72,9 +72,4 @@ class Template extends AppModel
return false;
}
}
public function generateRandomFileName()
{
return (new RandomTool())->random_str(false, 12);
}
}

View File

@ -39,10 +39,11 @@ class ApacheShibbAuthenticate extends BaseAuthenticate
* 'group_one' => 1,
* ),
* 'DefaultOrg' => 'MY_ORG',
* 'BlockOrgModifications' => false // set to true if you wish for the user's organisation never to be updated during login. Especially useful if you manually change organisations in MISP
* 'DefaultRole' => false // set to a specific value if you wish to hard-set users created via ApacheShibbAuth
* 'BlockRoleModifications' => false // set to true if you wish for the roles never to be updated during login. Especially * // useful if you manually change roles in MISP
* 'BlockOrgModifications' => false // set to true if you wish for the organizations never to be updated during login. Especially * // useful if you manually change orgs in MISP
* 'DefaultRole' => false, // set to a specific value if you wish to hard-set users created via ApacheShibbAuth
* 'BlockRoleModifications' => false, // set to true if you wish for the roles never to be updated during login. Especially
* // useful if you manually change roles in MISP
* 'BlockOrgModifications' => false, // set to true if you wish for the organizations never to be updated during login. Especially
* // useful if you manually change orgs in MISP
* ),
* @param CakeRequest $request The request that contains login information.
* @param CakeResponse $response Unused response object.

View File

@ -145,6 +145,9 @@ in the list given by apache. By default, you can leave it at ';'.
'possible_group_attribute_value_1' => 1,
),
'DefaultOrg' => 'MISP_DEFAULT_ORG',
'DefaultRole' => false, // set to a specific value if you wish to hard-set users created via ApacheShibbAuth
'BlockRoleModifications' => false, // set to true if you wish for the roles never to be updated during login. Especially useful if you manually change roles in MISP
'BlockOrgModifications' => false, // set to true if you wish for the organizations never to be updated during login. Especially useful if you manually change orgs in MISP
),
```

View File

@ -28,10 +28,6 @@
'type' => 'checkbox',
'label' => __('Only find IOCs flagged as to IDS')
));
echo $this->Form->input('alternate', array(
'type' => 'checkbox',
'label' => __('Alternate Search Result (Events)')
));
echo $this->Form->input('first_seen', array(
'type' => 'text',
'div' => 'input hidden',

View File

@ -1,33 +1,33 @@
<?php
$data = $this->DataPathCollector->extract($row, $field['data_path']);
if ($data['Feed.enabled']) {
if (in_array($data['Feed.source_format'], array('freetext', 'csv'))) {
if ($data['Feed.fixed_event']) {
if (!empty($data['Feed.event_error'])) {
$feed = $row['Feed'];
if ($feed['enabled']) {
if (in_array($feed['source_format'], array('freetext', 'csv'))) {
if ($feed['fixed_event']) {
if (!empty($feed['event_error'])) {
echo sprintf(
'<span class="red bold">%s</span>',
__('Error: Invalid event!')
);
} else {
if ($feed['event_id']) {
echo sprintf(
'<span class="red bold">%s</span>',
__('Error: Invalid event!')
'<a href="%s/events/view/%s">%s</a>',
$baseurl,
h($feed['event_id']),
__('Fixed event %s', h($feed['event_id']))
);
} else {
if ($data['Feed.event_id']) {
echo sprintf(
'<a href="%s/events/view/%s">%s</a>',
$baseurl,
h($data['Feed.event_id']),
__('Fixed event %s', h($data['Feed.event_id']))
);
} else {
echo __('New fixed event');
}
echo __('New fixed event');
}
} else {
echo sprintf(
'<span class="bold red" title="%s">%s</span>',
__('New event each pull can lead to potentially endlessly growing correlation tables. Only use this setting if you are sure that the data in the feed will mostly be completely distinct between each individual pull, otherwise use fixed events. Generally this setting is NOT recommended.'),
__('New event each pull')
);
}
} else {
echo sprintf(
'<span class="bold red" title="%s">%s</span>',
__('New event each pull can lead to potentially endlessly growing correlation tables. Only use this setting if you are sure that the data in the feed will mostly be completely distinct between each individual pull, otherwise use fixed events. Generally this setting is NOT recommended.'),
__('New event each pull')
);
}
} else {
echo __('Feed not enabled');
}
} else {
echo __('Feed not enabled');
}

View File

@ -495,8 +495,8 @@
'html' => sprintf(
'<span class="fas fa-star %s" id="setHomePage" title="%s" role="img" aria-label="%s" data-current-page="%s"></span>',
(!empty($homepage['path']) && $homepage['path'] === $this->here) ? 'orange' : '',
__('Set the current page as your home page in MISP'),
__('Set the current page as your home page in MISP'),
__('Set the current page as your home page in MISP'),
__('Set the current page as your home page in MISP'),
h($this->here)
)
),
@ -505,6 +505,12 @@
'url' => empty($homepage['path']) ? $baseurl : $baseurl . h($homepage['path']),
'html' => '<span class="logoBlueStatic bold" id="smallLogo">MISP</span>'
),
[
'type' => 'root',
'url' => Configure::read('MISP.menu_custom_right_link'),
'html' => Configure::read('MISP.menu_custom_right_link_html'),
'requirement' => !empty(Configure::read('MISP.menu_custom_right_link')),
],
array(
'type' => 'root',
'url' => $baseurl . '/dashboards',
@ -528,7 +534,7 @@
);
}
?>
<div id="topBar" class="navbar navbar-inverse <?php echo $debugMode;?>">
<div id="topBar" class="navbar navbar-inverse <?= $debugMode ?>">
<div class="navbar-inner">
<ul class="nav">
<?php

View File

@ -7,7 +7,9 @@
2 => 'success',
3 => 'info'
);
if ($setting['type'] == 'boolean') $setting['value'] = ($setting['value'] === true ? 'true' : 'false');
if ($setting['type'] === 'boolean') {
$setting['value'] = $setting['value'] === true ? 'true' : 'false';
}
if (isset($setting['options'])) {
$setting['value'] = empty($setting['options'][$setting['value']]) ? null : $setting['options'][$setting['value']];
}
@ -100,4 +102,4 @@
$columns
);
}
?>

View File

@ -1,32 +0,0 @@
<?php
/*
* Helper used to extract variables from an array based on path
* Used by the index factories
*
*/
App::uses('AppHelper', 'View/Helper');
class DataPathCollectorHelper extends AppHelper {
public function extract($data, $data_path, $options = array())
{
$result = array();
if (!is_array($data_path)) {
$data_path = array($data_path);
}
foreach ($data_path as $path) {
$temp = Hash::extract($data, $path);
if (is_array($temp)) {
if (count($temp) > 1) {
$temp = implode(', ', $temp);
} else {
if (count($temp) > 0) {
$temp = $temp[0];
} else {
$temp = '';
}
}
}
$result[$path] = $temp;
}
return $result;
}
}

View File

@ -1,24 +0,0 @@
<?php
App::uses('AppHelper', 'View/Helper');
class XmlOutputHelper extends AppHelper {
public function recursiveEcho($array) {
foreach ($array as $k => $v) {
if (is_array($v)) {
if (empty($v)) echo '<' . $k . '/>';
else {
foreach ($v as $element) {
echo '<' . $k . '>';
$this->recursiveEcho($element);
echo '</' . $k . '>';
}
}
} else {
if ($v === false) $v = 0;
if ($v === "" || $v === null) echo '<' . $k . '/>';
else {
echo '<' . $k . '>' . $v . '</' . $k . '>';
}
}
}
}
}

View File

@ -7,8 +7,8 @@
<p><?php echo __('Available Organisations');?></p>
<select id="leftValues" size="5" multiple style="width:285px;">
<?php
foreach ($orgs as $org) {
echo '<option value="' . h($org['id']) . '" selected>' . h($org['name']) . '</option>';
foreach ($orgs as $orgId => $orgName) {
echo '<option value="' . intval($orgId) . '" selected>' . h($orgName) . '</option>';
}
?>
</select>

View File

@ -1,17 +1,17 @@
<div class="server index">
<?php if ($writeableFiles[APP . 'Config/config.php'] != 0): ?>
<div class="bold" style="background-color:red;width:100%;color:white;"><span style="padding-left:10px;"><?php echo __('Warning: app/Config/config.php is not writeable. This means that any setting changes made here will NOT be saved.');?></span></div>
<?php if ($writeableFiles[APP . 'Config/config.php'] != 0 && !Configure::read('MISP.system_setting_db')): ?>
<div class="alert alert-error"><?= __('Warning: app/Config/config.php is not writeable. This means that any setting changes made here will NOT be saved.') ?></div>
<?php endif; ?>
<h2><?php echo __('Server Settings & Maintenance');?></h2>
<?php
echo $this->element('healthElements/tabs', array('active_tab' => $tab));
if (in_array($tab, array('MISP', 'Security', 'Encryption', 'Proxy', 'Plugin'))) {
if (in_array($tab, ['MISP', 'Security', 'Encryption', 'Proxy', 'Plugin'], true)) {
echo $this->element('healthElements/settings_tab');
} else if ($tab == 'diagnostics') {
} else if ($tab === 'diagnostics') {
echo $this->element('healthElements/diagnostics');
} else if ($tab == 'workers') {
} else if ($tab === 'workers') {
echo $this->element('healthElements/workers');
} else if ($tab == 'files') {
} else if ($tab === 'files') {
echo $this->element('healthElements/files');
} else {
echo $this->element('healthElements/overview');
@ -20,14 +20,11 @@
<div style="font-style: italic;"><?php echo __('To edit a setting, simply double click it.');?></div>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('#liveFilterField').focus();
$('#liveFilterField').keyup(function() {
$(function() {
$('#liveFilterField').focus().keyup(function() {
liveFilter();
});
});
</script>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'serverSettings'));
?>
<?= $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'serverSettings'));

@ -1 +1 @@
Subproject commit ae6a527bcb11767abafcc46939c269d297c5de19
Subproject commit 0f0093ba4b59e469c0e75bd15415ccddc679e4c7

View File

@ -6082,7 +6082,7 @@ components:
format: date
example: "2021-03-05"
org:
description: "Filter events by matching an the creator organisation name"
description: "Filter events by matching the creator organisation name"
type: string
nullable: true
example: "CIRCL"

View File

@ -6144,6 +6144,30 @@
"extra": ""
}
],
"system_settings": [
{
"column_name": "setting",
"is_nullable": "NO",
"data_type": "varchar",
"character_maximum_length": "255",
"numeric_precision": null,
"collation_name": "utf8mb4_unicode_ci",
"column_type": "varchar(255)",
"column_default": null,
"extra": ""
},
{
"column_name": "value",
"is_nullable": "NO",
"data_type": "blob",
"character_maximum_length": "65535",
"numeric_precision": null,
"collation_name": null,
"column_type": "blob",
"column_default": null,
"extra": ""
}
],
"tags": [
{
"column_name": "id",
@ -8096,6 +8120,9 @@
"source": false,
"type": false
},
"system_settings": {
"setting": true
},
"tags": {
"id": true,
"name": true,

View File

@ -25,6 +25,9 @@ coreCAKE () {
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Session.autoRegenerate" 0
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Session.timeout" 600
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Session.cookieTimeout" 3600
# Set the default temp dir
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.tmpdir" "${PATH_TO_MISP}/app/tmp"
# Change base url, either with this CLI command or in the UI
[[ ! -z ${MISP_BASEURL} ]] && ${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Baseurl $MISP_BASEURL
@ -46,7 +49,7 @@ coreCAKE () {
# Enable installer org and tune some configurables
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.host_org_id" 1
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.email" "info@admin.test"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.disable_emailing" true
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.disable_emailing" true --force
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.contact" "info@admin.test"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.disablerestalert" true
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.showCorrelationsOnIndex" true
@ -57,7 +60,7 @@ coreCAKE () {
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_services_url" "http://127.0.0.1"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_services_port" 9000
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_timeout" 120
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_authkey" ""
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_authkey" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_ssl_verify_peer" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_ssl_verify_host" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Cortex_ssl_allow_self_signed" true
@ -116,7 +119,7 @@ coreCAKE () {
Plugin.ElasticSearch_logging_enable
Plugin.S3_enable)
for PLUG in "${PLUGS[@]}"; do
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting ${PLUG} false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting ${PLUG} false 2> /dev/null
done
# Plugin CustomAuth tuneable
@ -132,7 +135,7 @@ coreCAKE () {
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_minimum_ttl" "1h"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_ttl" "1w"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_ns" "localhost."
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_ns_alt" ""
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_ns_alt" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.RPZ_email" "root.localhost"
# Kafka settings

View File

@ -30,7 +30,7 @@ sudo update-grub > /dev/null 2>&1
```
!!! notice
On recent Ubuntu install Netplan is default and you need to change the Network name.
On recent Ubuntu install Netplan is default and you might need to change the Network name in its respective config file.
```
sudo sed -i "s/enp0s3/eth0/" /etc/netplan/50-cloud-init.yaml
```
@ -38,3 +38,7 @@ sudo update-grub > /dev/null 2>&1
```
sudo sed -i "s/enp0s3/eth0/" /etc/netplan/01-netcfg.yaml
```
OR on Ubuntu 22.04
```
sudo sed -i "s/enp0s3/eth0/" /etc/netplan/00-installer-config.yaml
```

View File

@ -59,7 +59,8 @@ MISPvars () {
# MISP configuration variables
PATH_TO_MISP="${PATH_TO_MISP:-/var/www/MISP}"
PATH_TO_MISP_SCRIPTS="${PATH_TO_MISP}/app/files/scripts"
## For future use
# TMPDIR="${TMPDIR:-$PATH_TO_MISP/app/tmp}"
FQDN="${FQDN:-misp.local}"

View File

@ -38,6 +38,7 @@ mispmodules () {
# If you build an egg, the user you build it as need write permissions in the CWD
sudo chgrp $WWW_USER .
sudo chmod og+w .
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install pillow
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install -I -r REQUIREMENTS
sudo chgrp staff .
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install -I .

531
docs/xINSTALL.ubuntu2204.md Normal file
View File

@ -0,0 +1,531 @@
# INSTALLATION INSTRUCTIONS
## for Ubuntu 22.04-server
{!generic/manual-install-notes.md!}
### -1/ Installer and Manual install instructions
Make sure you are reading the parsed version of this Document. When in doubt [click here](https://misp.github.io/MISP/INSTALL.ubuntu2004/).
### 0/ MISP Ubuntu 20.04-server install - status
-------------------------
!!! notice
Installer tested working by [@SteveClement](https://twitter.com/SteveClement) on 20211002
!!! notice
If the next line is `[!generic/core.md!]()` [click here](https://misp.github.io/MISP/INSTALL.ubuntu2204/).
{!generic/core.md!}
### 1/ Minimal Ubuntu install
-------------------------
#### Install a minimal Ubuntu 20.04-server system with the software:
- OpenSSH server
- This guide assumes a user name of 'misp' with sudo working but can be overwritten by setting the environment variable: *${MISP_USER}*
#### Make sure your system is up2date
```bash
# <snippet-begin 0_apt-upgrade.sh>
aptUpgrade () {
debug "Upgrading system"
checkAptLock
# If we run in non-interactive mode, make sure we do not stop all of a sudden
if [[ "${PACKER}" == "1" || "${UNATTENDED}" == "1" ]]; then
export DEBIAN_FRONTEND=noninteractive
export DEBIAN_PRIORITY=critical
sudo -E apt-get -qy -o "Dpkg::Options::=--force-confdef" -o "Dpkg::Options::=--force-confold" upgrade
sudo -E apt-get -qy autoclean
else
sudo apt-get upgrade -qy
fi
}
# <snippet-end 0_apt-upgrade.sh>
```
{!generic/sudo_etckeeper.md!}
{!generic/ethX.md!}
#### install postfix, there will be some questions.
```bash
# <snippet-begin postfix.sh>
sudo apt-get install postfix dialog -qy
# <snippet-end postfix.sh>
```
!!! notice
Postfix Configuration: Satellite system<br />
change the relay server later with:
```bash
sudo postconf -e 'relayhost = example.com'
sudo postfix reload
```
{!generic/globalVariables.md!}
### 2/ Install LAMP & dependencies
------------------------------
Once the system is installed you can perform the following steps.
```bash
# <snippet-begin 0_installCoreDeps.sh>
installCoreDeps () {
debug "Installing core dependencies"
# Install the dependencies: (some might already be installed)
sudo apt-get install curl gcc git gpg-agent make python3 openssl redis-server sudo vim zip unzip virtualenv libfuzzy-dev sqlite3 moreutils -qy
# Install MariaDB (a MySQL fork/alternative)
sudo apt-get install mariadb-client mariadb-server -qy
# Install Apache2
sudo apt-get install apache2 apache2-doc apache2-utils -qy
# install Mitre's STIX and its dependencies by running the following commands:
sudo apt-get install python3-dev python3-pip libxml2-dev libxslt1-dev zlib1g-dev python-setuptools -qy
}
# <snippet-end 0_installCoreDeps.sh>
# <snippet-begin 0_installDepsPhp80.sh>
# Install Php 8.0 dependencies
# FIXME: Ugly hack to get 7.4 working until 8.0 (cake4) will be implemented.
echo "deb http://ppa.launchpad.net/ondrej/php/ubuntu devel main" |sudo tee /etc/apt/sources.list.d/ondrej-ubuntu-php-devel.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4F4EA0AAE5267A6C
sudo apt update
installDepsPhp80 () {
debug "Installing PHP 8.0 dependencies"
PHP_ETC_BASE=/etc/php/7.4
PHP_INI=${PHP_ETC_BASE}/apache2/php.ini
checkAptLock
sudo apt install -qy \
libapache2-mod-php7.4 \
php7.4 php7.4-cli \
php7.4-dev \
php-json php7.4-xml php7.4-mysql php7.4-opcache php7.4-readline php7.4-mbstring php7.4-zip \
php-redis php-gnupg \
php7.4-intl php7.4-bcmath \
php7.4-gd
for key in upload_max_filesize post_max_size max_execution_time max_input_time memory_limit
do
sudo sed -i "s/^\($key\).*/\1 = $(eval echo \${$key})/" $PHP_INI
done
sudo sed -i "s/^\(session.sid_length\).*/\1 = $(eval echo \${session0sid_length})/" $PHP_INI
sudo sed -i "s/^\(session.use_strict_mode\).*/\1 = $(eval echo \${session0use_strict_mode})/" $PHP_INI
}
# <snippet-end 0_installDepsPhp80.sh>
```
### 3/ MISP code
------------
```bash
# <snippet-begin 1_mispCoreInstall.sh>
installCore () {
debug "Installing ${LBLUE}MISP${NC} core"
# Download MISP using git in the /var/www/ directory.
if [[ ! -d ${PATH_TO_MISP} ]]; then
sudo mkdir ${PATH_TO_MISP}
sudo chown ${WWW_USER}:${WWW_USER} ${PATH_TO_MISP}
false; while [[ $? -ne 0 ]]; do checkAptLock; ${SUDO_WWW} git clone https://github.com/MISP/MISP.git ${PATH_TO_MISP}; done
false; while [[ $? -ne 0 ]]; do checkAptLock; ${SUDO_WWW} git -C ${PATH_TO_MISP} submodule update --progress --init --recursive; done
# Make git ignore filesystem permission differences for submodules
${SUDO_WWW} git -C ${PATH_TO_MISP} submodule foreach --recursive git config core.filemode false
# Make git ignore filesystem permission differences
${SUDO_WWW} git -C ${PATH_TO_MISP} config core.filemode false
# Create a python3 virtualenv
${SUDO_WWW} virtualenv -p python3 ${PATH_TO_MISP}/venv
# make pip happy
sudo mkdir /var/www/.cache/
sudo chown ${WWW_USER}:${WWW_USER} /var/www/.cache
# install python-stix dependencies
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install ordered-set python-dateutil six weakrefmethod
debug "Install PyMISP"
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install ${PATH_TO_MISP}/PyMISP
# FIXME: Remove libfaup etc once the egg has the library baked-in
sudo apt-get install cmake libcaca-dev liblua5.3-dev -y
cd /tmp
false; while [[ $? -ne 0 ]]; do [[ ! -d "faup" ]] && ${SUDO_CMD} git clone https://github.com/stricaud/faup.git faup; done
false; while [[ $? -ne 0 ]]; do [[ ! -d "gtcaca" ]] && ${SUDO_CMD} git clone https://github.com/stricaud/gtcaca.git gtcaca; done
sudo chown -R ${MISP_USER}:${MISP_USER} faup gtcaca
cd gtcaca
${SUDO_CMD} mkdir -p build
cd build
${SUDO_CMD} cmake .. && ${SUDO_CMD} make
sudo make install
cd ../../faup
${SUDO_CMD} mkdir -p build
cd build
${SUDO_CMD} cmake .. && ${SUDO_CMD} make
sudo make install
sudo ldconfig
# install pydeep
false; while [[ $? -ne 0 ]]; do checkAptLock; ${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install git+https://github.com/kbandla/pydeep.git; done
# install lief
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install lief
# install zmq needed by mispzmq
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install zmq redis
# install python-magic
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install python-magic
# install plyara
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install plyara
else
debug "Trying to git pull existing install"
${SUDO_WWW} git pull -C ${PATH_TO_MISP}
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git -C ${PATH_TO_MISP} submodule update --progress --init --recursive; done
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install -U ${PATH_TO_MISP}/PyMISP
false; while [[ $? -ne 0 ]]; do checkAptLock; ${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install -U git+https://github.com/kbandla/pydeep.git; done
fi
}
# <snippet-end 1_mispCoreInstall.sh>
```
### 4/ CakePHP
-----------
```bash
# <snippet-begin 1_installCake.sh>
installCake () {
debug "Installing CakePHP"
# Make composer cache happy
# /!\ composer on Ubuntu when invoked with sudo -u doesn't set $HOME to /var/www but keeps it /home/misp \!/
sudo mkdir -p /var/www/.composer ; sudo chown ${WWW_USER}:${WWW_USER} /var/www/.composer
${SUDO_WWW} sh -c "cd ${PATH_TO_MISP}/app ;php composer.phar install --no-dev"
# Enable CakeResque with php-redis
sudo phpenmod redis
sudo phpenmod gnupg
# To use the scheduler worker for scheduled tasks, do the following:
${SUDO_WWW} cp -fa ${PATH_TO_MISP}/INSTALL/setup/config.php ${PATH_TO_MISP}/app/Plugin/CakeResque/Config/config.php
# If you have multiple MISP instances on the same system, don't forget to have a different Redis per MISP instance for the CakeResque workers
# The default Redis port can be updated in Plugin/CakeResque/Config/config.php
}
# <snippet-end 1_installCake.sh>
```
### 5/ Set the permissions
----------------------
```bash
# <snippet-begin 2_permissions.sh>
# Main function to fix permissions to something sane
permissions () {
debug "Setting permissions"
sudo chown -R ${WWW_USER}:${WWW_USER} ${PATH_TO_MISP}
sudo chmod -R 750 ${PATH_TO_MISP}
sudo chmod -R g+ws ${PATH_TO_MISP}/app/tmp
sudo chmod -R g+ws ${PATH_TO_MISP}/app/files
sudo chmod -R g+ws ${PATH_TO_MISP}/app/files/scripts/tmp
}
# <snippet-end 2_permissions.sh>
```
### 6/ Create a database and user
-----------------------------
#### Set-up DB, User and import empty MISP DB
```bash
# <snippet-begin 1_prepareDB.sh>
prepareDB () {
if sudo test ! -e "/var/lib/mysql/mysql/"; then
#Make sure initial tables are created in MySQL
debug "Install mysql tables"
sudo mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
sudo service mysql start
fi
if sudo test ! -e "/var/lib/mysql/misp/"; then
debug "Start mysql"
sudo service mysql start
debug "Setting up database"
# Kill the anonymous users
sudo mysql -h $DBHOST -e "DROP USER IF EXISTS ''@'localhost'"
# Because our hostname varies we'll use some Bash magic here.
sudo mysql -h $DBHOST -e "DROP USER IF EXISTS ''@'$(hostname)'"
# Kill off the demo database
sudo mysql -h $DBHOST -e "DROP DATABASE IF EXISTS test"
# No root remote logins
sudo mysql -h $DBHOST -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')"
# Make sure that NOBODY can access the server without a password
sudo mysqladmin -h $DBHOST -u "${DBUSER_ADMIN}" password "${DBPASSWORD_ADMIN}"
# Make our changes take effect
sudo mysql -h $DBHOST -e "FLUSH PRIVILEGES"
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "CREATE DATABASE ${DBNAME};"
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "CREATE USER '${DBUSER_MISP}'@'localhost' IDENTIFIED BY '${DBPASSWORD_MISP}';"
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "GRANT USAGE ON *.* to '${DBUSER_MISP}'@'localhost';"
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "GRANT ALL PRIVILEGES on ${DBNAME}.* to '${DBUSER_MISP}'@'localhost';"
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "FLUSH PRIVILEGES;"
# Import the empty MISP database from MYSQL.sql
${SUDO_WWW} cat ${PATH_TO_MISP}/INSTALL/MYSQL.sql | mysql -h $DBHOST -u "${DBUSER_MISP}" -p"${DBPASSWORD_MISP}" ${DBNAME}
fi
}
# <snippet-end 1_prepareDB.sh>
```
### 7/ Apache configuration
-----------------------
Now configure your Apache webserver with the DocumentRoot ${PATH_TO_MISP}/app/webroot/
#### Apache version 2.4 config:
!!! notice
Be aware that the configuration files for apache 2.4 and up have changed.
The configuration file has to have the .conf extension in the sites-available directory
For more information, visit http://httpd.apache.org/docs/2.4/upgrading.html
```bash
# <snippet-begin 1_apacheConfig.sh>
apacheConfig () {
debug "Generating Apache config, if this hangs, make sure you have enough entropy (install: haveged or wait)"
sudo cp ${PATH_TO_MISP}/INSTALL/apache.24.misp.ssl /etc/apache2/sites-available/misp-ssl.conf
if [[ ! -z ${MISP_BASEURL} ]] && [[ "$(echo $MISP_BASEURL|cut -f 1 -d :)" == "http" || "$(echo $MISP_BASEURL|cut -f 1 -d :)" == "https" ]]; then
echo "Potentially replacing misp.local with $MISP_BASEURL in misp-ssl.conf"
fi
# If a valid SSL certificate is not already created for the server,
# create a self-signed certificate:
sudo openssl req -newkey rsa:4096 -days 365 -nodes -x509 \
-subj "/C=${OPENSSL_C}/ST=${OPENSSL_ST}/L=${OPENSSL_L}/O=${OPENSSL_O}/OU=${OPENSSL_OU}/CN=${OPENSSL_CN}/emailAddress=${OPENSSL_EMAILADDRESS}" \
-keyout /etc/ssl/private/misp.local.key -out /etc/ssl/private/misp.local.crt
# Enable modules, settings, and default of SSL in Apache
sudo a2dismod status
sudo a2enmod ssl
sudo a2enmod rewrite
sudo a2enmod headers
sudo a2dissite 000-default
sudo a2ensite default-ssl
# Apply all changes
sudo systemctl restart apache2
# activate new vhost
sudo a2dissite default-ssl
sudo a2ensite misp-ssl
# Restart apache
sudo systemctl restart apache2
}
# <snippet-end 1_apacheConfig.sh>
```
!!! notice
Please find a sample conf file for an SSL enabled conf file in-line below (alternatively use one of the samples provided in /var/www/MISP/INSTALL).<br />
Also remember to verify the SSLCertificateChainFile property in your config file.<br />
This is usually commented out for the self-generated certificate in the sample configurations, such as the one pasted below.<br />
Otherwise, copy the SSLCertificateFile, SSLCertificateKeyFile, and SSLCertificateChainFile to /etc/ssl/private/. (Modify path and config to fit your environment)
```
============================================= Begin sample working SSL config for MISP
<VirtualHost <IP, FQDN, or *>:80>
ServerName <your.FQDN.here>
Redirect permanent / https://<your.FQDN.here>
LogLevel warn
ErrorLog /var/log/apache2/misp.local_error.log
CustomLog /var/log/apache2/misp.local_access.log combined
ServerSignature Off
</VirtualHost>
<VirtualHost <IP, FQDN, or *>:443>
ServerAdmin admin@<your.FQDN.here>
ServerName <your.FQDN.here>
DocumentRoot /var/www/MISP/app/webroot
<Directory /var/www/MISP/app/webroot>
Options -Indexes
AllowOverride all
Order allow,deny
allow from all
</Directory>
SSLEngine On
SSLCertificateFile /etc/ssl/private/misp.local.crt
SSLCertificateKeyFile /etc/ssl/private/misp.local.key
# SSLCertificateChainFile /etc/ssl/private/misp-chain.crt
LogLevel warn
ErrorLog /var/log/apache2/misp.local_error.log
CustomLog /var/log/apache2/misp.local_access.log combined
ServerSignature Off
</VirtualHost>
============================================= End sample working SSL config for MISP
```
### 8/ Log rotation
---------------
```bash
# <snippet-begin 2_logRotation.sh>
logRotation () {
# MISP saves the stdout and stderr of its workers in ${PATH_TO_MISP}/app/tmp/logs
# To rotate these logs install the supplied logrotate script:
sudo cp ${PATH_TO_MISP}/INSTALL/misp.logrotate /etc/logrotate.d/misp
sudo chmod 0640 /etc/logrotate.d/misp
}
# <snippet-end 2_logRotation.sh>
```
### 9/ MISP configuration
---------------------
```bash
# <snippet-begin 2_configMISP.sh>
configMISP () {
debug "Generating ${LBLUE}MISP${NC} config files"
# There are 4 sample configuration files in ${PATH_TO_MISP}/app/Config that need to be copied
${SUDO_WWW} cp -a ${PATH_TO_MISP}/app/Config/bootstrap.default.php ${PATH_TO_MISP}/app/Config/bootstrap.php
${SUDO_WWW} cp -a ${PATH_TO_MISP}/app/Config/database.default.php ${PATH_TO_MISP}/app/Config/database.php
${SUDO_WWW} cp -a ${PATH_TO_MISP}/app/Config/core.default.php ${PATH_TO_MISP}/app/Config/core.php
${SUDO_WWW} cp -a ${PATH_TO_MISP}/app/Config/config.default.php ${PATH_TO_MISP}/app/Config/config.php
echo "<?php
class DATABASE_CONFIG {
public \$default = array(
'datasource' => 'Database/Mysql',
//'datasource' => 'Database/Postgres',
'persistent' => false,
'host' => '$DBHOST',
'login' => '$DBUSER_MISP',
'port' => 3306, // MySQL & MariaDB
//'port' => 5432, // PostgreSQL
'password' => '$DBPASSWORD_MISP',
'database' => '$DBNAME',
'prefix' => '',
'encoding' => 'utf8',
);
}" | ${SUDO_WWW} tee ${PATH_TO_MISP}/app/Config/database.php
# Important! Change the salt key in ${PATH_TO_MISP}/app/Config/config.php
# The salt key must be a string at least 32 bytes long.
# The admin user account will be generated on the first login, make sure that the salt is changed before you create that user
# If you forget to do this step, and you are still dealing with a fresh installation, just alter the salt,
# delete the user from mysql and log in again using the default admin credentials (admin@admin.test / admin)
# and make sure the file permissions are still OK
sudo chown -R ${WWW_USER}:${WWW_USER} ${PATH_TO_MISP}/app/Config
sudo chmod -R 750 ${PATH_TO_MISP}/app/Config
}
# <snippet-end 2_configMISP.sh>
```
{!generic/gnupg.md!}
!!! notice
If entropy is not high enough, you can install havegd and then start the service
```bash
sudo apt install haveged -qy
sudo service haveged start
```
```bash
# <snippet-begin 2_backgroundWorkers.sh>
backgroundWorkers () {
debug "Setting up background workers"
# To make the background workers start on boot
sudo chmod +x ${PATH_TO_MISP}/app/Console/worker/start.sh
if [ ! -e /etc/rc.local ]
then
echo '#!/bin/sh -e' | sudo tee -a /etc/rc.local
echo 'exit 0' | sudo tee -a /etc/rc.local
sudo chmod u+x /etc/rc.local
fi
echo "[Unit]
Description=MISP background workers
After=network.target
[Service]
Type=forking
User=${WWW_USER}
Group=${WWW_USER}
ExecStart=${PATH_TO_MISP}/app/Console/worker/start.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target" | sudo tee /etc/systemd/system/misp-workers.service
sudo systemctl daemon-reload
sudo systemctl enable --now misp-workers
# Add the following lines before the last line (exit 0). Make sure that you replace www-data with your apache user:
sudo sed -i -e '$i \echo never > /sys/kernel/mm/transparent_hugepage/enabled\n' /etc/rc.local
sudo sed -i -e '$i \echo 1024 > /proc/sys/net/core/somaxconn\n' /etc/rc.local
sudo sed -i -e '$i \sysctl vm.overcommit_memory=1\n' /etc/rc.local
}
# <snippet-end 2_backgroundWorkers.sh>
```
```bash
echo "Admin (root) DB Password: $DBPASSWORD_ADMIN"
echo "User (misp) DB Password: $DBPASSWORD_MISP"
```
{!generic/MISP_CAKE_init.md!}
{!generic/misp-modules-debian.md!}
{!generic/misp-modules-cake.md!}
{!generic/INSTALL.done.md!}
{!generic/recommended.actions.md!}
### Optional features
-----------------
#### MISP has a new pub/sub feature, using ZeroMQ. To enable it, simply run the following command
```bash
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install pyzmq
```
#### MISP has a feature for publishing events to Kafka. To enable it, simply run the following commands
```bash
# <snippet-begin 4_kafka.sh>
installKafka () {
sudo apt-get install librdkafka-dev php-dev -y
sudo pecl channel-update pecl.php.net
sudo pecl install rdkafka
echo "extension=rdkafka.so" | sudo tee ${PHP_ETC_BASE}/mods-available/rdkafka.ini
sudo phpenmod rdkafka
sudo service apache2 restart
}
# <snippet-end 4_kafka.sh>
```
{!generic/misp-dashboard-debian.md!}
{!generic/misp-dashboard-cake.md!}
{!generic/viper-debian.md!}
{!generic/ssdeep-debian.md!}
{!generic/mail_to_misp-debian.md!}
{!generic/hardening.md!}
# INSTALL.sh
!!! notice
The following section is an administrative section that is used by the "[INSTALL.sh](https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh)" script.
Please ignore.
{!generic/supportFunctions.md!}

View File

@ -77,6 +77,7 @@ nav:
- 'Debian 10': 'xINSTALL.debian10.md'
- 'Tsurugi Linux': 'xINSTALL.tsurugi.md'
- 'OpenBSD 7.0': 'xINSTALL.OpenBSD.md'
- 'Ubuntu 22.04': 'xINSTALL.ubuntu2204.md'
- Config Guides:
- 'Elastic Search Logging': 'CONFIG.elasticsearch-logging.md'
- 'Amazon S3 attachments': 'CONFIG.s3-attachments.md'

14
tools/fetcher/README.md Normal file
View File

@ -0,0 +1,14 @@
# MISP fetcher
Simple shell script to generate a zip file containing MISP with all submodules and composer libraries.
Simply run the script from its directory and use the zip's contents to update an airgapped MISP's codebase.
You will need to have composer installed and accessible
Assuming the standard MISP install path and www-data as your apache user, just run the following to update your MISP
```
unzip misp_flat.zip /var/www/MISP
chown -R www-data:www-data /var/www/MISP
```

18
tools/fetcher/fetcher.sh Normal file
View File

@ -0,0 +1,18 @@
# Stupid script to fetch MISP's install files including submodules and composer sourced libraries
# This is currently a relative path, highly recommended to replace with an absolute path
# For example, if you want the fetcher to work in /foo/bar/baz, use "/foo/bar/baz/MISPflat"
MISP_FLAT_ROOT="MISPflat"
git clone https://github.com/MISP/MISP.git $MISP_FLAT_ROOT
cd $MISP_FLAT_ROOT
git submodule update --init --recursive
cd ..
cd $MISP_FLAT_ROOT/app
composer install --no-dev
cd ../..
cd $MISP_FLAT_ROOT
zip -r ../misp_flat.zip .
cd ..
rm -rf $MISP_FLAT_ROOT