%PDF- %PDF-
Direktori : /backups/router/usr/local/opnsense/mvc/app/models/OPNsense/IPsec/ |
Current File : //backups/router/usr/local/opnsense/mvc/app/models/OPNsense/IPsec/Swanctl.php |
<?php /* * Copyright (C) 2022 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\IPsec; use OPNsense\Base\Messages\Message; use OPNsense\Base\BaseModel; use OPNsense\Core\Config; use OPNsense\Firewall\Util; /** * Class Swanctl * @package OPNsense\IPsec */ class Swanctl extends BaseModel { /** * convert group ids to group (class) names */ private function gidToNames($gids) { $result = []; $cnf = Config::getInstance()->object(); $mapping = []; if (isset($cnf->system->group)) { foreach ($cnf->system->group as $group) { $mapping[(string)$group->gid] = (string)$group->name; } } foreach (explode(',', $gids) as $gid) { if (!empty($mapping[$gid])) { $result[] = $mapping[$gid]; } } return implode(',', $result); } /** * {@inheritdoc} */ public function performValidation($validateFullModel = false) { $messages = parent::performValidation($validateFullModel); $vtis = []; $spds = []; foreach ($this->getFlatNodes() as $key => $node) { if ($validateFullModel || $node->isFieldChanged()) { $tagName = $node->getInternalXMLTagName(); $parentNode = $node->getParentNode(); $parentKey = $parentNode->__reference; $parentTagName = $parentNode->getInternalXMLTagName(); if ($parentTagName === 'VTI') { $vtis[$parentKey] = $parentNode; } elseif ($parentTagName === 'SPD') { $spds[$parentKey] = $parentNode; } } } foreach ($vtis as $key => $node) { $vti_inets = []; foreach (['local', 'remote', 'tunnel_local', 'tunnel_remote', 'tunnel_local2', 'tunnel_remote2'] as $prop) { if (empty((string)$node->$prop)) { $vti_inets[$prop] = '-'; } else { $vti_inets[$prop] = strpos((string)$node->$prop, ':') > 0 ? 'inet6' : 'inet'; } } if ( (empty((string)$node->local) && !empty((string)$node->remote)) || (!empty((string)$node->local) && empty((string)$node->remote)) ) { $messages->appendMessage( new Message( gettext("A local and remote address should be provided or both should be left empty"), $key . ".local" ) ); } if ($vti_inets['local'] != $vti_inets['remote']) { $messages->appendMessage(new Message(gettext("Protocol families should match"), $key . ".local")); } if ($vti_inets['tunnel_local'] != $vti_inets['tunnel_remote']) { $messages->appendMessage(new Message(gettext("Protocol families should match"), $key . ".tunnel_local")); } if ($vti_inets['tunnel_local2'] != $vti_inets['tunnel_remote2']) { $messages->appendMessage( new Message( gettext("Protocol families should match"), $key . ".tunnel_local2" ) ); } } foreach ($spds as $key => $node) { if ( ((string)$node->reqid == '' && (string)$node->connection_child == '') || ((string)$node->reqid != '' && (string)$node->connection_child != '') ) { $messages->appendMessage( new Message(gettext("Either reqid or child must be set"), $key . ".connection_child") ); } } return $messages; } /** * @return boolean enc0 enabled (any policy set) */ public function isEnabled() { foreach ($this->Connections->Connection->iterateItems() as $node_uuid => $node) { if (!empty((string)$node->enabled)) { return true; } } return false; } /** * generate swanctl configuration output, containing "pools" and "connections", locals, remotes and children * are treated as children of connection. * @return array */ public function getConfig() { $data = ['connections' => [], 'pools' => []]; $references = [ 'pools' => 'Pools.Pool', 'connections' => 'Connections.Connection', 'local' => 'locals.local', 'remote' => 'remotes.remote', 'children' => 'children.child', ]; $pool_names = []; foreach ($references as $key => $ref) { foreach ($this->getNodeByReference($ref)->iterateItems() as $node_uuid => $node) { if (empty((string)$node->enabled)) { continue; } $parent = null; $thisnode = []; foreach ($node->iterateItems() as $attr_name => $attr) { if ($attr_name == 'connection' && isset($data['connections'][(string)$attr])) { $parent = (string)$attr; continue; } elseif ($attr_name == 'pools') { // pools are mapped by name for clearer identification and legacy support if ((string)$attr != '') { $pools = []; foreach (explode(',', (string)$attr) as $pool_id) { $is_uuid = preg_match( '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/', $pool_id ) == 1; if (isset($pool_names[$pool_id])) { $pools[] = $pool_names[$pool_id]; } elseif (!$is_uuid) { $pools[] = $pool_id; } } if (!empty($pools)) { $thisnode['pools'] = implode(',', $pools); } } continue; } elseif ($attr_name == 'enabled') { if (empty((string)$attr)) { // disabled entity $thisnode = []; break; } else { continue; } } elseif ((string)$attr == '') { continue; } elseif (in_array($attr_name, ['name', 'description'])) { if ($key == 'pools') { $pool_names[$node_uuid] = (string)$attr; } continue; } elseif (is_a($attr, 'OPNsense\Base\FieldTypes\AuthGroupField')) { $thisnode[$attr_name] = $this->gidToNames((string)$attr); } elseif (is_a($attr, 'OPNsense\Base\FieldTypes\BooleanField')) { $thisnode[$attr_name] = (string)$attr == '1' ? 'yes' : 'no'; } elseif (is_a($attr, 'OPNsense\Base\FieldTypes\CertificateField')) { $tmp = []; foreach (explode(',', (string)$attr) as $item) { $tmp[] = $item . '.crt'; } $thisnode[$attr_name] = implode(',', $tmp); } elseif ($attr_name == 'pubkeys') { if ((string)$node->auth != 'pubkey') { // explicit skip, pubkeys bound to auth type selection continue; } $tmp = []; foreach (explode(',', (string)$attr) as $item) { $tmp[] = $item . '.pem'; } $thisnode[$attr_name] = implode(',', $tmp); } elseif ($attr_name == 'eap_id' && strpos((string)$node->auth, 'eap') === false) { // explicit skip, eap_id is only valid for eap auth types. continue; } else { $thisnode[$attr_name] = (string)$attr; } } if (empty($thisnode)) { continue; } elseif (!empty($parent)) { if ($key == 'children') { if (!isset($data['connections'][$parent]['children'])) { $data['connections'][$parent]['children'] = []; } $thisnode['updown'] = '/usr/local/opnsense/scripts/ipsec/updown_event.py --connection_child '; $thisnode['updown'] .= $node_uuid; $data['connections'][$parent]['children'][$node_uuid] = $thisnode; } else { $data['connections'][$parent][$key . '-' . $node_uuid] = $thisnode; } } elseif (in_array($key, ['connections', 'pools'])) { if (!isset($data[$key])) { $data[$key] = []; } $data[$key][$node_uuid] = $thisnode; } } } if (!empty($data['pools'])) { // pools are mapped by name for clearer identification and legacy support // need to remap them in the output tree foreach ($data['pools'] as $key => $value) { if (isset($pool_names[$key])) { $data['pools'][$pool_names[$key]] = $value; unset($data['pools'][$key]); } } } return $data; } /** * return non legacy vti devices formatted like ipsec_get_configured_vtis() */ public function getVtiDevices() { $result = []; foreach ($this->VTIs->VTI->iterateItems() as $node_uuid => $node) { if ((string)$node->origin != 'legacy' && (string)$node->enabled == '1') { $inet = strpos((string)$node->tunnel_local, ':') > 0 ? 'inet6' : 'inet'; $result['ipsec' . (string)$node->reqid] = [ 'reqid' => (string)$node->reqid, 'local' => (string)$node->local, 'remote' => (string)$node->remote, 'descr' => (string)$node->description, 'networks' => [ [ 'inet' => $inet, 'tunnel_local' => (string)$node->tunnel_local, 'tunnel_remote' => (string)$node->tunnel_remote, 'mask' => Util::smallestCIDR( [(string)$node->tunnel_local, (string)$node->tunnel_remote], $inet ) ] ] ]; if (!empty((string)$node->tunnel_local2)) { // add optional secondary address $inet = strpos((string)$node->tunnel_local2, ':') > 0 ? 'inet6' : 'inet'; $result['ipsec' . (string)$node->reqid]['networks'][] = [ 'inet' => $inet, 'tunnel_local' => (string)$node->tunnel_local2, 'tunnel_remote' => (string)$node->tunnel_remote2, 'mask' => Util::smallestCIDR( [(string)$node->tunnel_local2, (string)$node->tunnel_remote2], $inet ) ]; } } } return $result; } public function getUsedCertrefs() { $references = [ 'locals' => 'locals.local', 'remotes' => 'remotes.remote', ]; $certrefs = []; foreach ($references as $key => $ref) { foreach ($this->getNodeByReference($ref)->iterateItems() as $node_uuid => $node) { if (empty((string)$node->enabled)) { continue; } elseif (!empty((string)$node->certs)) { foreach (explode(',', (string)$node->certs) as $cert) { if (!in_array($cert, $certrefs)) { $certrefs[] = $cert; } } } } } return $certrefs; } /** * @return bool is there at least one connection using radius groups? */ public function radiusUsesGroups() { foreach ($this->remotes->iterateRecursiveItems() as $node) { if ($node->getInternalXMLTagName() == 'auth' && (string)$node == 'eap-radius') { $auth = $node->getParentNode(); $connid = (string)$auth->connection; if ( !empty((string)$auth->groups) && isset($this->Connections->Connection->$connid) && !empty((string)$this->Connections->Connection->$connid->enabled) ) { return true; } } } return false; } }