diff --git a/LICENSE b/LICENSE index 8f1fb9c80..5ac4735ef 100644 --- a/LICENSE +++ b/LICENSE @@ -20,7 +20,7 @@ Copyright (c) 2020 devNan0 Copyright (c) 2023 Dmitry Shinkaruk Copyright (c) 2024 DollarSign23 Copyright (c) 2006 Eric Friesen -Copyright (c) 2008-2010 Ermal Luçi +Copyright (c) 2008-2014 Ermal Luçi Copyright (c) 2016-2019 EURO-LOG AG Copyright (c) 2017-2020 Fabian Franz Copyright (c) 2019 Felix Matouschek @@ -42,7 +42,7 @@ Copyright (c) 2024 laraveluser Copyright (c) 2023 Liam Steckler Copyright (c) 2020-2021 Manuel Faux Copyright (c) 2021 Manuel Hofmann -Copyright (c) 2003-2004 Manuel Kasper +Copyright (c) 2003-2005 Manuel Kasper Copyright (c) 2023 Marc Bartelt Copyright (c) 2021 Marcel Koepfli Copyright (c) 2021 Markus Peter @@ -63,6 +63,7 @@ Copyright (c) 2022 Nikolaj Brinch Jørgensen Copyright (c) 2021 Nim G Copyright (c) 2023 Oliver Hartl Copyright (c) 2024 Olly Baker +Copyright (c) 2019 Pascal Mathis Copyright (c) 2025 Ralph Moser, PJ Monitoring GmbH Copyright (c) 2024 realizelol Copyright (c) 2022 Robbert Rijkse diff --git a/security/strongswan-legacy/Makefile b/security/strongswan-legacy/Makefile index fceb923cf..2f2a02c73 100644 --- a/security/strongswan-legacy/Makefile +++ b/security/strongswan-legacy/Makefile @@ -1,5 +1,5 @@ PLUGIN_NAME= strongswan-legacy -PLUGIN_VERSION= 0.1 +PLUGIN_VERSION= 1.0 PLUGIN_COMMENT= IPsec legacy support PLUGIN_DEPENDS= # strongswan PLUGIN_MAINTAINER= ad@opnsense.org diff --git a/security/strongswan-legacy/src/opnsense/mvc/app/models/OPNsense/IPsecLegacy/ACL/ACL.xml b/security/strongswan-legacy/src/opnsense/mvc/app/models/OPNsense/IPsecLegacy/ACL/ACL.xml new file mode 100644 index 000000000..7441c7c6c --- /dev/null +++ b/security/strongswan-legacy/src/opnsense/mvc/app/models/OPNsense/IPsecLegacy/ACL/ACL.xml @@ -0,0 +1,28 @@ + + + VPN: IPsec: Tunnels [legacy] + + ui/ipsec/tunnels + api/ipsec/tunnel/* + api/ipsec/legacy_subsystem/* + + + + VPN: IPsec: Edit Phase 1 + + vpn_ipsec_phase1.php* + + + + VPN: IPsec: Edit Phase 2 + + vpn_ipsec_phase2.php* + + + + VPN: IPsec: Mobile [legacy] + + vpn_ipsec_mobile.php* + + + diff --git a/security/strongswan-legacy/src/opnsense/mvc/app/models/OPNsense/IPsecLegacy/Menu/Menu.xml b/security/strongswan-legacy/src/opnsense/mvc/app/models/OPNsense/IPsecLegacy/Menu/Menu.xml new file mode 100644 index 000000000..6a33b6cf6 --- /dev/null +++ b/security/strongswan-legacy/src/opnsense/mvc/app/models/OPNsense/IPsecLegacy/Menu/Menu.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/security/strongswan-legacy/src/www/vpn_ipsec_mobile.php b/security/strongswan-legacy/src/www/vpn_ipsec_mobile.php new file mode 100644 index 000000000..23d00589a --- /dev/null +++ b/security/strongswan-legacy/src/www/vpn_ipsec_mobile.php @@ -0,0 +1,310 @@ + + * 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. + */ + +require_once("guiconfig.inc"); +require_once("interfaces.inc"); +require_once("filter.inc"); +require_once("system.inc"); +require_once("plugins.inc.d/ipsec.inc"); + +config_read_array('ipsec', 'client'); +config_read_array('ipsec', 'phase1'); + +// define formfields +$form_fields = "pool_address,pool_netbits,pool_address_v6,pool_netbits_v6"; + +if ($_SERVER['REQUEST_METHOD'] === 'GET') { + // pass savemessage + if (isset($_GET['savemsg'])) { + $savemsg = htmlspecialchars($_GET['savemsg']); + } + $pconfig = array(); + // defaults + $pconfig['pool_netbits'] = 24; + $pconfig['pool_netbits_v6'] = 64; + + // copy / initialize $pconfig attributes + foreach (explode(",", $form_fields) as $fieldname) { + $fieldname = trim($fieldname); + if (isset($config['ipsec']['client'][$fieldname])) { + $pconfig[$fieldname] = $config['ipsec']['client'][$fieldname]; + } elseif (!isset($pconfig[$fieldname])) { + // initialize element + $pconfig[$fieldname] = null; + } + } + if (isset($config['ipsec']['client']['enable'])) { + $pconfig['enable'] = true; + } + +} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { + $input_errors = array(); + $pconfig = $_POST; + if (isset($_POST['create'])) { + // create new phase1 entry + header(url_safe('Location: /vpn_ipsec_phase1.php?mobile=true')); + exit; + } elseif (isset($_POST['apply'])) { + // apply changes + ipsec_configure_do(); + $savemsg = get_std_save_message(true); + clear_subsystem_dirty('ipsec'); + header(url_safe('Location: /vpn_ipsec_mobile.php?savemsg=%s', array($savemsg))); + exit; + } elseif (isset($_POST['submit'])) { + // save form changes + if (!empty($pconfig['pool_address']) && !is_ipaddr($pconfig['pool_address'])) { + $input_errors[] = gettext("A valid IPv4 address for 'Virtual IPv4 Address Pool Network' must be specified."); + } + + if (!empty($pconfig['pool_address_v6']) && !is_ipaddr($pconfig['pool_address_v6'])) { + $input_errors[] = gettext("A valid IPv6 address for 'Virtual IPv6 Address Pool Network' must be specified."); + } + + + if (count($input_errors) == 0) { + $client = array(); + $copy_fields = "pool_address,pool_netbits,pool_address_v6,pool_netbits_v6"; + foreach (explode(",", $copy_fields) as $fieldname) { + $fieldname = trim($fieldname); + if (!empty($pconfig[$fieldname])) { + $client[$fieldname] = $pconfig[$fieldname]; + } + } + if (!empty($pconfig['enable'])) { + $client['enable'] = true; + } + + $config['ipsec']['client'] = $client; + + write_config(); + mark_subsystem_dirty('ipsec'); + header(url_safe('Location: /vpn_ipsec_mobile.php')); + exit; + } + } + + // initialize missing post attributes + foreach (explode(",", $form_fields) as $fieldname) { + $fieldname = trim($fieldname); + if (!isset($pconfig[$fieldname])) { + $pconfig[$fieldname] = null; + } + } +} + +legacy_html_escape_form_data($pconfig); + +$service_hook = 'strongswan'; + +include("head.inc"); + +?> + + + + + + + +
+
+
+" . gettext("You must apply the changes in order for them to take effect.")); +} +$ph1found = false; +$legacy_radius_configured = false; +foreach ($config['ipsec']['phase1'] as $ph1ent) { + if (!isset($ph1ent['disabled']) && isset($ph1ent['mobile'])) { + $ph1found = true; + if (($ph1ent['authentication_method'] ?? '') == 'eap-radius') { + $legacy_radius_configured = true; + } + } +} + +function print_legacy_box($msg, $name, $value) +{ + $savebutton = "
"; + $savebutton .= ""; + if (!empty($_POST['if'])) { + $savebutton .= ""; + } + $savebutton .= '
'; + + echo << + +
+ +EOFnp; +} + +if (!empty($pconfig['enable']) && !$ph1found && !(new OPNsense\IPsec\Swanctl())->isEnabled()) { + print_legacy_box(gettext("Support for IPsec Mobile clients is enabled but a Phase1 definition was not found") . ".
" . gettext("When using (legacy) tunnels, please click Create to define one."), "create", gettext("Create Phase1")); +} +if (isset($input_errors) && count($input_errors) > 0) { + print_input_errors($input_errors); +} +?> +
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ + +
+ /> + + +
+ onclick="pool_change()" /> + +
+ + +
+
+ onclick="pool_v6_change()" /> + +
+ + +
+
+
+
+
+
+ + + + + +
  + +
+
+
+
+ + + + + + diff --git a/security/strongswan-legacy/src/www/vpn_ipsec_phase1.php b/security/strongswan-legacy/src/www/vpn_ipsec_phase1.php new file mode 100644 index 000000000..f2548491c --- /dev/null +++ b/security/strongswan-legacy/src/www/vpn_ipsec_phase1.php @@ -0,0 +1,1351 @@ + + * Copyright (C) 2014-2015 Deciso B.V. + * Copyright (C) 2008 Shrew Soft Inc. + * Copyright (C) 2003-2005 Manuel Kasper + * Copyright (C) 2014 Ermal Luçi + * 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. + */ + +require_once("guiconfig.inc"); +require_once("system.inc"); +require_once("filter.inc"); +require_once("interfaces.inc"); +require_once("plugins.inc.d/ipsec.inc"); + +/* + * ikeid management functions + */ + +function ipsec_ikeid_used($ikeid) { + global $config; + + if (!empty($config['ipsec']['phase1'])) { + foreach ($config['ipsec']['phase1'] as $ph1ent) { + if( $ikeid == $ph1ent['ikeid'] ) { + return true; + } + } + } + return false; +} + +function ipsec_ikeid_next() { + $ikeid = 1; + while(ipsec_ikeid_used($ikeid)) { + $ikeid++; + } + + return $ikeid; +} + +function ipsec_keypairs() +{ + $mdl = new \OPNsense\IPsec\IPsec(); + $node = $mdl->getNodeByReference('keyPairs.keyPair'); + + return $node ? $node->getNodes() : []; +} + +config_read_array('ipsec', 'phase1'); +config_read_array('ipsec', 'phase2'); + +if ($_SERVER['REQUEST_METHOD'] === 'GET') { + // fetch data + if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + $p1index = $_GET['dup']; + } elseif (isset($_GET['p1index']) && is_numericint($_GET['p1index'])) { + $p1index = $_GET['p1index']; + } + $pconfig = array(); + + // generice defaults + $pconfig['interface'] = "wan"; + $pconfig['iketype'] = "ikev2"; + $phase1_fields = "mode,protocol,myid_type,myid_data,peerid_type,peerid_data + ,encryption-algorithm,lifetime,authentication_method,descr,nat_traversal,rightallowany,inactivity_timeout + ,interface,iketype,dpd_delay,dpd_maxfail,dpd_action,remote-gateway,pre-shared-key,certref,margintime,rekeyfuzz + ,caref,local-kpref,peer-kpref,reauth_enable,rekey_enable,auto,tunnel_isolation,authservers,mobike,keyingtries + ,closeaction,unique"; + if (isset($p1index) && isset($config['ipsec']['phase1'][$p1index])) { + // 1-on-1 copy + foreach (explode(",", $phase1_fields) as $fieldname) { + $fieldname = trim($fieldname); + if(isset($config['ipsec']['phase1'][$p1index][$fieldname])) { + $pconfig[$fieldname] = $config['ipsec']['phase1'][$p1index][$fieldname]; + } elseif (!isset($pconfig[$fieldname])) { + // initialize element + $pconfig[$fieldname] = null; + } + } + + // attributes with some kind of logic behind them... + if (!isset($_GET['dup']) || !is_numericint($_GET['dup'])) { + // don't copy the ikeid on dup + $pconfig['ikeid'] = $config['ipsec']['phase1'][$p1index]['ikeid']; + } + $pconfig['disabled'] = isset($config['ipsec']['phase1'][$p1index]['disabled']); + $pconfig['sha256_96'] = !empty($config['ipsec']['phase1'][$p1index]['sha256_96']); + $pconfig['installpolicy'] = empty($config['ipsec']['phase1'][$p1index]['noinstallpolicy']); // XXX: reversed + + foreach (array('authservers', 'dhgroup', 'hash-algorithm') as $fieldname) { + if (!empty($config['ipsec']['phase1'][$p1index][$fieldname])) { + $pconfig[$fieldname] = explode(',', $config['ipsec']['phase1'][$p1index][$fieldname]); + } else { + $pconfig[$fieldname] = array(); + } + } + + $pconfig['remotebits'] = null; + $pconfig['remotenet'] = null ; + if (isset($a_phase1[$p1index]['remote-subnet']) && strpos($config['ipsec']['phase1'][$p1index]['remote-subnet'],'/') !== false) { + list($pconfig['remotenet'],$pconfig['remotebits']) = explode("/", $config['ipsec']['phase1'][$p1index]['remote-subnet']); + } elseif (isset($config['ipsec']['phase1'][$p1index]['remote-subnet'])) { + $pconfig['remotenet'] = $config['ipsec']['phase1'][$p1index]['remote-subnet']; + } + + if (isset($config['ipsec']['phase1'][$p1index]['mobile'])) { + $pconfig['mobile'] = true; + } + } else { + /* defaults new */ + if (isset($config['interfaces']['lan'])) { + $pconfig['localnet'] = "lan"; + } + $pconfig['mode'] = "main"; + $pconfig['protocol'] = "inet"; + $pconfig['myid_type'] = "myaddress"; + $pconfig['peerid_type'] = "peeraddress"; + $pconfig['authentication_method'] = "pre_shared_key"; + $pconfig['encryption-algorithm'] = ["name" => "aes256gcm16"]; + $pconfig['hash-algorithm'] = ['sha256']; + $pconfig['dhgroup'] = array('14'); + $pconfig['nat_traversal'] = "on"; + $pconfig['installpolicy'] = true; + $pconfig['authservers'] = array(); + + /* mobile client */ + if (isset($_GET['mobile'])) { + $pconfig['mobile'] = true; + } + // init empty + foreach (explode(",", $phase1_fields) as $fieldname) { + $fieldname = trim($fieldname); + if (!isset($pconfig[$fieldname])) { + $pconfig[$fieldname] = null; + } + } + } +} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { + $a_phase1 = &config_read_array('ipsec', 'phase1'); + if (isset($_POST['p1index']) && is_numericint($_POST['p1index'])) { + $p1index = $_POST['p1index']; + } + $input_errors = array(); + $pconfig = $_POST; + $old_ph1ent = $a_phase1[$p1index]; + + // unset dpd on post + if (!isset($pconfig['dpd_enable'])) { + unset($pconfig['dpd_delay']); + unset($pconfig['dpd_maxfail']); + unset($pconfig['dpd_action']); + } + + /* My identity */ + if ($pconfig['myid_type'] == "myaddress") { + $pconfig['myid_data'] = ""; + } + /* Peer identity */ + if ($pconfig['myid_type'] == "peeraddress") { + $pconfig['peerid_data'] = ""; + } + + /* input validation */ + $method = $pconfig['authentication_method']; + + // Only require PSK here for normal PSK tunnels (not mobile) or xauth. + // For RSA methods, require the CA/Cert. + switch ($method) { + case "eap-tls": + case "psk_eap-tls": + case "eap-mschapv2": + case "rsa_eap-mschapv2": + case "eap-radius": + if (!in_array($pconfig['iketype'], array('ikev2', 'ike'))) { + $input_errors[] = sprintf(gettext("%s can only be used with IKEv2 type VPNs."), strtoupper($method)); + } + if ($method == 'eap-radius' && empty($pconfig['authservers'])) { + $input_errors[] = gettext("Please select radius servers to use."); + } + break; + case "pre_shared_key": + // If this is a mobile PSK tunnel the user PSKs go on + // the PSK tab, not here, so skip the check. + if ($pconfig['mobile']) { + break; + } + case "xauth_psk_server": + $reqdfields = explode(" ", "pre-shared-key"); + $reqdfieldsn = array(gettext("Pre-Shared Key")); + break; + case "hybrid_rsa_server": + $reqdfields = explode(' ', 'certref'); + $reqdfieldsn = array(gettext("Certificate")); + break; + case "xauth_rsa_server": + case "rsasig": + $reqdfields = explode(" ", "caref certref"); + $reqdfieldsn = array(gettext("Certificate Authority"),gettext("Certificate")); + break; + case "pubkey": + $reqdfields = explode(" ", "local-kpref peer-kpref"); + $reqdfieldsn = array(gettext("Local Key Pair"),gettext("Peer Key Pair")); + break; + } + + if (empty($pconfig['mobile'])) { + $reqdfields[] = "remote-gateway"; + $reqdfieldsn[] = gettext("Remote gateway"); + } + + do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors); + + if (!empty($pconfig['inactivity_timeout']) && !is_numericint($pconfig['inactivity_timeout'])) { + $input_errors[] = gettext("The inactivity timeout must be an integer."); + } + if (!empty($pconfig['keyingtries']) && !is_numericint($pconfig['keyingtries']) && $pconfig['keyingtries'] != "-1") { + $input_errors[] = gettext("The keyingtries must be an integer."); + } + + if ((!empty($pconfig['lifetime']) && !is_numeric($pconfig['lifetime']))) { + $input_errors[] = gettext("The P1 lifetime must be an integer."); + } + if (!empty($pconfig['margintime'])) { + if (!is_numericint($pconfig['margintime'])) { + $input_errors[] = gettext("The margintime must be an integer."); + } else { + $rekeyfuzz = empty($pconfig['rekeyfuzz']) || !is_numeric($pconfig['rekeyfuzz']) ? 100 : $pconfig['rekeyfuzz']; + if (((int)$pconfig['margintime'] * 2) * ($rekeyfuzz / 100.0) > (int)$pconfig['lifetime']) { + $input_errors[] = gettext("The value margin... + margin... * rekeyfuzz must not exceed the original lifetime limit."); + } + } + } + if (!empty($pconfig['rekeyfuzz']) && !is_numericint($pconfig['rekeyfuzz'])) { + $input_errors[] = gettext("Rekeyfuzz must be an integer."); + } + + if (!empty($pconfig['remote-gateway'])) { + if (!is_ipaddr($pconfig['remote-gateway']) && !is_domain($pconfig['remote-gateway'])) { + $input_errors[] = gettext("A valid remote gateway address or host name must be specified."); + } elseif (is_ipaddrv4($pconfig['remote-gateway']) && ($pconfig['protocol'] != "inet")) { + $input_errors[] = gettext("A valid remote gateway IPv4 address must be specified or you need to change protocol to IPv6"); + } elseif (is_ipaddrv6($pconfig['remote-gateway']) && ($pconfig['protocol'] != "inet6")) { + $input_errors[] = gettext("A valid remote gateway IPv6 address must be specified or you need to change protocol to IPv4"); + } + } + + if (!empty($pconfig['remote-gateway']) && is_ipaddr($pconfig['remote-gateway']) && !isset($pconfig['disabled']) && + (empty($pconfig['iketype']) || $pconfig['iketype'] == "ikev1")) { + $t = 0; + foreach ($a_phase1 as $ph1tmp) { + if ($p1index != $t) { + if (isset($ph1tmp['remote-gateway']) && $ph1tmp['remote-gateway'] == $pconfig['remote-gateway'] && !isset($ph1tmp['disabled'])) { + $input_errors[] = sprintf(gettext('The remote gateway "%s" is already used by phase1 "%s".'), $pconfig['remote-gateway'], $ph1tmp['descr']); + } + } + $t++; + } + } + + if ($pconfig['interface'] == 'any' && $pconfig['myid_type'] == "myaddress") { + $input_errors[] = gettext("Please select an identifier (My Identifier) other then 'any' when selecting 'Any' interface"); + } elseif ($pconfig['myid_type'] == "address" && $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter an address for 'My Identifier'"); + } elseif ($pconfig['myid_type'] == "keyid tag" && $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter a keyid tag for 'My Identifier'"); + } elseif ($pconfig['myid_type'] == "fqdn" && $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter a fully qualified domain name for 'My Identifier'"); + } elseif ($pconfig['myid_type'] == "user_fqdn" && $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter a user and fully qualified domain name for 'My Identifier'"); + } elseif ($pconfig['myid_type'] == "dyn_dns" && $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter a dynamic domain name for 'My Identifier'"); + } elseif ((($pconfig['myid_type'] == "address") && !is_ipaddr($pconfig['myid_data']))) { + $input_errors[] = gettext("A valid IP address for 'My identifier' must be specified."); + } elseif ((($pconfig['myid_type'] == "fqdn") && !is_domain($pconfig['myid_data']))) { + $input_errors[] = gettext("A valid domain name for 'My identifier' must be specified."); + } elseif ($pconfig['myid_type'] == "fqdn" && !is_domain($pconfig['myid_data'])) { + $input_errors[] = gettext("A valid FQDN for 'My identifier' must be specified."); + } elseif ($pconfig['myid_type'] == "user_fqdn") { + $user_fqdn = explode("@", $pconfig['myid_data']); + if (is_domain($user_fqdn[1]) == false) { + $input_errors[] = gettext("A valid User FQDN in the form of user@my.domain.com for 'My identifier' must be specified."); + } + } elseif ($pconfig['myid_type'] == "dyn_dns") { + if (is_domain($pconfig['myid_data']) == false) { + $input_errors[] = gettext("A valid Dynamic DNS address for 'My identifier' must be specified."); + } + } + + // Only enforce peer ID if we are not dealing with a pure-psk mobile config. + if (!(($pconfig['authentication_method'] == "pre_shared_key") && !empty($pconfig['mobile']))) { + if ($pconfig['peerid_type'] == "address" and $pconfig['peerid_data'] == "") { + $input_errors[] = gettext("Please enter an address for 'Peer Identifier'"); + } + if ($pconfig['peerid_type'] == "keyid tag" and $pconfig['peerid_data'] == "") { + $input_errors[] = gettext("Please enter a keyid tag for 'Peer Identifier'"); + } + if ($pconfig['peerid_type'] == "fqdn" and $pconfig['peerid_data'] == "") { + $input_errors[] = gettext("Please enter a fully qualified domain name for 'Peer Identifier'"); + } + if ($pconfig['peerid_type'] == "user_fqdn" and $pconfig['peerid_data'] == "") { + $input_errors[] = gettext("Please enter a user and fully qualified domain name for 'Peer Identifier'"); + } + if ((($pconfig['peerid_type'] == "address") && !is_ipaddr($pconfig['peerid_data']))) { + $input_errors[] = gettext("A valid IP address for 'Peer identifier' must be specified."); + } + if ((($pconfig['peerid_type'] == "fqdn") && !is_domain($pconfig['peerid_data']))) { + $input_errors[] = gettext("A valid domain name for 'Peer identifier' must be specified."); + } + if ($pconfig['peerid_type'] == "fqdn") { + if (is_domain($pconfig['peerid_data']) == false) { + $input_errors[] = gettext("A valid FQDN for 'Peer identifier' must be specified."); + } + } + if ($pconfig['peerid_type'] == "user_fqdn") { + $user_fqdn = explode("@", $pconfig['peerid_data']); + if (is_domain($user_fqdn[1]) == false) { + $input_errors[] = gettext("A valid User FQDN in the form of user@my.domain.com for 'Peer identifier' must be specified."); + } + } + } + + if (!empty($pconfig['closeaction']) && !in_array($pconfig['closeaction'], ['clear', 'hold', 'restart'])) { + $input_errors[] = gettext('Invalid argument for close action.'); + } + + if (!empty($pconfig['unique']) && !in_array($pconfig['unique'], ['no', 'replace', 'never', 'keep'])) { + $input_errors[] = gettext('Invalid argument for unique.'); + } + + if (!empty($pconfig['dpd_enable'])) { + if (!is_numeric($pconfig['dpd_delay'])) { + $input_errors[] = gettext("A numeric value must be specified for DPD delay."); + } + if (!is_numeric($pconfig['dpd_maxfail'])) { + $input_errors[] = gettext("A numeric value must be specified for DPD retries."); + } + if (!empty($pconfig['dpd_action']) && !in_array($pconfig['dpd_action'], array("restart", "clear"))) { + $input_errors[] = gettext('Invalid argument for DPD action.'); + } + } + + if (!empty($pconfig['iketype']) && !in_array($pconfig['iketype'], array("ike", "ikev1", "ikev2"))) { + $input_errors[] = gettext('Invalid argument for key exchange protocol version.'); + } + + /* build our encryption algorithms array */ + if (!isset($pconfig['encryption-algorithm']) || !is_array($pconfig['encryption-algorithm'])) { + $pconfig['encryption-algorithm'] = array(); + } + $pconfig['encryption-algorithm']['name'] = $pconfig['ealgo']; + if (!empty($pconfig['ealgo_keylen'])) { + $pconfig['encryption-algorithm']['keylen'] = $pconfig['ealgo_keylen']; + } + + if (empty($pconfig['hash-algorithm'])) { + $input_errors[] = gettext("At least one hashing algorithm needs to be selected."); + $pconfig['hash-algorithm'] = array(); + } + + if (empty($pconfig['dhgroup'])) { + $pconfig['dhgroup'] = array(); + } + + foreach (ipsec_p1_ealgos() as $algo => $algodata) { + if (!empty($pconfig['iketype']) && !empty($pconfig['encryption-algorithm']['name']) && !empty($algodata['iketype']) + && $pconfig['iketype'] != $algodata['iketype'] && $pconfig['encryption-algorithm']['name'] == $algo) { + $input_errors[] = sprintf(gettext("%s can only be used with IKEv2 type VPNs."), $algodata['name']); + } + } + + if (!empty($pconfig['ikeid']) && !empty($pconfig['installpolicy'])) { + foreach ($config['ipsec']['phase2'] as $phase2ent) { + if ($phase2ent['ikeid'] == $pconfig['ikeid'] && $phase2ent['mode'] == 'route-based') { + $input_errors[] = gettext( + "Install policy on phase1 is not a valid option when using Route-based phase 2 entries." + ); + break; + } + } + } + + if (count($input_errors) == 0) { + $copy_fields = "ikeid,iketype,interface,mode,protocol,myid_type,myid_data + ,peerid_type,peerid_data,encryption-algorithm,margintime,rekeyfuzz,inactivity_timeout,keyingtries + ,lifetime,pre-shared-key,certref,caref,authentication_method,descr,local-kpref,peer-kpref + ,nat_traversal,auto,mobike,closeaction,unique"; + + foreach (explode(",",$copy_fields) as $fieldname) { + $fieldname = trim($fieldname); + if(!empty($pconfig[$fieldname])) { + $ph1ent[$fieldname] = $pconfig[$fieldname]; + } + } + + foreach (array('authservers', 'dhgroup', 'hash-algorithm') as $fieldname) { + if (!empty($pconfig[$fieldname])) { + $ph1ent[$fieldname] = implode(',', $pconfig[$fieldname]); + } + } + + $ph1ent['disabled'] = !empty($pconfig['disabled']); + $ph1ent['sha256_96'] = !empty($pconfig['sha256_96']); + $ph1ent['noinstallpolicy'] = empty($pconfig['installpolicy']); // XXX: reversed + $ph1ent['private-key'] =isset($pconfig['privatekey']) ? base64_encode($pconfig['privatekey']) : null; + if (!empty($pconfig['mobile'])) { + $ph1ent['mobile'] = true; + } else { + $ph1ent['remote-gateway'] = $pconfig['remote-gateway']; + } + if (isset($pconfig['reauth_enable'])) { + $ph1ent['reauth_enable'] = true; + } + if (isset($pconfig['rekey_enable'])) { + $ph1ent['rekey_enable'] = true; + } + + if (isset($pconfig['tunnel_isolation'])) { + $ph1ent['tunnel_isolation'] = true; + } + + if (isset($pconfig['rightallowany'])) { + $ph1ent['rightallowany'] = true; + } + + if (isset($pconfig['dpd_enable'])) { + $ph1ent['dpd_delay'] = $pconfig['dpd_delay']; + $ph1ent['dpd_maxfail'] = $pconfig['dpd_maxfail']; + $ph1ent['dpd_action'] = $pconfig['dpd_action']; + } + + /* generate unique phase1 ikeid */ + if ($ph1ent['ikeid'] == 0) { + $ph1ent['ikeid'] = ipsec_ikeid_next(); + } + + if (isset($p1index) && isset($a_phase1[$p1index])) { + $a_phase1[$p1index] = $ph1ent; + } else { + if (!empty($pconfig['clone_phase2']) && !empty($a_phase1[$_GET['dup']]) + && !empty($config['ipsec']['phase2'])) { + // clone phase 2 entries in disabled state if requested. + $prev_ike_id = $a_phase1[$_GET['dup']]['ikeid']; + foreach ($config['ipsec']['phase2'] as $phase2ent) { + if ($phase2ent['ikeid'] == $prev_ike_id) { + $new_phase2 = $phase2ent; + $new_phase2['disabled'] = true; + $new_phase2['uniqid'] = uniqid(); + $new_phase2['ikeid'] = $ph1ent['ikeid']; + unset($new_phase2['reqid']); + $config['ipsec']['phase2'][] = $new_phase2; + } + } + } + $a_phase1[] = $ph1ent; + } + + write_config(); + mark_subsystem_dirty('ipsec'); + + header(url_safe('Location: /ui/ipsec/tunnels')); + exit; + } +} + +$service_hook = 'strongswan'; + +legacy_html_escape_form_data($pconfig); +include("head.inc"); +?> + + + + + +
+
+
+ 0) { + print_input_errors($input_errors); + } +?> + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ /> + +
+ /> + + +
+ + + +
+ + + +
+ + +
+ + +
+ + +
+ /> + + +
+ + +
 
+ + +
+ + +
+ +
+ +
+
+ + + + + +
+ " /> + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + + +
+ + +
+ + +
+ /> + +
+ /> + +
+ /> + +
+ /> + +
+ /> + +
+ + +
+ /> + +
+ + +
+ + +
+ /> + +
+
+ + + +
+ + + +
+ + + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
  + + + + + + + + +
+
+
+
+
+
+
+
+ + + * Copyright (C) 2003-2005 Manuel Kasper + * 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. + */ + +require_once("guiconfig.inc"); +require_once("interfaces.inc"); +require_once("plugins.inc.d/ipsec.inc"); + +/** + * combine ealgos and keylen_* tags + */ +function pconfig_to_ealgos($pconfig) +{ + $ealgos = []; + if (isset($pconfig['ealgos'])) { + foreach (ipsec_p2_ealgos() as $algo_name => $algo_data) { + if (in_array($algo_name, $pconfig['ealgos'])) { + $ealgos[] = ['name' => $algo_name]; + } + } + } + return $ealgos; +} + +function ealgos_to_pconfig(& $ealgos, & $pconfig) +{ + $p2_ealgos = ipsec_p2_ealgos(); + $pconfig['ealgos'] = []; + foreach ($ealgos as $cnf_algo_data) { + foreach ($p2_ealgos as $algo_name => $algo_data) { + if ($algo_name == $cnf_algo_data['name']) { + $pconfig['ealgos'][] = $algo_name; + } elseif ($algo_data['name'] == $cnf_algo_data['name']) { + // XXX: extract and convert legacy encryption-algorithm-option setting + if ($cnf_algo_data['keylen'] == $algo_data['keylen'] || $cnf_algo_data['keylen'] == "auto") { + $pconfig['ealgos'][] = $algo_name; + } + } + } + } + + return $ealgos; +} + +/** + * convert id_address, id_netbits, id_type + * to type/address/netbits structure + */ +function pconfig_to_idinfo($prefix, $pconfig) +{ + $type = isset($pconfig[$prefix."id_type"]) ? $pconfig[$prefix."id_type"] : null; + $address = isset($pconfig[$prefix."id_address"]) ? $pconfig[$prefix."id_address"] : null; + $netbits = isset($pconfig[$prefix."id_netbits"]) ? $pconfig[$prefix."id_netbits"] : null; + + switch ($type) { + case "address": + return array('type' => $type, 'address' => $address); + case "network": + return array('type' => $type, 'address' => $address, 'netbits' => $netbits); + default: + return array('type' => $type ); + } +} + +/** + * reverse pconfig_to_idinfo from $idinfo array to $pconfig + */ +function idinfo_to_pconfig($prefix, $idinfo, & $pconfig) +{ + switch ($idinfo['type']) { + case "address": + $pconfig[$prefix."id_type"] = $idinfo['type']; + $pconfig[$prefix."id_address"] = $idinfo['address']; + break; + case "network": + $pconfig[$prefix."id_type"] = $idinfo['type']; + $pconfig[$prefix."id_address"] = $idinfo['address']; + $pconfig[$prefix."id_netbits"] = $idinfo['netbits']; + break; + default: + $pconfig[$prefix."id_type"] = $idinfo['type']; + break; + } +} + +/** + * search phase 2 entries for record with uniqid + */ +function getIndexByUniqueId($uniqid) +{ + global $config; + $p2index = null; + if ($uniqid != null) { + foreach ($config['ipsec']['phase2'] as $idx => $ph2) { + if ($ph2['uniqid'] == $uniqid) { + $p2index = $idx; + break; + } + } + } + return $p2index; +} + +config_read_array('ipsec', 'client'); +config_read_array('ipsec', 'phase2'); + +if ($_SERVER['REQUEST_METHOD'] === 'GET') { + // lookup p2index + if (!empty($_GET['dup'])) { + $p2index = getIndexByUniqueId($_GET['dup']); + } elseif (!empty($_GET['p2index'])) { + $p2index = getIndexByUniqueId($_GET['p2index']); + } else { + $p2index = null; + } + // initialize form data + $pconfig = array(); + + $phase2_fields = "ikeid,mode,descr,uniqid,proto,hash-algorithm-option,pfsgroup,lifetime,pinghost,protocol,spd,"; + $phase2_fields .= "tunnel_local,tunnel_remote"; + if ($p2index !== null) { + // 1-on-1 copy + foreach (explode(",", $phase2_fields) as $fieldname) { + $fieldname = trim($fieldname); + if (isset($config['ipsec']['phase2'][$p2index][$fieldname])) { + $pconfig[$fieldname] = $config['ipsec']['phase2'][$p2index][$fieldname]; + } elseif (!isset($pconfig[$fieldname])) { + // initialize element + $pconfig[$fieldname] = null; + } + } + // fields with some kind of logic + $pconfig['disabled'] = isset($config['ipsec']['phase2'][$p2index]['disabled']); + + idinfo_to_pconfig("local", $config['ipsec']['phase2'][$p2index]['localid'], $pconfig); + idinfo_to_pconfig("remote", $config['ipsec']['phase2'][$p2index]['remoteid'], $pconfig); + if (!empty($config['ipsec']['phase2'][$p2index]['encryption-algorithm-option'])) { + ealgos_to_pconfig($config['ipsec']['phase2'][$p2index]['encryption-algorithm-option'], $pconfig); + } else { + $pconfig['ealgos'] = []; + } + + if (!empty($_GET['dup'])) { + $pconfig['uniqid'] = uniqid(); + } + } else { + if (isset($_GET['ikeid'])) { + $pconfig['ikeid'] = $_GET['ikeid']; + } + /* defaults */ + $pconfig['localid_type'] = "lan"; + $pconfig['remoteid_type'] = "network"; + $pconfig['protocol'] = "esp"; + $pconfig['ealgos'] = ['aes256gcm16']; + $pconfig['hash-algorithm-option'] = ['hmac_sha256']; + $pconfig['pfsgroup'] = "0"; + $pconfig['uniqid'] = uniqid(); + + // init empty + foreach (explode(",", $phase2_fields) as $fieldname) { + $fieldname = trim($fieldname); + if (!isset($pconfig[$fieldname])) { + $pconfig[$fieldname] = null; + } + } + } + /* mobile client */ + foreach ($config['ipsec']['phase1'] as $phase1ent) { + if ($phase1ent['ikeid'] == $pconfig['ikeid'] && isset($phase1ent['mobile'])) { + $pconfig['mobile'] = true; + break; + } + } +} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!empty($_POST['uniqid'])) { + $p2index = getIndexByUniqueId($_POST['uniqid']); + } else { + $p2index = null; + } + $input_errors = array(); + $pconfig = $_POST; + + /* input validation */ + if (!isset($_POST['ikeid'])) { + $input_errors[] = gettext("A valid ikeid must be specified."); + } + $reqdfields = explode(" ", "localid_type uniqid"); + $reqdfieldsn = array(gettext("Local network type"), gettext("Unique Identifier")); + if (!isset($pconfig['mobile'])) { + $reqdfields[] = "remoteid_type"; + $reqdfieldsn[] = gettext("Remote network type"); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($pconfig['mode'] == 'tunnel') || ($pconfig['mode'] == 'tunnel6')) { + switch ($pconfig['localid_type']) { + case 'network': + if (($pconfig['localid_netbits'] != 0 && !$pconfig['localid_netbits']) || !is_numeric($pconfig['localid_netbits'])) { + $input_errors[] = gettext('A valid local network bit count must be specified.'); + } + /* FALLTHROUGH */ + case 'address': + if (!$pconfig['localid_address'] || !is_ipaddr($pconfig['localid_address'])) { + $input_errors[] = gettext('A valid local network IP address must be specified.'); + } elseif (is_ipaddrv4($pconfig['localid_address']) && ($pconfig['mode'] != 'tunnel')) { + $input_errors[] = gettext('A valid local network IPv4 address must be specified or you need to change Mode to IPv6'); + } elseif (is_ipaddrv6($pconfig['localid_address']) && ($pconfig['mode'] != 'tunnel6')) { + $input_errors[] = gettext('A valid local network IPv6 address must be specified or you need to change Mode to IPv4'); + } + break; + default: + if ($pconfig['mode'] == 'tunnel') { + list (, $subnet) = interfaces_primary_address($pconfig['localid_type']); + if (!is_subnetv4($subnet)) { + $input_errors[] = sprintf( + gettext('Invalid local network: %s has no valid IPv4 network.'), + convert_friendly_interface_to_friendly_descr($pconfig['localid_type']) + ); + } + } elseif ($pconfig['mode'] == 'tunnel6') { + list (, $subnet) = interfaces_primary_address6($pconfig['localid_type']); + if (!is_subnetv6($subnet)) { + $input_errors[] = sprintf( + gettext('Invalid local network: %s has no valid IPv6 network.'), + convert_friendly_interface_to_friendly_descr($pconfig['localid_type']) + ); + } + } + break; + } + + switch ($pconfig['remoteid_type']) { + case "network": + if (($pconfig['remoteid_netbits'] != 0 && !$pconfig['remoteid_netbits']) || !is_numeric($pconfig['remoteid_netbits'])) { + $input_errors[] = gettext("A valid remote network bit count must be specified."); + } + // address rules also apply to network type (hence, no break) + case "address": + if (!$pconfig['remoteid_address'] || !is_ipaddr($pconfig['remoteid_address'])) { + $input_errors[] = gettext("A valid remote network IP address must be specified."); + } elseif (is_ipaddrv4($pconfig['remoteid_address']) && ($pconfig['mode'] != "tunnel")) { + $input_errors[] = gettext("A valid remote network IPv4 address must be specified or you need to change Mode to IPv6"); + } elseif (is_ipaddrv6($pconfig['remoteid_address']) && ($pconfig['mode'] != "tunnel6")) { + $input_errors[] = gettext("A valid remote network IPv6 address must be specified or you need to change Mode to IPv4"); + } + break; + } + } elseif ($pconfig['mode'] == 'route-based') { + // validate if both tunnel networks are using the correct address family + if (!is_ipaddr($pconfig['tunnel_local']) || !is_ipaddr($pconfig['tunnel_remote'])) { + if (!is_ipaddr($pconfig['tunnel_local'])) { + $input_errors[] = gettext('A valid local network IP address must be specified.'); + } + if (!is_ipaddr($pconfig['tunnel_remote'])) { + $input_errors[] = gettext("A valid remote network IP address must be specified."); + } + } elseif( + !(is_ipaddrv4($pconfig['tunnel_local']) && is_ipaddrv4($pconfig['tunnel_remote'])) && + !(is_ipaddrv6($pconfig['tunnel_local']) && is_ipaddrv6($pconfig['tunnel_remote'])) + ) { + $input_errors[] = gettext("A valid local network IP address must be specified."); + $input_errors[] = gettext("A valid remote network IP address must be specified."); + } + } + /* Validate enabled phase2's are not duplicates */ + if (isset($pconfig['mobile'])) { + /* User is adding phase 2 for mobile phase1 */ + foreach ($config['ipsec']['phase2'] as $key => $name) { + if (isset($name['mobile']) && $pconfig['ikeid'] == $name['ikeid'] && $name['uniqid'] != $pconfig['uniqid']) { + /* check duplicate localids only for mobile clients */ + $localid_data = ipsec_idinfo_to_cidr($name['localid'], false, $name['mode']); + $entered = array(); + $entered['type'] = $pconfig['localid_type']; + if (isset($pconfig['localid_address'])) { + $entered['address'] = $pconfig['localid_address']; + } + if (isset($pconfig['localid_netbits'])) { + $entered['netbits'] = $pconfig['localid_netbits']; + } + $entered_localid_data = ipsec_idinfo_to_cidr($entered, false, $pconfig['mode']); + if ($localid_data == $entered_localid_data) { + /* adding new p2 entry */ + $input_errors[] = gettext("Phase2 with this Local Network is already defined for mobile clients."); + break; + } + } + } + } else { + /* User is adding phase 2 for site-to-site phase1 */ + foreach ($config['ipsec']['phase2'] as $key => $name) { + if (!isset($name['mobile']) && $pconfig['mode'] != 'route-based' && + $pconfig['ikeid'] == $name['ikeid'] && $pconfig['uniqid'] != $name['uniqid']) { + /* check duplicate subnets only for given phase1 */ + $localid_data = ipsec_idinfo_to_cidr($name['localid'], false, $name['mode']); + $remoteid_data = ipsec_idinfo_to_cidr($name['remoteid'], false, $name['mode']); + $entered_local = array(); + $entered_local['type'] = $pconfig['localid_type']; + if (isset($pconfig['localid_address'])) { + $entered_local['address'] = $pconfig['localid_address']; + } + if (isset($pconfig['localid_netbits'])) { + $entered_local['netbits'] = $pconfig['localid_netbits']; + } + $entered_localid_data = ipsec_idinfo_to_cidr($entered_local, false, $pconfig['mode']); + $entered_remote = array(); + $entered_remote['type'] = $pconfig['remoteid_type']; + if (isset($pconfig['remoteid_address'])) { + $entered_remote['address'] = $pconfig['remoteid_address']; + } + if (isset($pconfig['remoteid_netbits'])) { + $entered_remote['netbits'] = $pconfig['remoteid_netbits']; + } + $entered_remoteid_data = ipsec_idinfo_to_cidr($entered_remote, false, $pconfig['mode']); + if ($localid_data == $entered_localid_data && $remoteid_data == $entered_remoteid_data) { + /* adding new p2 entry */ + $input_errors[] = gettext("Phase2 with this Local/Remote networks combination is already defined for this Phase1."); + break; + } + } + } + } + + if (!empty($pconfig['ikeid'])) { + foreach ($config['ipsec']['phase1'] as $phase1ent) { + if ($phase1ent['ikeid'] == $pconfig['ikeid'] && + $pconfig['mode'] == 'route-based' && + empty($phase1ent['noinstallpolicy']) + ) { + $input_errors[] = gettext( + "Install policy on phase1 is not a valid option when using Route-based phase 2 entries." + ); + break; + } + } + } + + /* For ESP protocol, handle encryption algorithms */ + if ($pconfig['protocol'] == "esp") { + $ealgos = pconfig_to_ealgos($pconfig); + + if (!count($ealgos)) { + $input_errors[] = gettext("At least one encryption algorithm must be selected."); + } else { + if (empty($pconfig['hash-algorithm-option'])) { + foreach ($ealgos as $ealgo) { + if (!strpos($ealgo['name'], "gcm")) { + $input_errors[] = gettext("At least one hashing algorithm needs to be selected."); + break; + } + } + $pconfig['hash-algorithm-option'] = array(); + } + } + } + if ((!empty($_POST['lifetime']) && !is_numeric($_POST['lifetime']))) { + $input_errors[] = gettext("The P2 lifetime must be an integer."); + } + + if (!empty($pconfig['spd'])) { + foreach (explode(',', $pconfig['spd']) as $spd_entry) { + if (($pconfig['mode'] == "tunnel" && !is_subnetv4(trim($spd_entry))) || + ($pconfig['mode'] == "tunnel6" && !is_subnetv6(trim($spd_entry)))) { + $input_errors[] = sprintf(gettext('SPD "%s" is not a valid network, it should match the tunnel type (IPv4/IPv6).'), $spd_entry) ; + } + } + } + + if (count($input_errors) == 0) { + $ph2ent = array(); + $copy_fields = "ikeid,uniqid,mode,pfsgroup,lifetime,pinghost,descr,protocol,spd"; + + // 1-on-1 copy + foreach (explode(",", $copy_fields) as $fieldname) { + $fieldname = trim($fieldname); + if (!empty($pconfig[$fieldname])) { + $ph2ent[$fieldname] = $pconfig[$fieldname]; + } + } + + // fields with some logic in them + $ph2ent['disabled'] = $pconfig['disabled'] ? true : false; + if (($ph2ent['mode'] == "tunnel") || ($ph2ent['mode'] == "tunnel6")) { + $ph2ent['localid'] = pconfig_to_idinfo("local", $pconfig); + $ph2ent['remoteid'] = pconfig_to_idinfo("remote", $pconfig); + } elseif ($ph2ent['mode'] == 'route-based') { + $ph2ent['tunnel_local'] = $pconfig['tunnel_local']; + $ph2ent['tunnel_remote'] = $pconfig['tunnel_remote']; + } + + $ph2ent['encryption-algorithm-option'] = pconfig_to_ealgos($pconfig); + + if (!empty($pconfig['hash-algorithm-option'])) { + $ph2ent['hash-algorithm-option'] = $pconfig['hash-algorithm-option']; + } else { + unset($ph2ent['hash-algorithm-option']); + } + + // attach or generate reqid + if ($p2index !== null && !empty($config['ipsec']['phase2'][$p2index]['reqid'])) { + $ph2ent['reqid'] = $config['ipsec']['phase2'][$p2index]['reqid']; + } else { + $reqids = []; + foreach ($config['ipsec']['phase2'] as $tmp) { + if (!empty($tmp['reqid'])) { + $reqids[] = $tmp['reqid']; + } + } + for ($i=1; $i < 65535; $i++) { + if (!in_array($i, $reqids)) { + $ph2ent['reqid'] = $i; + break; + } + } + } + // save to config + if ($p2index !== null) { + $config['ipsec']['phase2'][$p2index] = $ph2ent; + } else { + $config['ipsec']['phase2'][] = $ph2ent; + } + + + write_config(); + mark_subsystem_dirty('ipsec'); + + header(url_safe('Location: /ui/ipsec/tunnels')); + exit; + } +} + +$service_hook = 'strongswan'; + +legacy_html_escape_form_data($pconfig); + +include("head.inc"); + +?> + + + + + + 0) { + print_input_errors($input_errors); +} +?> +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ /> + +
+ +
+ + +
+ +
+ +
+ +
   + + + + + +
+ + + +
+
:   + +
:   + + + + + +
+ + + +
+
+ +
+ +
+ +
+ +
+ + +
+ + + + + +
+ + +
+ + +
+ +
+ + +
+ + +
  + + + + + + + +
+
+
+
+
+
+
+
+ +