<?php /** * Copyright (C) 2015 Deciso B.V. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ namespace OPNsense\IDS\Api; use \Phalcon\Filter; use \OPNsense\Base\Filters\QueryFilter; use \OPNsense\Base\ApiControllerBase; use \OPNsense\Core\Backend; use \OPNsense\IDS\IDS; use \OPNsense\Cron\Cron; use \OPNsense\Core\Config; /** * Class ServiceController * @package OPNsense\IDS */ class ServiceController extends ApiControllerBase { /** * start ids service * @return array */ public function startAction() { if ($this->request->isPost()) { $backend = new Backend(); $response = trim($backend->configdRun("ids start")); return array("response" => $response); } else { return array("response" => array()); } } /** * stop ids service * @return array */ public function stopAction() { if ($this->request->isPost()) { $backend = new Backend(); $response = trim($backend->configdRun("ids stop")); return array("response" => $response); } else { return array("response" => array()); } } /** * restart ids service * @return array */ public function restartAction() { if ($this->request->isPost()) { $backend = new Backend(); $response = $backend->configdRun("ids restart"); return array("response" => $response); } else { return array("response" => array()); } } /** * retrieve status of the IDS * @return array * @throws \Exception */ public function statusAction() { $backend = new Backend(); $mdlIDS = new IDS(); $response = $backend->configdRun("ids status"); if (strpos($response, "not running") > 0) { if ((string)$mdlIDS->general->enabled == 1) { $status = "stopped"; } else { $status = "disabled"; } } elseif (strpos($response, "is running") > 0) { $status = "running"; } elseif ((string)$mdlIDS->general->enabled == 0) { $status = "disabled"; } else { $status = "unkown"; } return array("status" => $status); } /** * reconfigure IDS */ public function reconfigureAction() { $status = "failed"; if ($this->request->isPost()) { // close session for long running action $this->sessionClose(); $mdlIDS = new IDS(); $runStatus = $this->statusAction(); // we should always have a cron item configured for IDS, let's create one upon first reconfigure. if ((string)$mdlIDS->general->UpdateCron == "") { $mdlCron = new Cron(); // update cron relation (if this doesn't break consistency) $mdlIDS->general->UpdateCron = $mdlCron->newDailyJob("IDS", "ids update", "ids rule updates", "0"); if ($mdlCron->performValidation()->count() == 0) { $mdlCron->serializeToConfig(); // save data to config, do not validate because the current in memory model doesn't know about the // cron item just created. $mdlIDS->serializeToConfig($validateFullModel = false, $disable_validation = true); Config::getInstance()->save(); } } if ($runStatus['status'] == "running" && (string)$mdlIDS->general->enabled == 0) { $this->stopAction(); } $backend = new Backend(); $bckresult = trim($backend->configdRun('template reload OPNsense/IDS')); if ($bckresult == "OK") { if ((string)$mdlIDS->general->enabled == 1) { $bckresult = trim($backend->configdRun("ids install rules")); if ($bckresult == "OK") { if ($runStatus['status'] == 'running') { $status = $this->restartAction()['response']; } else { $status = $this->startAction()['response']; } } else { $status = "error installing ids rules (".$bckresult.")"; } } else { $status = "OK"; } } else { $status = "error generating ids template (".$bckresult.")"; } } return array("status" => $status); } /** * download and update rules * @param null|string $wait wait for update to complete (default) or run in background and return message id * @return array * @throws \Exception */ public function updateRulesAction($wait = null) { $status = "failed"; if ($this->request->isPost()) { // close session for long running action $this->sessionClose(); $backend = new Backend(); // we have to trigger a template reload to be sure we have the right download configuration // ideally we should only regenerate the download config, but that's not supported at the moment. // (not sure if it should be supported) $bckresult = trim($backend->configdRun('template reload OPNsense/IDS')); if ($bckresult == "OK") { if ($wait != null) { $detach = true; } else { $detach = false; } $status = $backend->configdRun("ids update", $detach); } else { $status = "template error"; } } return array("status" => $status); } /** * flush rule configuration to config and reload suricata ruleset (graceful restart) * @return array */ public function reloadRulesAction() { $status = "failed"; if ($this->request->isPost()) { // close session for long running action $this->sessionClose(); $backend = new Backend(); // flush rule configuration $bckresult = trim($backend->configdRun('template reload OPNsense/IDS')); if ($bckresult == "OK") { $status = $backend->configdRun("ids reload"); } else { $status = "template error"; } } return array("status" => $status); } /** * query suricata alerts * @return array */ public function queryAlertsAction() { if ($this->request->isPost()) { $this->sessionClose(); // create filter to sanitize input data $filter = new Filter(); $filter->add('query', new QueryFilter()); // fetch query parameters (limit results to prevent out of memory issues) $itemsPerPage = $this->request->getPost('rowCount', 'int', 9999); $currentPage = $this->request->getPost('current', 'int', 1); if ($this->request->getPost('searchPhrase', 'string', '') != "") { $filterTag = $filter->sanitize($this->request->getPost('searchPhrase'), "query"); $searchPhrase = 'alert,alert_action,src_ip/"*'.$filterTag .'*"'; } else { $searchPhrase = ''; } if ($this->request->getPost('fileid', 'string', '') != "") { $fileid = $this->request->getPost('fileid', 'int', -1); } else { $fileid = null; } $backend = new Backend(); $response = $backend->configdpRun("ids query alerts", array($itemsPerPage, ($currentPage-1)*$itemsPerPage, $searchPhrase, $fileid)); $result = json_decode($response, true); if ($result != null) { $result['rowCount'] = count($result['rows']); $result['total'] = $result['total_rows']; $result['current'] = (int)$currentPage; return $result; } } return array(); } /** * fetch alert detailed info * @param string $alertId alert id, position in log file * @param string $fileid log file id number (empty for standard) * @return array alert info */ public function getAlertInfoAction($alertId, $fileid = "") { $backend = new Backend(); $filter = new Filter(); $id = $filter->sanitize($alertId, "int"); $response = $backend->configdpRun("ids query alerts", array(1, 0, "filepos/".$id, $fileid)); $result = json_decode($response, true); if ($result != null && count($result['rows']) > 0) { return $result['rows'][0]; } else { return array(); } } /** * list all available logs * @return array list of alert logs * @throws \Exception */ public function getAlertLogsAction() { $backend = new Backend(); $response = $backend->configdRun("ids list alertlogs"); $result = json_decode($response, true); if ($result != null) { $logs = array(); foreach ($result as $log) { $log['modified'] = date('Y/m/d G:i', $log['modified']); $logs[] = $log; } return $logs; } else { return array(); } } /** * drop alert log * @return array status */ public function dropAlertLogAction() { if ($this->request->isPost()) { // close session for long running action $this->sessionClose(); $backend = new Backend(); $filename = $this->request->getPost('filename', 'string', null); if ($filename != null) { $filename = basename($filename); $backend->configdpRun("ids drop alertlog", array($filename)); return array("status" => "ok"); } } return array("status" => "failed"); } }