%PDF- %PDF-
Direktori : /backups/router/usr/local/opnsense/mvc/app/controllers/OPNsense/IPsec/Api/ |
Current File : //backups/router/usr/local/opnsense/mvc/app/controllers/OPNsense/IPsec/Api/TunnelController.php |
<?php /* * Copyright (C) 2021 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\Api; use OPNsense\Base\ApiControllerBase; use OPNsense\Core\Config; /** * Class TunnelController * @package OPNsense\IPsec\Api */ class TunnelController extends ApiControllerBase { /*** * search phase 1 entries in legacy config returning a standard structure as we use in the mvc variant */ public function searchPhase1Action() { $ph1type = ['ikev1' => 'IKE', 'ikev2' => 'IKEv2', 'ike' => 'auto']; $ph1algos = [ 'aes' => 'AES', 'aes128gcm16' => '128 bit AES-GCM with 128 bit ICV', 'aes192gcm16' => '192 bit AES-GCM with 128 bit ICV', 'aes256gcm16' => '256 bit AES-GCM with 128 bit ICV', 'camellia' => 'Camellia', 'blowfish' => 'Blowfish', '3des' => '3DES', 'cast128' => 'CAST128', 'des' => 'DES' ]; $ph1authmethods = [ 'hybrid_rsa_server' => 'Hybrid RSA + Xauth', 'xauth_rsa_server' => 'Mutual RSA + Xauth', 'xauth_psk_server' => 'Mutual PSK + Xauth', 'eap-tls' => 'EAP-TLS', 'psk_eap-tls' => 'RSA (local) + EAP-TLS (remote)', 'eap-mschapv2' => 'EAP-MSCHAPV2', 'rsa_eap-mschapv2' => 'Mutual RSA + EAP-MSCHAPV2', 'eap-radius' => 'EAP-RADIUS', 'rsasig' => 'Mutual RSA', 'pubkey' => 'Mutual Public Key', 'pre_shared_key' => 'Mutual PSK' ]; $items = []; $config = Config::getInstance()->object(); if (!empty($config->ipsec->phase1)) { $idx = 0; $ifs = []; if ($config->interfaces->count() > 0) { foreach ($config->interfaces->children() as $key => $node) { $ifs[$key] = !empty((string)$node->descr) ? (string)$node->descr : strtoupper($key); } if ($config->virtualip->count() > 0) { foreach ($config->virtualip->children() as $node) { if (!empty((string)$node->vhid)) { $key = (string)$node->interface . '_vip' . (string)$node->vhid; } else { $key = (string)$node->subnet; } $ifs[$key] = "$node->subnet ({$node->descr})"; } } } foreach ($config->ipsec->phase1 as $p1) { $interface = (string)$p1->interface; $ph1proposal = $ph1algos[(string)$p1->{"encryption-algorithm"}->name]; if (!empty((string)$p1->{"encryption-algorithm"}->keylen)) { $ph1proposal .= sprintf(" ({$p1->{"encryption-algorithm"}->keylen} %s)", gettext("bits")); } $ph1proposal .= " + " . strtoupper((string)$p1->{"hash-algorithm"}); if (!empty($p1->dhgroup)) { $ph1proposal .= " + " . gettext("DH Group") . " {$p1->dhgroup}"; } $item = [ "id" => intval((string)$p1->ikeid), // ikeid should be unique "seqid" => $idx, "enabled" => empty((string)$p1->disabled) ? "1" : "0", "protocol" => $p1->protocol == "inet46" ? "IPv4+6" : ($p1->protocol == "inet6" ? "IPv6" : "IPv4"), "iketype" => $ph1type[(string)$p1->iketype], "interface" => !empty($ifs[$interface]) ? $ifs[$interface] : $interface, "remote_gateway" => (string)$p1->{"remote-gateway"}, "mobile" => !empty((string)$p1->mobile), "mode" => (string)$p1->mode, "proposal" => $ph1proposal, "authentication" => $ph1authmethods[(string)$p1->authentication_method], "description" => (string)$p1->descr ]; $item['type'] = "{$item['protocol']} {$item['iketype']}"; $items[] = $item; $idx++; } } return $this->searchRecordsetBase($items); } /*** * search phase 2 entries in legacy config returning a standard structure as we use in the mvc variant */ public function searchPhase2Action() { $selected_ikeid = intval($this->request->getPost('ikeid', 'int', -1)); $ph2algos = [ 'aes' => 'AES', 'aes128' => 'AES128', 'aes192' => 'AES192', 'aes256' => 'AES256', 'aes128gcm16' => 'aes128gcm16', 'aes192gcm16' => 'aes192gcm16', 'aes256gcm16' => 'aes256gcm16', 'null' => gettext("NULL (no encryption)") ]; $ph2halgos = [ 'hmac_sha1' => 'SHA1', 'hmac_sha256' => 'SHA256', 'hmac_sha384' => 'SHA384', 'hmac_sha512' => 'SHA512', 'aesxcbc' => 'AES-XCBC' ]; $items = []; $config = Config::getInstance()->object(); if (!empty($config->ipsec->phase2)) { $p2idx = 0; $ifs = []; if ($config->interfaces->count() > 0) { foreach ($config->interfaces->children() as $key => $node) { $ifs[$key] = !empty((string)$node->descr) ? (string)$node->descr : strtoupper($key); } } foreach ($config->ipsec->phase2 as $p2) { $ikeid = intval((string)$p2->ikeid); if ($ikeid != $selected_ikeid && $selected_ikeid != -1) { $p2idx++; continue; } $p2mode = array_search( (string)$p2->mode, [ "IPv4 tunnel" => "tunnel", "IPv6 tunnel" => "tunnel6", "transport" => "transport", "Route-based" => "route-based" ] ); if (in_array((string)$p2->mode, ['tunnel', 'tunnel6'])) { foreach (['localid', 'remoteid'] as $id) { $content = (string)$p2->$id->type; switch ((string)$p2->$id->type) { case "address": $content = "{$p2->$id->address}"; break; case "network": $content = "{$p2->$id->address}/{$p2->$id->netbits}"; break; case "mobile": $content = gettext("Mobile Client"); break; case "none": $content = gettext("None"); break; default: if (!empty($ifs[(string)$p2->$id->type])) { $content = $ifs[(string)$p2->$id->type]; } break; } if ($id == 'localid') { $local_subnet = $content; } else { $remote_subnet = $content; } } } elseif ((string)$p2->mode == "route-based") { $local_subnet = (string)$p2->tunnel_local; $remote_subnet = (string)$p2->tunnel_remote; } else { $local_subnet = ""; $remote_subnet = ""; } $ph2proposal = ""; foreach ($p2->{"encryption-algorithm-option"} as $node) { $this_proposal = isset($ph2algos[(string)$node->name]) ? $ph2algos[(string)$node->name] : ""; if (!empty($ph2proposal)) { $ph2proposal .= " , "; } if (!empty($this_proposal)) { $ph2proposal .= $this_proposal; if ((string)$node->keylen == 'auto') { $ph2proposal .= " (auto) "; } elseif (!empty((string)$node->keylen)) { $ph2proposal .= sprintf(" ({$node->keylen} %s) ", gettext("bits")); } } else { // Unsupported algorithm, will be ignored $ph2proposal .= "!!" . (string)$node->name; } } $ph2proposal .= " + "; $idx = 0; foreach ($p2->{"hash-algorithm-option"} as $node) { if (isset($ph2halgos[(string)$node])) { $ph2proposal .= ($idx++) > 0 ? " , " : ""; $ph2proposal .= $ph2halgos[(string)$node]; } } if (!empty((string)$p2->pfsgroup)) { $ph2proposal .= sprintf("+ %s %s", gettext("DH Group"), "{$p2->pfsgroup}"); } $item = [ "id" => $p2idx, "uniqid" => (string)$p2->uniqid, // XXX: a bit convoluted, should probably replace id at some point "ikeid" => $ikeid, "reqid" => (string)$p2->reqid, "enabled" => empty((string)$p2->disabled) ? "1" : "0", "protocol" => $p2->protocol == "esp" ? "ESP" : "AH", "mode" => $p2mode, "local_subnet" => $local_subnet, "remote_subnet" => $remote_subnet, "proposal" => $ph2proposal, "description" => (string)$p2->descr ]; $items[] = $item; $p2idx++; } } return $this->searchRecordsetBase($items); } /** * delete phase 1 including associated phase 2 entries */ public function delPhase1Action($ikeid) { if ($this->request->isPost()) { $phase_ids = []; Config::getInstance()->lock(); $config = Config::getInstance()->object(); foreach ([$config->ipsec->phase1, $config->ipsec->phase2] as $phid => $phase) { $phase_ids[$phid] = []; if (!empty($phase)) { $idx = 0; foreach ($phase as $p) { if (intval((string)$p->ikeid) == intval($ikeid)) { $phase_ids[$phid][] = $idx; } $idx++; } foreach (array_reverse($phase_ids[$phid]) as $idx) { unset($phase[$idx]); } } } Config::getInstance()->save(); if (!empty($phase_ids[0])) { @touch("/tmp/ipsec.dirty"); } return [ 'status' => 'ok', 'phase1count' => count($phase_ids[0]), // should be 1 as ikeid is unique 'phase2count' => count($phase_ids[1]), ]; } return ['status' => 'failed']; } /** * toggle if phase 1 is enabled */ public function togglePhase1Action($ikeid, $enabled = null) { if ($this->request->isPost()) { Config::getInstance()->lock(); $config = Config::getInstance()->object(); if (!empty($config->ipsec->phase1)) { $idx = 0; foreach ($config->ipsec->phase1 as $p1) { if (intval((string)$p1->ikeid) == intval($ikeid)) { if ($enabled == "0" || $enabled == "1") { $new_status = $enabled == "1" ? "0" : "1"; } else { $new_status = $config->ipsec->phase1[$idx]->disabled == "1" ? "0" : "1"; } if ($new_status == "1") { $config->ipsec->phase1[$idx]->disabled = $new_status; } elseif (isset($config->ipsec->phase1[$idx]->disabled)) { unset($config->ipsec->phase1[$idx]->disabled); } Config::getInstance()->save(); @touch("/tmp/ipsec.dirty"); return ['status' => 'ok', 'disabled' => $new_status]; } $idx++; } } return ['status' => 'not_found']; } return ['status' => 'failed']; } /** * delete phase 2 entry */ public function delPhase2Action($seqid) { if ($this->request->isPost()) { Config::getInstance()->lock(); $config = Config::getInstance()->object(); if ((string)intval($seqid) == $seqid && isset($config->ipsec->phase2[intval($seqid)])) { unset($config->ipsec->phase2[intval($seqid)]); Config::getInstance()->save(); @touch("/tmp/ipsec.dirty"); return ['status' => 'ok']; } return ['status' => 'not_found']; } return ['status' => 'failed']; } /** * toggle if phase 2 is enabled */ public function togglePhase2Action($seqid, $enabled = null) { if ($this->request->isPost()) { Config::getInstance()->lock(); $config = Config::getInstance()->object(); if ((string)intval($seqid) == $seqid && isset($config->ipsec->phase2[intval($seqid)])) { if ($enabled == "0" || $enabled == "1") { $new_status = $enabled == "1" ? "0" : "1"; } else { $new_status = $config->ipsec->phase2[intval($seqid)]->disabled == "1" ? "0" : "1"; } if ($new_status == "1") { $config->ipsec->phase2[intval($seqid)]->disabled = $new_status; } elseif (isset($config->ipsec->phase2[intval($seqid)]->disabled)) { unset($config->ipsec->phase2[intval($seqid)]->disabled); } Config::getInstance()->save(); @touch("/tmp/ipsec.dirty"); return ['status' => 'ok', 'disabled' => $new_status]; } return ['status' => 'not_found']; } return ['status' => 'failed']; } /** * toggle if IPsec is enabled */ public function toggleAction($enabled = null) { if ($this->request->isPost()) { Config::getInstance()->lock(); $config = Config::getInstance()->object(); if ($enabled == "0" || $enabled == "1") { $new_status = $enabled == "1"; } else { $new_status = !isset($config->ipsec->enable); } if ($new_status) { $config->ipsec->enable = true; } elseif (isset($config->ipsec->enable)) { unset($config->ipsec->enable); } Config::getInstance()->save(); @touch("/tmp/ipsec.dirty"); return ['status' => 'ok']; } return ['status' => 'failed']; } }