From 99f79ec3187068f8d69c6e22c99606a9cbb56118 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 15 May 2015 14:58:53 +0200 Subject: [PATCH] First version of the RPZ export - still undocumented - very naive policy settings - limit per event / tags / date range --- app/Controller/AttributesController.php | 35 +++++++++++ app/Lib/Export/RPZExport.php | 78 +++++++++++++++++++++++++ app/Model/Attribute.php | 49 ++++++++++++++++ app/Model/Whitelist.php | 16 +++++ app/View/Attributes/rpz.ctp | 4 ++ 5 files changed, 182 insertions(+) create mode 100644 app/Lib/Export/RPZExport.php create mode 100644 app/View/Attributes/rpz.ctp diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 44983df30..36cb006a2 100755 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -1792,6 +1792,41 @@ class AttributesController extends AppController { $this->set('attributes', $attributes); } + public function rpz($key='download', $tags=false, $eventId=false, $from=false, $to=false, $policy='DROP') { + $simpleFalse = array('eventId', 'tags', 'from', 'to'); + foreach ($simpleFalse as $sF) { + if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false; + } + if (!in_array($policy, array('NXDOMAIN', 'NODATA', 'DROP'))) $policy = 'DROP'; + if ($from) $from = $this->Attribute->Event->dateFieldCheck($from); + if ($to) $from = $this->Attribute->Event->dateFieldCheck($to); + if ($key != 'download') { + // check if the key is valid -> search for users based on key + $user = $this->checkAuthUser($key); + if (!$user) { + throw new UnauthorizedException('This authentication key is not authorized to be used for exports. Contact your administrator.'); + } + } else { + if (!$this->Auth->user('id')) { + throw new UnauthorizedException('You have to be logged in to do that.'); + } + } + $values = $this->Attribute->rpz($this->_checkOrg(), $this->_isSiteAdmin(), $tags, $eventId, $from, $to); + $this->response->type('txt'); // set the content type + $file = ''; + if ($tags) $file = 'filtered.'; + if ($eventId) $file .= 'event-' . $eventId . '.'; + if ($from) $file .= 'from-' . $from . '.'; + if ($to) $file .= 'to-' . $to . '.'; + if ($file == '') $file = 'all'; + $this->header('Content-Disposition: download; filename="misp.rpz.' . $file . '.txt"'); + $this->layout = 'text/default'; + $this->loadModel('Whitelist'); + $values = $this->Whitelist->removeWhitelistedValuesFromArray($values); + $this->set('values', $values); + $this->set('policy', $policy); + //debug($values); + } public function reportValidationIssuesAttributes() { // TODO improve performance of this function by eliminating the additional SQL query per attribute diff --git a/app/Lib/Export/RPZExport.php b/app/Lib/Export/RPZExport.php new file mode 100644 index 000000000..00add1b49 --- /dev/null +++ b/app/Lib/Export/RPZExport.php @@ -0,0 +1,78 @@ + '# The following list of IP addresses will ', + 'domain' => '# The following domain names and all of their sub-domains will ', + 'hostname' => '# The following hostnames will ' + ); + $policy_explanations = array( + 'NXDOMAIN' => 'return NXDOMAIN (name does not exist) irrespective of actual result received.', + 'NODATA' => 'returns NODATA (name exists but no answers returned) irrespective of actual result received.', + 'DROP' => 'timeout.', + ); + return $explanations[$type] . $policy_explanations[$policy] . PHP_EOL . PHP_EOL; + } + + public function export($items, $policy) { + switch ($policy) { + case 'NXDOMAIN': + $action = '.'; + break; + case 'NODATA': + $action = '*.'; + break; + default: + $policy = 'DROP'; + $action = 'rpz-drop.'; + } + $result = ''; + + $result .= $this->explain('ip', $policy); + foreach ($items['ip'] as $item) { + $result .= $this->__convertIP($item, $action); + } + + $result .= $this->explain('domain', $policy); + foreach ($items['domain'] as $item) { + $result .= $this->__convertdomain($item, $action); + } + + $result .= $this->explain('hostname', $policy); + foreach ($items['hostname'] as $item) { + + $result .= $this->__converthostname($item, $action); + } + return $result; + } + + private function __convertdomain($input, $action) { + return $input . ' CNAME ' . $action . PHP_EOL . '*.' . $input . ' CNAME ' . $action . PHP_EOL; + } + + private function __converthostname($input, $action) { + return $input . ' CNAME ' . $action . PHP_EOL; + } + + private function __convertip($input, $action) { + $type = filter_var($input, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? 'ipv6' : 'ipv4'; + if ($type == 'ipv6') $prefix = '128'; + else $prefix = '32'; + if (strpos($input, '/')) { + list($input, $prefix) = explode('/', $input); + } + return $prefix . '.' . $this->{'__' . $type}($input) . ' CNAME ' . $action . PHP_EOL; + } + + private function __ipv6($input) { + return implode('.', array_reverse(preg_split('/:/', str_replace('::', ':zz:', $input), NULL, PREG_SPLIT_NO_EMPTY))); + } + + private function __ipv4($input) { + return implode('.', array_reverse(explode('.', $input))); + + } + +} diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 0747c2645..70c8415e5 100755 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -1314,6 +1314,55 @@ class Attribute extends AppModel { return $attributes; } + public function rpz($org, $isSiteAdmin, $tags = false, $eventId = false, $from = false, $to = false) { + // we can group hostname and domain as well as ip-src and ip-dst in this case + $conditions['AND'] = array('Attribute.to_ids' => 1, 'Event.published' => 1); + $typesToFetch = array('ip' => array('ip-src', 'ip-dst'), 'hostname' => array('hostname'), 'domain' => array('domain')); + if ($from) $conditions['AND']['Event.date >='] = $from; + if ($to) $conditions['AND']['Event.date <='] = $to; + if (!$isSiteAdmin) { + $temp = array(); + $distribution = array(); + array_push($temp, array('Attribute.distribution >' => 0)); + array_push($temp, array('(SELECT events.org FROM events WHERE events.id = Attribute.event_id) LIKE' => $org)); + $conditions['OR'] = $temp; + } + if ($eventId !== false) { + $conditions['AND'][] = array('Event.id' => $eventId); + } elseif ($tags !== false) { + // If we sent any tags along, load the associated tag names for each attribute + $tag = ClassRegistry::init('Tag'); + $args = $this->dissectArgs($tags); + $tagArray = $tag->fetchEventTagIds($args[0], $args[1]); + $temp = array(); + foreach ($tagArray[0] as $accepted) { + $temp['OR'][] = array('Event.id' => $accepted); + } + $conditions['AND'][] = $temp; + $temp = array(); + foreach ($tagArray[1] as $rejected) { + $temp['AND'][] = array('Event.id !=' => $rejected); + } + $conditions['AND'][] = $temp; + } + + foreach ($typesToFetch as $k => $v) { + $params = array( + 'conditions' => array('AND' => + $conditions, + array('type' => $v), + ), + 'fields' => array('Attribute.value'), //array of field names + 'order' => array('Attribute.value'), //string or array defining order + 'group' => array('Attribute.value'), //fields to GROUP BY + ); + $temp = $this->find('all', $params); + foreach ($temp as $value) $values[$k][] = $value['Attribute']['value']; + unset($temp); + } + return $values; + } + public function generateCorrelation() { $this->Correlation = ClassRegistry::init('Correlation'); $this->Correlation->deleteAll(array('id !=' => ''), false); diff --git a/app/Model/Whitelist.php b/app/Model/Whitelist.php index 5b1387545..27e39cf76 100755 --- a/app/Model/Whitelist.php +++ b/app/Model/Whitelist.php @@ -126,4 +126,20 @@ class Whitelist extends AppModel { } return $data; } + + // A simplified whitelist removal, for when we just want to throw values against the list instead of attributes / events + public function removeWhitelistedValuesFromArray($data) { + $whitelists = $this->getBlockedValues(); + // if we don't have any whitelist items in the db, don't loop through each attribute + if (!empty($whitelists)) { + foreach ($data as $k => $value) { + foreach ($whitelists as $wlitem) { + if (preg_match($wlitem, $value)) { + unset($data[$k]); + } + } + } + } + return $data; + } } diff --git a/app/View/Attributes/rpz.ctp b/app/View/Attributes/rpz.ctp new file mode 100644 index 000000000..57d954394 --- /dev/null +++ b/app/View/Attributes/rpz.ctp @@ -0,0 +1,4 @@ +export($values, $policy)); \ No newline at end of file