diff options
Diffstat (limited to 'src/etc/inc/vpn.inc')
-rw-r--r-- | src/etc/inc/vpn.inc | 2056 |
1 files changed, 2056 insertions, 0 deletions
diff --git a/src/etc/inc/vpn.inc b/src/etc/inc/vpn.inc new file mode 100644 index 0000000..2820822 --- /dev/null +++ b/src/etc/inc/vpn.inc @@ -0,0 +1,2056 @@ +<?php + +/* + vpn.inc + Copyright (C) 2004 Scott Ullrich + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + 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. +*/ + +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_BUILDER_BINARIES: /usr/local/sbin/ipsec /usr/local/libexec/ipsec/charon /usr/local/libexec/ipsec/starter + pfSense_BUILDER_BINARIES: /usr/local/sbin/filterdns /usr/local/sbin/mpd4 + pfSense_MODULE: vpn +*/ + +require_once("ipsec.inc"); +require_once("filter.inc"); + +function vpn_ipsec_configure_loglevels($forconfig = false) { + global $config, $ipsec_loglevels; + + $cfgtext = array(); + foreach ($ipsec_loglevels as $lkey => $ldescr) { + if (!isset($config['ipsec']["ipsec_{$lkey}"]) && !$forconfig) { + mwexec("/usr/local/sbin/ipsec stroke loglevel {$lkey} -- -1", false); + } else if (is_numeric($config['ipsec']["ipsec_{$lkey}"]) && + intval($config['ipsec']["ipsec_{$lkey}"]) >= 0 && intval($config['ipsec']["ipsec_{$lkey}"]) <= 5) { + $forconfig ? $cfgtext[] = "${lkey} " . (intval($config['ipsec']["ipsec_{$lkey}"]) - 1) : + mwexec("/usr/local/sbin/ipsec stroke loglevel {$lkey} " . (intval($config['ipsec']["ipsec_{$lkey}"]) - 1) , false); + } + } + if ($forconfig) { + return implode(',', $cfgtext); + } +} + +/* include all configuration functions */ +function vpn_ipsec_convert_to_modp($index) { + + $convertion = ""; + switch ($index) { + case '1': + $convertion = "modp768"; + break; + case '2': + $convertion = "modp1024"; + break; + case '5': + $convertion = "modp1536"; + break; + case '14': + $convertion = "modp2048"; + break; + case '15': + $convertion = "modp3072"; + break; + case '16': + $convertion = "modp4096"; + break; + case '17': + $convertion = "modp6144"; + break; + case '18': + $convertion = "modp8192"; + break; + case '19': + $convertion = "ecp256"; + break; + case '20': + $convertion = "ecp384"; + break; + case '21': + $convertion = "ecp521"; + break; + case '28': + $convertion = "ecp256bp"; + break; + case '29': + $convertion = "ecp384bp"; + break; + case '30': + $convertion = "ecp512bp"; + break; + } + + return $convertion; +} + +function vpn_ipsec_configure($restart = false) { + global $config, $g, $sa, $sn, $p1_ealgos, $p2_ealgos, $ipsec_idhandling; + + /* get the automatic ping_hosts.sh ready */ + unlink_if_exists("{$g['vardb_path']}/ipsecpinghosts"); + touch("{$g['vardb_path']}/ipsecpinghosts"); + + /* service may have been enabled, disabled, or otherwise changed in a way requiring rule updates */ + filter_configure(); + + $syscfg = $config['system']; + $ipseccfg = $config['ipsec']; + if (!isset($ipseccfg['enable'])) { + /* try to stop charon */ + mwexec("/usr/local/sbin/ipsec stop"); + /* Stop dynamic monitoring */ + killbypid("{$g['varrun_path']}/filterdns-ipsec.pid"); + + /* wait for process to die */ + sleep(2); + + /* disallow IPSEC, it is off */ + mwexec("/sbin/ifconfig enc0 down"); + set_single_sysctl("net.inet.ip.ipsec_in_use", "0"); + + return 0; + } + + $a_phase1 = $config['ipsec']['phase1']; + $a_phase2 = $config['ipsec']['phase2']; + $a_client = $config['ipsec']['client']; + + $certpath = "{$g['varetc_path']}/ipsec/ipsec.d/certs"; + $capath = "{$g['varetc_path']}/ipsec/ipsec.d/cacerts"; + $keypath = "{$g['varetc_path']}/ipsec/ipsec.d/private"; + $crlpath = "{$g['varetc_path']}/ipsec/ipsec.d/crls"; + + mwexec("/sbin/ifconfig enc0 up"); + set_single_sysctl("net.inet.ip.ipsec_in_use", "1"); + if (php_uname('m') != "amd64") { + set_single_sysctl("net.inet.ipsec.directdispatch", "0"); + } + + /* needed for config files */ + if (!is_dir("{$g['varetc_path']}/ipsec")) { + mkdir("{$g['varetc_path']}/ipsec"); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d"); + } + if (!is_dir($capath)) { + mkdir($capath); + } + if (!is_dir($keypath)) { + mkdir($keypath); + } + if (!is_dir($crlpath)) { + mkdir($crlpath); + } + if (!is_dir($certpath)) { + mkdir($certpath); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d/aacerts")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d/aacerts"); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d/acerts")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d/acerts"); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d/ocspcerts")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d/ocspcerts"); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d/reqs")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d/reqs"); + } + + + if (platform_booting()) { + echo gettext("Configuring IPsec VPN... "); + } + + /* fastforwarding is not compatible with ipsec tunnels */ + set_single_sysctl("net.inet.ip.fastforwarding", "0"); + + /* resolve all local, peer addresses and setup pings */ + $ipmap = array(); + $rgmap = array(); + $filterdns_list = array(); + $listeniflist = array(); + $aggressive_mode_psk = false; + unset($iflist); + $ifacesuse = array(); + if (is_array($a_phase1) && count($a_phase1)) { + + $ipsecpinghosts = ""; + /* step through each phase1 entry */ + foreach ($a_phase1 as $ph1ent) { + if (isset($ph1ent['disabled'])) { + continue; + } + + if (strpos($ph1ent['interface'], '_vip')) { + $vpninterface = explode('_vip', $ph1ent['interface']); + $ifacesuse[] = get_real_interface($vpninterface[0]); + } else { + $vpninterface = get_failover_interface($ph1ent['interface']); + if (strpos($vpninterface, '_vip')) { + $vpninterface = explode('_vip', $vpninterface); + $ifacesuse[] = get_real_interface($vpninterface[0]); + } elseif (!empty($vpninterface)) { + $ifacesuse[] = $vpninterface; + } + } + + if ($ph1ent['mode'] == "aggressive" && ($ph1ent['authentication_method'] == "pre_shared_key" || $ph1ent['authentication_method'] == "xauth_psk_server")) { + $aggressive_mode_psk = true; + } + + $ikeid = $ph1ent['ikeid']; + $listeniflist = get_real_interface($a_phase1['interface']); + + $ep = ipsec_get_phase1_src($ph1ent); + if (!is_ipaddr($ep)) { + log_error("IPsec ERROR: Could not find phase 1 source for connection {$ph1ent['descr']}. Omitting from configuration file."); + continue; + } + + if (!in_array($ep, $ipmap)) { + $ipmap[] = $ep; + } + + /* see if this tunnel has a hostname for the remote-gateway. If so, + try to resolve it now and add it to the list for filterdns */ + + if (isset ($ph1ent['mobile'])) { + continue; + } + + $rg = $ph1ent['remote-gateway']; + + if (!is_ipaddr($rg)) { + $filterdns_list[] = "{$rg}"; + add_hostname_to_watch($rg); + if (!platform_booting()) { + $rg = resolve_retry($rg); + } + if (!is_ipaddr($rg)) { + continue; + } + } + if (array_search($rg, $rgmap)) { + log_error("The remote gateway {$rg} already exists on another phase 1 entry"); + continue; + } + $rgmap[$ph1ent['remote-gateway']] = $rg; + + if (is_array($a_phase2)) { + /* step through each phase2 entry */ + foreach ($a_phase2 as $ph2ent) { + if (isset($ph2ent['disabled'])) { + continue; + } + + if ($ikeid != $ph2ent['ikeid']) { + continue; + } + + /* add an ipsec pinghosts entry */ + if ($ph2ent['pinghost']) { + if (!is_array($iflist)) { + $iflist = get_configured_interface_list(); + } + $srcip = null; + $local_subnet = ipsec_idinfo_to_cidr($ph2ent['localid'], true, $ph2ent['mode']); + if (is_ipaddrv6($ph2ent['pinghost'])) { + foreach ($iflist as $ifent => $ifname) { + $interface_ip = get_interface_ipv6($ifent); + if (!is_ipaddrv6($interface_ip)) { + continue; + } + if (ip_in_subnet($interface_ip, $local_subnet)) { + $srcip = $interface_ip; + break; + } + } + } else { + foreach ($iflist as $ifent => $ifname) { + $interface_ip = get_interface_ip($ifent); + if (!is_ipaddrv4($interface_ip)) { + continue; + } + if ($local_subnet == "0.0.0.0/0" || ip_in_subnet($interface_ip, $local_subnet)) { + $srcip = $interface_ip; + break; + } + } + } + /* if no valid src IP was found in configured interfaces, try the vips */ + if (is_null($srcip)) { + $viplist = get_configured_vips_list(); + foreach ($viplist as $vip) { + if (ip_in_subnet($vip['ipaddr'], $local_subnet)) { + $srcip = $vip['ipaddr']; + break; + } + } + } + $dstip = $ph2ent['pinghost']; + if (is_ipaddrv6($dstip)) { + $family = "inet6"; + } else { + $family = "inet"; + } + if (is_ipaddr($srcip)) { + $ipsecpinghosts[] = "{$srcip}|{$dstip}|3|||||{$family}|\n"; + } + } + } + } + } + @file_put_contents("{$g['vardb_path']}/ipsecpinghosts", $ipsecpinghosts); + unset($ipsecpinghosts); + } + unset($iflist); + + $accept_unencrypted = ""; + if (isset($config['ipsec']['acceptunencryptedmainmode'])) { + $accept_unencrypted = "accept_unencrypted_mainmode_messages = yes"; + } + + $stronconf = ''; + if (file_exists("{$g['varetc_path']}/ipsec/strongswan.conf")) { + $stronconf = file_get_contents("{$g['varetc_path']}/ipsec/strongswan.conf"); + } + + $i_dont_care_about_security_and_use_aggressive_mode_psk = ""; + if ($aggressive_mode_psk) { + log_error("WARNING: Setting i_dont_care_about_security_and_use_aggressive_mode_psk option because a phase 1 is configured using aggressive mode with pre-shared keys. This is not a secure configuration."); + if (!empty($stronconf) && strpos($stronconf, 'i_dont_care_about_security_and_use_aggressive_mode_psk') === FALSE) { + $restart = true; + } + $i_dont_care_about_security_and_use_aggressive_mode_psk = "i_dont_care_about_security_and_use_aggressive_mode_psk=yes"; + } + + $unity_enabled = 'yes'; + if (isset($config['ipsec']['unityplugin'])) { + $unity_enabled = 'no'; + if (file_exists("/usr/local/lib/ipsec/plugins/libstrongswan-unity.so")) { + conf_mount_rw(); + mwexec("mv /usr/local/lib/ipsec/plugins/libstrongswan-unity.so /usr/local/lib/ipsec/plugins/libstrongswan-unity.MOVED"); + conf_mount_ro(); + } + } else if (file_exists("/usr/local/lib/ipsec/plugins/libstrongswan-unity.MOVED")) { + conf_mount_rw(); + mwexec("mv /usr/local/lib/ipsec/plugins/libstrongswan-unity.MOVED /usr/local/lib/ipsec/plugins/libstrongswan-unity.so"); + conf_mount_ro(); + } + + $makebeforebreak = ''; + if (isset($config['ipsec']['makebeforebreak'])) { + $makebeforebreak = 'make_before_break = yes'; + } + + if (isset($config['ipsec']['enableinterfacesuse'])) { + if (!empty($ifacesuse)) { + $ifacesuse = 'interfaces_use = ' . implode(',', array_unique($ifacesuse)); + } else { + $ifacesuse = ''; + } + } else { + $ifacesuse = ''; + } + + unset($stronconf); + + $strongswan = <<<EOD + +# Automatically generated config file - DO NOT MODIFY. Changes will be overwritten. +starter { +load_warning = no +} + +charon { +# number of worker threads in charon +threads = 16 +ikesa_table_size = 32 +ikesa_table_segments = 4 +init_limit_half_open = 1000 +install_routes = no +{$i_dont_care_about_security_and_use_aggressive_mode_psk} +{$accept_unencrypted} +cisco_unity = {$unity_enabled} +{$ifacesuse} +{$makebeforebreak} + +# And two loggers using syslog. The subsections define the facility to log +# to, currently one of: daemon, auth. +syslog { + identifier = charon + # default level to the LOG_DAEMON facility + daemon { + ike_name = yes + } + # very minimalistic IKE auditing logs to LOG_AUTHPRIV + auth { + default = -1 + ike = 1 + ike_name = yes + } +} + +EOD; + + $strongswan .= "\tplugins {\n"; + + $a_servers = auth_get_authserver_list(); + foreach ($a_servers as $id => $pconfig) { + if ($id == $config['ipsec']['client']['user_source'] && $pconfig['type'] == "radius") { + $strongswan .= <<<EOD + eap-radius { + class_group = yes + eap_start = no + servers { + primary { + address = {$pconfig['host']} + secret = {$pconfig['radius_secret']} + auth_port = {$pconfig['radius_auth_port']} + acct_port = {$pconfig['radius_acct_port']} + } + } + } + +EOD; + break; + } + } + + if (is_array($a_client) && isset($a_client['enable'])) { + $strongswan .= "\t\tattr {\n"; + if ($a_client['pool_address'] && $a_client['pool_netbits']) { + $strongswan .= "\t\t\tsubnet = {$a_client['pool_address']}/{$a_client['pool_netbits']}\n"; + } + + $cfgservers = array(); + if (!empty($a_client['dns_server1'])) { + $cfgservers[] = $a_client['dns_server1']; + } + if (!empty($a_client['dns_server2'])) { + $cfgservers[] = $a_client['dns_server2']; + } + if (!empty($a_client['dns_server3'])) { + $cfgservers[] = $a_client['dns_server3']; + } + if (!empty($a_client['dns_server4'])) { + $cfgservers[] = $a_client['dns_server4']; + } + + if (!empty($cfgservers)) { + $strongswan .= "\t\t\tdns = " . implode(",", $cfgservers) . "\n"; + } + unset($cfgservers); + $cfgservers = array(); + if (!empty($a_client['wins_server1'])) { + $cfgservers[] = $a_client['wins_server1']; + } + if (!empty($a_client['wins_server2'])) { + $cfgservers[] = $a_client['wins_server2']; + } + if (!empty($cfgservers)) { + $strongswan .= "\t\t\tnbns = " . implode(",", $cfgservers) . "\n"; + } + unset($cfgservers); + + if (isset($a_client['net_list']) && is_array($a_phase2)) { + $net_list = ''; + foreach ($a_phase2 as $ph2ent) { + if (isset($ph2ent['disabled'])) { + continue; + } + + if (!isset($ph2ent['mobile'])) { + continue; + } + + $localid = ipsec_idinfo_to_cidr($ph2ent['localid'], true, $ph2ent['mode']); + + if (!empty($net_list)) { + $net_list .= ","; + } + $net_list .= $localid; + } + + if (!empty($net_list)) { + $strongswan .= "\t\t\tsplit-include = {$net_list}\n"; + unset($net_list); + } + } + + if (!empty($a_client['dns_domain'])) { + $strongswan .= "\t\t\t# Search domain and default domain\n"; + $strongswan .= "\t\t\t28674 = \"{$a_client['dns_domain']}\"\n"; + if (empty($a_client['dns_split'])) { + $strongswan .= "\t\t\t28675 = \"{$a_client['dns_domain']}\""; + } + $strongswan .= "\n"; + } + + if (!empty($a_client['dns_split'])) { + $strongswan .= "\t\t\t28675 = {$a_client['dns_split']}\n"; + } + + if (!empty($a_client['login_banner'])) { + $strongswan .= "\t\t\t28672 = \"{$a_client['login_banner']}\"\n"; + } + + if (isset($a_client['save_passwd'])) { + $strongswan .= "\t\t\t28673 = 1\n"; + } + + if ($a_client['pfs_group']) { + $strongswan .= "\t\t\t28679 = \"{$a_client['pfs_group']}\"\n"; + } + $strongswan .= "\t\t}\n"; + + if ($a_client['user_source'] != "none") { + $strongswan .= "\t\txauth-generic {\n"; + $strongswan .= "\t\t\tscript = /etc/inc/ipsec.auth-user.php\n"; + $strongswan .= "\t\t\tauthcfg = "; + $firstsed = 0; + $authcfgs = explode(",", $a_client['user_source']); + foreach ($authcfgs as $authcfg) { + if ($firstsed > 0) { + $strongswan .= ","; + } + if ($authcfg == "system") { + $authcfg = "Local Database"; + } + $strongswan .= $authcfg; + $firstsed = 1; + } + $strongswan .= "\n"; + $strongswan .= "\t\t}\n"; + } + } + + $strongswan .= "\t}\n}\n"; + @file_put_contents("{$g['varetc_path']}/ipsec/strongswan.conf", $strongswan); + unset($strongswan); + + /* generate CA certificates files */ + if (is_array($config['ca']) && count($config['ca'])) { + foreach ($config['ca'] as $ca) { + if (!isset($ca['crt'])) { + log_error(sprintf(gettext("Error: Invalid certificate info for %s"), $ca['descr'])); + continue; + } + $cert = base64_decode($ca['crt']); + $x509cert = openssl_x509_parse(openssl_x509_read($cert)); + if (!is_array($x509cert) || !isset($x509cert['hash'])) { + log_error(sprintf(gettext("Error: Invalid certificate hash info for %s"), $ca['descr'])); + continue; + } + $fname = "{$capath}/{$x509cert['hash']}.0.crt"; + if (!@file_put_contents($fname, $cert)) { + log_error(sprintf(gettext("Error: Cannot write IPsec CA file for %s"), $ca['descr'])); + continue; + } + unset($cert); + } + } + + /* write out CRL files */ + if (is_array($config['crl']) && count($config['crl'])) { + foreach ($config['crl'] as $crl) { + if (!isset($crl['text'])) { + log_error(sprintf(gettext("Warning: Missing CRL data for %s"), $crl['descr'])); + continue; + } + $fpath = "{$crlpath}/{$crl['refid']}.crl"; + if (!@file_put_contents($fpath, base64_decode($crl['text']))) { + log_error(sprintf(gettext("Error: Cannot write IPsec CRL file for %s"), $crl['descr'])); + continue; + } + } + } + + $pskconf = ""; + + if (is_array($a_phase1) && count($a_phase1)) { + foreach ($a_phase1 as $ph1ent) { + + if (isset($ph1ent['disabled'])) { + continue; + } + + if (strstr($ph1ent['authentication_method'], 'rsa') || + in_array($ph1ent['authentication_method'], array('eap-mschapv2', 'eap-tls', 'eap-radius'))) { + $certline = ''; + + $ikeid = $ph1ent['ikeid']; + $cert = lookup_cert($ph1ent['certref']); + + if (!$cert) { + log_error(sprintf(gettext("Error: Invalid phase1 certificate reference for %s"), $ph1ent['name'])); + continue; + } + + @chmod($certpath, 0600); + + $ph1keyfile = "{$keypath}/cert-{$ikeid}.key"; + if (!file_put_contents($ph1keyfile, base64_decode($cert['prv']))) { + log_error(sprintf(gettext("Error: Cannot write phase1 key file for %s"), $ph1ent['name'])); + continue; + } + @chmod($ph1keyfile, 0600); + + $ph1certfile = "{$certpath}/cert-{$ikeid}.crt"; + if (!file_put_contents($ph1certfile, base64_decode($cert['crt']))) { + log_error(sprintf(gettext("Error: Cannot write phase1 certificate file for %s"), $ph1ent['name'])); + @unlink($ph1keyfile); + continue; + } + @chmod($ph1certfile, 0600); + + /* XXX" Traffic selectors? */ + $pskconf .= " : RSA {$ph1keyfile}\n"; + } else { + list ($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local'); + list ($peerid_type, $peerid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap); + + $myid = trim($myid_data); + + if (empty($peerid_data)) { + continue; + } + + if ($myid_type == 'fqdn' && !empty($myid)) { + $myid = "@{$myid}"; + } + + $myid = isset($ph1ent['mobile']) ? trim($myid_data) : "%any"; + + $peerid = ($peerid_data != 'allusers') ? trim($peerid_data) : ''; + + if ($peerid_type == 'fqdn' && !empty($peerid)) { + $peerid = "@{$peerid}"; + } + + if (!empty($ph1ent['pre-shared-key'])) { + $pskconf .= "{$myid} {$peerid} : PSK 0s" . base64_encode(trim($ph1ent['pre-shared-key'])) . "\n"; + } + } + } + } + + /* Add user PSKs */ + if (is_array($config['system']) && is_array($config['system']['user'])) { + foreach ($config['system']['user'] as $user) { + if (!empty($user['ipsecpsk'])) { + $pskconf .= "{$myid} {$user['name']} : PSK 0s" . base64_encode($user['ipsecpsk']) . "\n"; + } + } + unset($user); + } + + /* add PSKs for mobile clients */ + if (is_array($ipseccfg['mobilekey'])) { + foreach ($ipseccfg['mobilekey'] as $key) { + if ($key['ident'] == "allusers") { + $key['ident'] = '%any'; + } + if (empty($key['type'])) { + $key['type'] = 'PSK'; + } + $pskconf .= "{$myid} {$key['ident']} : {$key['type']} 0s" . base64_encode($key['pre-shared-key']) . "\n"; + } + unset($key); + } + + @file_put_contents("{$g['varetc_path']}/ipsec/ipsec.secrets", $pskconf); + chmod("{$g['varetc_path']}/ipsec/ipsec.secrets", 0600); + unset($pskconf); + + $uniqueids = 'yes'; + if (!empty($config['ipsec']['uniqueids'])) { + if (array_key_exists($config['ipsec']['uniqueids'], $ipsec_idhandling)) { + $uniqueids = $config['ipsec']['uniqueids']; + } + } + $natfilterrules = false; + /* begin ipsec.conf */ + $ipsecconf = ""; + $enablecompression = false; + if (is_array($a_phase1) && count($a_phase1)) { + + $ipsecconf .= "# This file is automatically generated. Do not edit\n"; + $ipsecconf .= "config setup\n\tuniqueids = {$uniqueids}\n"; + $ipsecconf .= "\tcharondebug=\"" . vpn_ipsec_configure_loglevels(true) . "\"\n"; + + if (isset($config['ipsec']['strictcrlpolicy'])) { + $ipsecconf .= "\tstrictcrlpolicy = yes \n"; + } + + if (!isset($config['ipsec']['noshuntlaninterfaces'])) { + if ($config['interfaces']['lan']) { + $lanip = get_interface_ip("lan"); + if (!empty($lanip) && is_ipaddrv4($lanip)) { + $lansn = get_interface_subnet("lan"); + $lansa = gen_subnet($lanip, $lansn); + $ipsecconf .= <<<EOD + +conn bypasslan + leftsubnet = {$lansa}/{$lansn} + rightsubnet = {$lansa}/{$lansn} + authby = never + type = passthrough + auto = route + +EOD; + } + } + } + + foreach ($a_phase1 as $ph1ent) { + if (isset($ph1ent['disabled'])) { + continue; + } + + if ($ph1ent['mode'] == "aggressive") { + $aggressive = "yes"; + } else { + $aggressive = "no"; + } + + $ep = ipsec_get_phase1_src($ph1ent); + if (!$ep) { + continue; + } + + $ikeid = $ph1ent['ikeid']; + $keyexchange = "ikev1"; + $passive = "route"; + if (!empty($ph1ent['iketype'])) { + if ($ph1ent['iketype'] == "ikev2") { + $keyexchange = "ikev2"; + //$passive = "start"; + } + } + + if (isset($ph1ent['mobile'])) { + $right_spec = "%any"; + $passive = 'add'; + } else { + if (isset($ph1ent['responderonly'])) { + $passive = 'add'; + } + + $right_spec = $ph1ent['remote-gateway']; + if (is_ipaddr($right_spec)) { + $sourcehost = $right_spec; + } else { + $sourcehost = $rgmap['remote-gateway']; + } + + if ($ph1ent['protocol'] == 'inet') { + if (strpos($ph1ent['interface'], '_vip')) { + $vpninterface = explode('_vip', $ph1ent['interface']); + $ifacesuse = get_real_interface($vpninterface[0]); + $vpninterface = $vpninterface[0]; + } else { + $ifacesuse = get_failover_interface($ph1ent['interface']); + if (strpos($ifacesuse, '_vip')) { + $vpninterface = explode('_vip', $ifacesuse); + $ifacesuse = get_real_interface($vpninterface[0]); + $vpninterface = $vpninterface[0]; + } else { + $vpninterface = convert_real_interface_to_friendly_interface_name($ifacesuse); + } + } + + if (!empty($ifacesuse) && interface_has_gateway($vpninterface)) { + $gatewayip = get_interface_gateway($vpninterface); + $interfaceip = get_interface_ip($vpninterface); + $subnet_bits = get_interface_subnet($vpninterface); + $subnet_ip = gen_subnetv4($interfaceip, $subnet_bits); + /* if the remote gateway is in the local subnet, then don't add a route */ + if (!ip_in_subnet($sourcehost, "{$subnet_ip}/{$subnet_bits}")) { + if (is_ipaddrv4($gatewayip)) { + // log_error("IPSEC interface is not WAN but {$ifacesuse}, adding static route for VPN endpoint {$rgip} via {$gatewayip}"); + mwexec("/sbin/route change -host {$sourcehost} {$gatewayip}", true); + } + } + } + } else if ($ph1ent['protocol'] == 'inet6') { + if (strpos($ph1ent['interface'], '_vip')) { + $vpninterface = explode('_vip', $ph1ent['interface']); + $ifacesuse = get_real_interface($vpninterface[0]); + $vpninterface = $vpninterface[0]; + } else { + $ifacesuse = get_failover_interface($ph1ent['interface']); + if (strpos($ifacesuse, '_vip')) { + $vpninterface = explode('_vip', $ifacesuse); + $ifacesuse = get_real_interface($vpninterface[0]); + $vpninterface = $vpninterface[0]; + } else { + $vpninterface = convert_real_interface_to_friendly_interface_name($ifacesuse); + } + } + + if (!empty($ifacesuse) && interface_has_gateway($vpninterface)) { + $gatewayip = get_interface_gateway_v6($vpninterface); + $interfaceip = get_interface_ipv6($vpninterface); + $subnet_bits = get_interface_subnetv6($vpninterface); + $subnet_ip = gen_subnetv6($interfaceip, $subnet_bits); + /* if the remote gateway is in the local subnet, then don't add a route */ + if (!ip_in_subnet($sourcehost, "{$subnet_ip}/{$subnet_bits}")) { + if (is_ipaddrv6($gatewayip)) { + // log_error("IPSEC interface is not WAN but {$ifacesuse}, adding static route for VPN endpoint {$rgip} via {$gatewayip}"); + mwexec("/sbin/route change -inet6 -host {$sourcehost} {$gatewayip}", true); + } + } + } + } + } + + list ($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local'); + if ($myid_type != 'address' && $myid_type != 'keyid' && $myid_type != 'asn1dn') { + $myid_data = "{$myid_type}:{$myid_data}"; + } elseif ($myid_type == "asn1dn" && !empty($myid_data)) { + if ($myid_data[0] == '#') { + /* asn1dn needs double quotes */ + $myid_data = "\"{$myid_type}:{$myid_data}\""; + } else { + $myid_data = "\"{$myid_data}\""; + } + } + $leftid = ''; + if (!empty($myid_data)) { + $leftid = "leftid = {$myid_data}"; + } + + $peerid_spec = ''; + if (isset($ph1ent['mobile']) && ($ph1ent['authentication_method'] == "pre_shared_key" || $ph1ent['authentication_method'] == "xauth_psk_server")) { + // Only specify peer ID if we are not dealing with mobile PSK + } else { + list ($peerid_type, $peerid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap); + if ($peerid_type == 'any') { + $peerid_spec = ''; + } elseif ($peerid_type != 'address' && $peerid_type != 'keyid' && $peerid_type != 'asn1dn') { + $peerid_spec = "{$peerid_type}:{$peerid_data}"; + } elseif ($peerid_type == "asn1dn") { + /* asn1dn needs double quotes */ + if ($peerid_data[0] == '#') { + $peerid_spec = "\"{$peerid_type}:{$peerid_data}\""; + } elseif (!empty($peerid_data)) { + $peerid_spec = "\"{$peerid_data}\""; + } + } else { + $peerid_spec = $peerid_data; + } + } + + if (is_array($ph1ent['encryption-algorithm']) && !empty($ph1ent['encryption-algorithm']['name']) && !empty($ph1ent['hash-algorithm'])) { + $ealgosp1 = ''; + $ealg_id = $ph1ent['encryption-algorithm']['name']; + $ealg_kl = $ph1ent['encryption-algorithm']['keylen']; + if ($ealg_kl) { + $ealgosp1 = "ike = {$ealg_id}{$ealg_kl}-{$ph1ent['hash-algorithm']}"; + } else { + $ealgosp1 = "ike = {$ealg_id}-{$ph1ent['hash-algorithm']}"; + } + + $modp = vpn_ipsec_convert_to_modp($ph1ent['dhgroup']); + if (!empty($modp)) { + $ealgosp1 .= "-{$modp}"; + } + + $ealgosp1 .= "!"; + } + + if ($ph1ent['dpd_delay'] && $ph1ent['dpd_maxfail']) { + if ($passive == "route") { + $dpdline = "dpdaction = restart"; + } else { + $dpdline = "dpdaction = clear"; + } + $dpdline .= "\n\tdpddelay = {$ph1ent['dpd_delay']}s"; + $dpdtimeout = $ph1ent['dpd_delay'] * ($ph1ent['dpd_maxfail'] + 1); + $dpdline .= "\n\tdpdtimeout = {$dpdtimeout}s"; + } else { + $dpdline = "dpdaction = none"; + } + + $ikelifeline = ''; + if ($ph1ent['lifetime']) { + $ikelifeline = "ikelifetime = {$ph1ent['lifetime']}s"; + } + + $rightsourceip = NULL; + if (isset($ph1ent['mobile']) && !empty($a_client['pool_address'])) { + $rightsourceip = "\trightsourceip = {$a_client['pool_address']}/{$a_client['pool_netbits']}\n"; + } + + $authentication = ""; + switch ($ph1ent['authentication_method']) { + case 'eap-mschapv2': + if (isset($ph1ent['mobile'])) { + $authentication = "eap_identity=%any\n\t"; + $authentication .= "leftauth=pubkey\n\trightauth=eap-mschapv2"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } + break; + case 'eap-tls': + if (isset($ph1ent['mobile'])) { + $authentication = "eap_identity=%identity\n\t"; + $authentication .= "leftauth=pubkey\n\trightauth=eap-tls"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } else { + $authentication = "leftauth=eap-tls\n\trightauth=eap-tls"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } + break; + case 'eap-radius': + if (isset($ph1ent['mobile'])) { + $authentication = "eap_identity=%identity\n\t"; + $authentication .= "leftauth=pubkey\n\trightauth=eap-radius"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } else { + $authentication = "leftauth=eap-radius\n\trightauth=eap-radius"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } + break; + case 'xauth_rsa_server': + $authentication = "leftauth = pubkey\n\trightauth = pubkey"; + $authentication .= "\n\trightauth2 = xauth-generic"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + break; + case 'xauth_psk_server': + $authentication = "leftauth = psk\n\trightauth = psk"; + $authentication .= "\n\trightauth2 = xauth-generic"; + break; + case 'pre_shared_key': + $authentication = "leftauth = psk\n\trightauth = psk"; + break; + case 'rsasig': + $authentication = "leftauth = pubkey\n\trightauth = pubkey"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + break; + case 'hybrid_rsa_server': + $authentication = "leftauth = xauth-generic\n\trightauth = pubkey"; + $authentication .= "\n\trightauth2 = xauth"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + break; + } + + $left_spec = $ep; + + if (isset($ph1ent['reauth_enable'])) { + $reauth = "reauth = no"; + } else { + $reauth = "reauth = yes"; + } + if (isset($ph1ent['rekey_enable'])) { + $rekey = "rekey = no"; + } else { + $rekey = "rekey = yes"; + } + + if ($ph1ent['nat_traversal'] == 'off') { + $forceencaps = 'forceencaps = no'; + } else if ($ph1ent['nat_traversal'] == 'force') { + $forceencaps = 'forceencaps = yes'; + } else { + $forceencaps = 'forceencaps = no'; + } + + if ($ph1ent['mobike'] == 'on') { + $mobike = 'mobike = yes'; + } else { + $mobike = 'mobike = no'; + } + + $ipseclifetime = 0; + $rightsubnet_spec = array(); + $leftsubnet_spec = array(); + $reqids = array(); + $ealgoAHsp2arr = array(); + $ealgoESPsp2arr = array(); + if (is_array($a_phase2) && count($a_phase2)) { + foreach ($a_phase2 as $ph2ent) { + if ($ikeid != $ph2ent['ikeid']) { + continue; + } + + if (isset($ph2ent['disabled'])) { + continue; + } + + if (isset($ph2ent['mobile']) && !isset($a_client['enable'])) { + continue; + } + + if (($ph2ent['mode'] == 'tunnel') or ($ph2ent['mode'] == 'tunnel6')) { + $tunneltype = "type = tunnel"; + + $localid_type = $ph2ent['localid']['type']; + $leftsubnet_data = ipsec_idinfo_to_cidr($ph2ent['localid'], false, $ph2ent['mode']); + + /* Do not print localid in some cases, such as a pure-psk or psk/xauth single phase2 mobile tunnel */ + if (($localid_type == "none" || $localid_type == "mobile") && + isset($ph1ent['mobile']) && (ipsec_get_number_of_phase2($ikeid) == 1)) { + $left_spec = '%any'; + } else { + if ($localid_type != "address") { + $localid_type = "subnet"; + } + // Don't let an empty subnet into config, it can cause parse errors. Ticket #2201. + if (!is_ipaddr($leftsubnet_data) && !is_subnet($leftsubnet_data) && ($leftsubnet_data != "0.0.0.0/0")) { + log_error("Invalid IPsec Phase 2 \"{$ph2ent['descr']}\" - {$ph2ent['localid']['type']} has no subnet."); + continue; + } + if (!empty($ph2ent['natlocalid'])) { + $natleftsubnet_data = ipsec_idinfo_to_cidr($ph2ent['natlocalid'], false, $ph2ent['mode']); + if ($ph2ent['natlocalid']['type'] != "address") { + if (is_subnet($natleftsubnet_data)) { + $leftsubnet_data = "{$natleftsubnet_data}|{$leftsubnet_data}"; + } + } else { + if (is_ipaddr($natleftsubnet_data)) { + $leftsubnet_data = "{$natleftsubnet_data}|{$leftsubnet_data}"; + } + } + $natfilterrules = true; + } + } + + $leftsubnet_spec[] = $leftsubnet_data; + + if (!isset($ph2ent['mobile'])) { + $tmpsubnet = ipsec_idinfo_to_cidr($ph2ent['remoteid'], false, $ph2ent['mode']); + $rightsubnet_spec[] = $tmpsubnet; + } else if (!empty($a_client['pool_address'])) { + $rightsubnet_spec[] = "{$a_client['pool_address']}/{$a_client['pool_netbits']}"; + } + } else { + $tunneltype = "type = transport"; + + if ((($ph1ent['authentication_method'] == "xauth_psk_server") || + ($ph1ent['authentication_method'] == "pre_shared_key")) && isset($ph1ent['mobile'])) { + $left_spec = "%any"; + } else { + $tmpsubnet = ipsec_get_phase1_src($ph1ent); + $leftsubnet_spec[] = $tmpsubnet; + } + + if (!isset($ph2ent['mobile'])) { + $rightsubnet_spec[] = $right_spec; + } + } + + if (isset($a_client['pfs_group']) && isset($ph2ent['mobile'])) { + $ph2ent['pfsgroup'] = $a_client['pfs_group']; + } + + if ($ph2ent['protocol'] == 'esp') { + if (is_array($ph2ent['encryption-algorithm-option'])) { + foreach ($ph2ent['encryption-algorithm-option'] as $ealg) { + $ealg_id = $ealg['name']; + $ealg_kl = $ealg['keylen']; + + if (!empty($ealg_kl) && $ealg_kl == "auto") { + if (empty($p2_ealgos) || !is_array($p2_ealgos)) { + require("ipsec.inc"); + } + $key_hi = $p2_ealgos[$ealg_id]['keysel']['hi']; + $key_lo = $p2_ealgos[$ealg_id]['keysel']['lo']; + $key_step = $p2_ealgos[$ealg_id]['keysel']['step']; + /* XXX: in some cases where include ordering is suspect these variables + * are somehow 0 and we enter this loop forever and timeout after 900 + * seconds wrecking bootup */ + if ($key_hi != 0 and $key_lo != 0 and $key_step != 0) { + for ($keylen = $key_hi; $keylen >= $key_lo; $keylen -= $key_step) { + if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option'])) { + foreach ($ph2ent['hash-algorithm-option'] as $halgo) { + $halgo = str_replace('hmac_', '', $halgo); + $tmpealgo = "{$ealg_id}{$keylen}-{$halgo}"; + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + if (!empty($modp)) { + $tmpealgo .= "-{$modp}"; + } + $ealgoESPsp2arr[] = $tmpealgo; + } + } else { + $tmpealgo = "{$ealg_id}{$keylen}"; + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + if (!empty($modp)) { + $tmpealgo .= "-{$modp}"; + } + $ealgoESPsp2arr[] = $tmpealgo; + } + } + } + } else { + if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option'])) { + foreach ($ph2ent['hash-algorithm-option'] as $halgo) { + $halgo = str_replace('hmac_', '', $halgo); + $tmpealgo = "{$ealg_id}{$ealg_kl}-{$halgo}"; + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + if (!empty($modp)) { + $tmpealgo .= "-{$modp}"; + } + $ealgoESPsp2arr[] = $tmpealgo; + } + } else { + $tmpealgo = "{$ealg_id}{$ealg_kl}"; + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + if (!empty($modp)) { + $tmpealgo .= "-{$modp}"; + } + $ealgoESPsp2arr[] = $tmpealgo; + } + } + } + } + } else if ($ph2ent['protocol'] == 'ah') { + if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option'])) { + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + foreach ($ph2ent['hash-algorithm-option'] as $tmpAHalgo) { + $tmpAHalgo = str_replace('hmac_', '', $tmpAHalgo); + if (!empty($modp)) { + $tmpAHalgo = "-{$modp}"; + } + $ealgoAHsp2arr[] = $tmpAHalgo; + } + } + } + + $reqids[] = $ph2ent['reqid']; + + if (!empty($ph2ent['lifetime'])) { + if ($ipseclifetime == 0 || intval($ipseclifetime) > intval($ph2ent['lifetime'])) { + $ipseclifetime = intval($ph2ent['lifetime']); + } + } + + } + } + + $ipsecconnect =<<<EOD + fragmentation = yes + keyexchange = {$keyexchange} + {$reauth} + {$forceencaps} + {$mobike} + {$rekey} + installpolicy = yes + {$tunneltype} + {$dpdline} + auto = {$passive} + left = {$left_spec} + right = {$right_spec} + {$leftid} + +EOD; + + if (isset($config['ipsec']['compression'])) { + $ipsecconnect .= "\tcompress = yes\n"; + $enablecompression = true; + } + if (!empty($ikelifeline)) { + $ipsecconnect .= "\t{$ikelifeline}\n"; + } + if ($ipseclifetime > 0) { + $ipsecconnect .= "\tlifetime = {$ipseclifetime}s\n"; + } + if (!empty($rightsourceip)) { + $ipsecconnect .= "{$rightsourceip}"; + } + if (!empty($ealgosp1)) { + $ipsecconnect .= "\t{$ealgosp1}\n"; + } + if (!empty($ealgoAHsp2arr)) { + $ipsecconnect .= "\tah = " . join(',', $ealgoAHsp2arr) . "!\n"; + } + if (!empty($ealgoESPsp2arr)) { + $ipsecconnect .= "\tesp = " . join(',', $ealgoESPsp2arr) . "!\n"; + } + if (!empty($authentication)) { + $ipsecconnect .= "\t{$authentication}\n"; + } + if (!empty($peerid_spec)) { + $ipsecconnect .= "\trightid = {$peerid_spec}\n"; + } + if ($keyexchange == 'ikev1') { + $ipsecconnect .= "\taggressive = {$aggressive}\n"; + } + + if (!isset($ph1ent['mobile']) && $keyexchange == 'ikev1') { + if (!empty($rightsubnet_spec)) { + $ipsecfin = ''; + foreach ($rightsubnet_spec as $idx => $rsubnet) { + $ipsecfin .= "\nconn con{$ph1ent['ikeid']}00{$idx}\n"; + //if (!empty($reqids[$idx])) { + // $ipsecfin .= "\treqid = " . $reqids[$idx] . "\n"; + //} + $ipsecfin .= $ipsecconnect; + $ipsecfin .= "\trightsubnet = {$rsubnet}\n"; + $ipsecfin .= "\tleftsubnet = " . $leftsubnet_spec[$idx] . "\n"; + } + } else { + log_error("No phase2 specifications for tunnel with REQID = {$ikeid}"); + } + } else { + $ipsecfin = "\nconn con{$ph1ent['ikeid']}\n"; + //if (!empty($reqids[$idx])) { + // $ipsecfin .= "\treqid = " . $reqids[0] . "\n"; + //} + $ipsecfin .= $ipsecconnect; + if (!isset($ph1ent['mobile']) && !empty($rightsubnet_spec)) { + $tempsubnets = array(); + foreach ($rightsubnet_spec as $rightsubnet) { + $tempsubnets[$rightsubnet] = $rightsubnet; + } + $ipsecfin .= "\trightsubnet = " . join(",", $tempsubnets) . "\n"; + unset($tempsubnets, $rightsubnet); + } + if (!empty($leftsubnet_spec)) { + $tempsubnets = array(); + foreach ($leftsubnet_spec as $leftsubnet) { + $tempsubnets[$leftsubnet] = $leftsubnet; + } + $ipsecfin .= "\tleftsubnet = " . join(",", $tempsubnets) . "\n"; + unset($tempsubnets, $leftsubnet); + } + } + $ipsecconf .= $ipsecfin; + unset($ipsecfin); + } + } + + @file_put_contents("{$g['varetc_path']}/ipsec/ipsec.conf", $ipsecconf); + unset($ipsecconf); + /* end ipsec.conf */ + + if ($enablecompression === true) { + set_single_sysctl('net.inet.ipcomp.ipcomp_enable', 1); + } else { + set_single_sysctl('net.inet.ipcomp.ipcomp_enable', 0); + } + + /* manage process */ + if ($restart === true) { + mwexec("/usr/local/sbin/ipsec restart", false); + } else { + if (isvalidpid("{$g['varrun_path']}/starter.charon.pid")) { + /* Update configuration changes */ + /* Read secrets */ + mwexec("/usr/local/sbin/ipsec rereadall", false); + mwexec("/usr/local/sbin/ipsec reload", false); + } else { + mwexec("/usr/local/sbin/ipsec start", false); + } + } + + if ($natfilterrules == true) { + filter_configure(); + } + /* start filterdns, if necessary */ + if (count($filterdns_list) > 0) { + $interval = 60; + if (!empty($ipseccfg['dns-interval']) && is_numeric($ipseccfg['dns-interval'])) { + $interval = $ipseccfg['dns-interval']; + } + + $hostnames = ""; + array_unique($filterdns_list); + foreach ($filterdns_list as $hostname) { + $hostnames .= "cmd {$hostname} '/usr/local/sbin/pfSctl -c \"service reload ipsecdns\"'\n"; + } + file_put_contents("{$g['varetc_path']}/ipsec/filterdns-ipsec.hosts", $hostnames); + unset($hostnames); + + if (isvalidpid("{$g['varrun_path']}/filterdns-ipsec.pid")) { + sigkillbypid("{$g['varrun_path']}/filterdns-ipsec.pid", "HUP"); + } else { + mwexec("/usr/local/sbin/filterdns -p {$g['varrun_path']}/filterdns-ipsec.pid -i {$interval} -c {$g['varetc_path']}/ipsec/filterdns-ipsec.hosts -d 1"); + } + } else { + killbypid("{$g['varrun_path']}/filterdns-ipsec.pid"); + @unlink("{$g['varrun_path']}/filterdns-ipsec.pid"); + } + + if (platform_booting()) { + echo "done\n"; + } + + return count($filterdns_list); +} + +/* + * Forcefully restart IPsec + * This is required for when dynamic interfaces reload + * For all other occasions the normal vpn_ipsec_configure() + * will gracefully reload the settings without restarting + */ +function vpn_ipsec_force_reload($interface = "") { + global $g, $config; + + $ipseccfg = $config['ipsec']; + + if (!empty($interface) && is_array($ipseccfg['phase1'])) { + $found = false; + foreach ($ipseccfg['phase1'] as $ipsec) { + if (!isset($ipsec['disabled']) && ($ipsec['interface'] == $interface)) { + $found = true; + break; + } + } + if (!$found) { + log_error(sprintf(gettext("Ignoring IPsec reload since there are no tunnels on interface %s"), $interface)); + return; + } + } + + /* if ipsec is enabled, start up again */ + if (isset($ipseccfg['enable'])) { + log_error(gettext("Forcefully reloading IPsec")); + vpn_ipsec_configure(); + } +} + +/* master setup for vpn (mpd) */ +function vpn_setup() { + /* start pptpd */ + vpn_pptpd_configure(); + + /* start pppoe server */ + vpn_pppoes_configure(); + + /* setup l2tp */ + vpn_l2tp_configure(); +} + +function vpn_netgraph_support() { + $iflist = get_configured_interface_list(); + foreach ($iflist as $iface) { + $realif = get_real_interface($iface); + /* Get support for netgraph(4) from the nic */ + $ifinfo = pfSense_get_interface_addresses($realif); + if (!empty($ifinfo) && in_array($ifinfo['iftype'], array("ether", "vlan", "bridge"))) { + pfSense_ngctl_attach(".", $realif); + } + } +} + +function vpn_pptpd_configure() { + global $config, $g; + + $syscfg = $config['system']; + $pptpdcfg = $config['pptpd']; + + if (platform_booting()) { + if (!$pptpdcfg['mode'] || ($pptpdcfg['mode'] == "off")) { + return 0; + } + + if (platform_booting(true)) { + echo gettext("Configuring PPTP VPN service... "); + } + } else { + /* kill mpd */ + killbypid("{$g['varrun_path']}/pptp-vpn.pid"); + + /* wait for process to die */ + sleep(3); + + if (is_process_running("mpd -b")) { + killbypid("{$g['varrun_path']}/pptp-vpn.pid"); + log_error(gettext("Could not kill mpd within 3 seconds. Trying again.")); + } + + /* remove mpd.conf, if it exists */ + unlink_if_exists("{$g['varetc_path']}/pptp-vpn/mpd.conf"); + unlink_if_exists("{$g['varetc_path']}/pptp-vpn/mpd.links"); + unlink_if_exists("{$g['varetc_path']}/pptp-vpn/mpd.secret"); + } + + if (empty($pptpdcfg['n_pptp_units'])) { + log_error("Something wrong in the PPTPd configuration. Preventing starting the daemon because issues would arise."); + return; + } + + /* make sure pptp-vpn directory exists */ + if (!file_exists("{$g['varetc_path']}/pptp-vpn")) { + mkdir("{$g['varetc_path']}/pptp-vpn"); + } + + switch ($pptpdcfg['mode']) { + case 'server': + /* write mpd.conf */ + $fd = fopen("{$g['varetc_path']}/pptp-vpn/mpd.conf", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.conf in vpn_pptpd_configure().") . "\n"); + return 1; + } + + $mpdconf = <<<EOD +pptps: + +EOD; + + for ($i = 0; $i < $pptpdcfg['n_pptp_units']; $i++) { + $mpdconf .= " load pt{$i}\n"; + } + + for ($i = 0; $i < $pptpdcfg['n_pptp_units']; $i++) { + + $clientip = long2ip32(ip2long($pptpdcfg['remoteip']) + $i); + + $mpdconf .= <<<EOD + +pt{$i}: + new -i pptpd{$i} pt{$i} pt{$i} + set ipcp ranges {$pptpdcfg['localip']}/32 {$clientip}/32 + load pts + +EOD; + } + + $mpdconf .=<<<EOD + +pts: + set iface disable on-demand + set iface enable proxy-arp + set iface enable tcpmssfix + set iface idle 1800 + set iface up-script /usr/local/sbin/vpn-linkup + set iface down-script /usr/local/sbin/vpn-linkdown + set bundle enable multilink + set bundle enable crypt-reqd + set link yes acfcomp protocomp + set link no pap chap + set link enable chap-msv2 + set link mtu 1460 + set link keep-alive 10 60 + set ipcp yes vjcomp + set bundle enable compression + set ccp yes mppc + set ccp yes mpp-e128 + set ccp yes mpp-stateless + +EOD; + + if (!isset ($pptpdcfg['req128'])) { + $mpdconf .=<<<EOD + set ccp yes mpp-e40 + set ccp yes mpp-e56 + +EOD; + } + + if (isset($pptpdcfg["wins"]) && $pptpdcfg['wins'] != "") { + $mpdconf .= " set ipcp nbns {$pptpdcfg['wins']}\n"; + } + + if (!empty($pptpdcfg['dns1'])) { + $mpdconf .= " set ipcp dns " . $pptpdcfg['dns1']; + if (!empty($pptpdcfg['dns2'])) { + $mpdconf .= " " . $pptpdcfg['dns2']; + } + $mpdconf .= "\n"; + } elseif (isset ($config['dnsmasq']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (isset($config['unbound']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $mpdconf .= " set ipcp dns " . join(" ", $syscfg['dnsserver']) . "\n"; + } + + if (isset ($pptpdcfg['radius']['server']['enable'])) { + $authport = (isset($pptpdcfg['radius']['server']['port']) && strlen($pptpdcfg['radius']['server']['port']) > 1) ? $pptpdcfg['radius']['server']['port'] : 1812; + $acctport = $authport + 1; + $mpdconf .=<<<EOD + set radius server {$pptpdcfg['radius']['server']['ip']} "{$pptpdcfg['radius']['server']['secret']}" {$authport} {$acctport} + +EOD; + if (isset ($pptpdcfg['radius']['server2']['enable'])) { + $authport = (isset($pptpdcfg['radius']['server2']['port']) && strlen($pptpdcfg['radius']['server2']['port']) > 1) ? $pptpdcfg['radius']['server2']['port'] : 1812; + $acctport = $authport + 1; + $mpdconf .=<<<EOD + set radius server {$pptpdcfg['radius']['server2']['ip']} "{$pptpdcfg['radius']['server2']['secret2']}" {$authport} {$acctport} + +EOD; + } + $mpdconf .=<<<EOD + set radius retries 3 + set radius timeout 10 + set auth enable radius-auth + +EOD; + + if (isset ($pptpdcfg['radius']['accounting'])) { + $mpdconf .=<<<EOD + set auth enable radius-acct + set radius acct-update 300 + +EOD; + } + } + + fwrite($fd, $mpdconf); + fclose($fd); + unset($mpdconf); + + /* write mpd.links */ + $fd = fopen("{$g['varetc_path']}/pptp-vpn/mpd.links", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.links in vpn_pptpd_configure().") . "\n"); + return 1; + } + + $mpdlinks = ""; + + for ($i = 0; $i < $pptpdcfg['n_pptp_units']; $i++) { + $mpdlinks .=<<<EOD + +pt{$i}: + set link type pptp + set pptp enable incoming + set pptp disable originate + set pptp disable windowing + +EOD; + } + + fwrite($fd, $mpdlinks); + fclose($fd); + unset($mpdlinks); + + /* write mpd.secret */ + $fd = fopen("{$g['varetc_path']}/pptp-vpn/mpd.secret", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.secret in vpn_pptpd_configure().") . "\n"); + return 1; + } + + $mpdsecret = ""; + + if (is_array($pptpdcfg['user'])) { + foreach ($pptpdcfg['user'] as $user) { + $pass = str_replace('\\', '\\\\', $user['password']); + $pass = str_replace('"', '\"', $pass); + $mpdsecret .= "{$user['name']} \"{$pass}\" {$user['ip']}\n"; + } + } + + fwrite($fd, $mpdsecret); + fclose($fd); + unset($mpdsecret); + chmod("{$g['varetc_path']}/pptp-vpn/mpd.secret", 0600); + + vpn_netgraph_support(); + + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd4 -b -d {$g['varetc_path']}/pptp-vpn -p {$g['varrun_path']}/pptp-vpn.pid -s pptps pptps"); + + break; + + case 'redir': + break; + } + + if (platform_booting()) { + echo "done\n"; + } + + return 0; +} + +function vpn_pppoes_configure() { + global $config; + + if (is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoe) { + vpn_pppoe_configure($pppoe); + } + } +} + +function vpn_pppoe_configure(&$pppoecfg) { + global $config, $g; + + $syscfg = $config['system']; + + /* create directory if it does not exist */ + if (!is_dir("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn")) { + mkdir("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn"); + } + + if (platform_booting()) { + if (!$pppoecfg['mode'] || ($pppoecfg['mode'] == "off")) { + return 0; + } + + echo gettext("Configuring PPPoE Server service... "); + } else { + /* kill mpd */ + killbypid("{$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid"); + + /* wait for process to die */ + sleep(2); + + } + + switch ($pppoecfg['mode']) { + + case 'server': + + $pppoe_interface = get_real_interface($pppoecfg['interface']); + + if ($pppoecfg['paporchap'] == "chap") { + $paporchap = "set link enable chap"; + } else { + $paporchap = "set link enable pap"; + } + + /* write mpd.conf */ + $fd = fopen("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn/mpd.conf", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.conf in vpn_pppoe_configure().") . "\n"); + return 1; + } + $mpdconf = "\n\n"; + $mpdconf .= "poes:\n"; + + for ($i = 0; $i < $pppoecfg['n_pppoe_units']; $i++) { + $mpdconf .= " load poes{$pppoecfg['pppoeid']}{$i}\n"; + } + + for ($i = 0; $i < $pppoecfg['n_pppoe_units']; $i++) { + + $clientip = long2ip32(ip2long($pppoecfg['remoteip']) + $i); + + if (isset($pppoecfg['radius']['radiusissueips']) && isset($pppoecfg['radius']['server']['enable'])) { + $issue_ip_type = "set ipcp ranges {$pppoecfg['localip']}/32 0.0.0.0/0"; + } else { + $issue_ip_type = "set ipcp ranges {$pppoecfg['localip']}/32 {$clientip}/32"; + } + + $mpdconf .=<<<EOD + +poes{$pppoecfg['pppoeid']}{$i}: + new -i poes{$pppoecfg['pppoeid']}{$i} poes{$pppoecfg['pppoeid']}{$i} poes{$pppoecfg['pppoeid']}{$i} + {$issue_ip_type} + load pppoe_standard + +EOD; + } + + $mpdconf .=<<<EOD + +pppoe_standard: + set bundle no multilink + set bundle enable compression + set auth max-logins 1 + set iface up-script /usr/local/sbin/vpn-linkup + set iface down-script /usr/local/sbin/vpn-linkdown + set iface idle 0 + set iface disable on-demand + set iface disable proxy-arp + set iface enable tcpmssfix + set iface mtu 1500 + set link no pap chap + {$paporchap} + set link keep-alive 60 180 + set ipcp yes vjcomp + set ipcp no vjcomp + set link max-redial -1 + set link mtu 1492 + set link mru 1492 + set ccp yes mpp-e40 + set ccp yes mpp-e128 + set ccp yes mpp-stateless + set link latency 1 + #set ipcp dns 10.10.1.3 + #set bundle accept encryption + +EOD; + + if (!empty($pppoecfg['dns1'])) { + $mpdconf .= " set ipcp dns " . $pppoecfg['dns1']; + if (!empty($pppoecfg['dns2'])) { + $mpdconf .= " " . $pppoecfg['dns2']; + } + $mpdconf .= "\n"; + } elseif (isset ($config['dnsmasq']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (isset ($config['unbound']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $mpdconf .= " set ipcp dns " . join(" ", $syscfg['dnsserver']) . "\n"; + } + + if (isset ($pppoecfg['radius']['server']['enable'])) { + $radiusport = ""; + $radiusacctport = ""; + if (isset($pppoecfg['radius']['server']['port'])) { + $radiusport = $pppoecfg['radius']['server']['port']; + } + if (isset($pppoecfg['radius']['server']['acctport'])) { + $radiusacctport = $pppoecfg['radius']['server']['acctport']; + } + $mpdconf .=<<<EOD + set radius server {$pppoecfg['radius']['server']['ip']} "{$pppoecfg['radius']['server']['secret']}" {$radiusport} {$radiusacctport} + set radius retries 3 + set radius timeout 10 + set auth enable radius-auth + +EOD; + + if (isset ($pppoecfg['radius']['accounting'])) { + $mpdconf .=<<<EOD + set auth enable radius-acct + +EOD; + } + } + + fwrite($fd, $mpdconf); + fclose($fd); + unset($mpdconf); + + /* write mpd.links */ + $fd = fopen("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn/mpd.links", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.links in vpn_pppoe_configure().") . "\n"); + return 1; + } + + $mpdlinks = ""; + + for ($i = 0; $i < $pppoecfg['n_pppoe_units']; $i++) { + $mpdlinks .=<<<EOD + +poes{$pppoecfg['pppoeid']}{$i}: + set phys type pppoe + set pppoe iface {$pppoe_interface} + set pppoe service "*" + set pppoe disable originate + set pppoe enable incoming + +EOD; + } + + fwrite($fd, $mpdlinks); + fclose($fd); + unset($mpdlinks); + + if ($pppoecfg['username']) { + /* write mpd.secret */ + $fd = fopen("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn/mpd.secret", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.secret in vpn_pppoe_configure().") . "\n"); + return 1; + } + + $mpdsecret = "\n\n"; + + if (!empty($pppoecfg['username'])) { + $item = explode(" ", $pppoecfg['username']); + foreach ($item as $userdata) { + $data = explode(":", $userdata); + $mpdsecret .= "{$data[0]} \"" . base64_decode($data[1]) . "\" {$data[2]}\n"; + } + } + + fwrite($fd, $mpdsecret); + fclose($fd); + unset($mpdsecret); + chmod("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn/mpd.secret", 0600); + } + + /* Check if previous instance is still up */ + while (file_exists("{$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid") && isvalidpid("{$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid")) { + killbypid("{$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid"); + } + + /* Get support for netgraph(4) from the nic */ + pfSense_ngctl_attach(".", $pppoe_interface); + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd4 -b -d {$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn -p {$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid -s poes poes"); + + break; + } + + if (platform_booting()) { + echo gettext("done") . "\n"; + } + + return 0; +} + +function vpn_l2tp_configure() { + global $config, $g; + + $syscfg = $config['system']; + $l2tpcfg = $config['l2tp']; + + /* create directory if it does not exist */ + if (!is_dir("{$g['varetc_path']}/l2tp-vpn")) { + mkdir("{$g['varetc_path']}/l2tp-vpn"); + } + + if (platform_booting()) { + if (!$l2tpcfg['mode'] || ($l2tpcfg['mode'] == "off")) { + return 0; + } + + echo gettext("Configuring l2tp VPN service... "); + } else { + /* kill mpd */ + killbypid("{$g['varrun_path']}/l2tp-vpn.pid"); + + /* wait for process to die */ + sleep(8); + + } + + /* make sure l2tp-vpn directory exists */ + if (!file_exists("{$g['varetc_path']}/l2tp-vpn")) { + mkdir("{$g['varetc_path']}/l2tp-vpn"); + } + + switch ($l2tpcfg['mode']) { + + case 'server': + if ($l2tpcfg['paporchap'] == "chap") { + $paporchap = "set link enable chap"; + } else { + $paporchap = "set link enable pap"; + } + + /* write mpd.conf */ + $fd = fopen("{$g['varetc_path']}/l2tp-vpn/mpd.conf", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.conf in vpn_l2tp_configure().") . "\n"); + return 1; + } + $mpdconf = "\n\n"; + $mpdconf .=<<<EOD +l2tps: + +EOD; + + for ($i = 0; $i < $l2tpcfg['n_l2tp_units']; $i++) { + $mpdconf .= " load l2tp{$i}\n"; + } + + for ($i = 0; $i < $l2tpcfg['n_l2tp_units']; $i++) { + + $clientip = long2ip32(ip2long($l2tpcfg['remoteip']) + $i); + + if (isset ($l2tpcfg['radius']['radiusissueips']) && isset ($l2tpcfg['radius']['enable'])) { + $issue_ip_type = "set ipcp ranges {$l2tpcfg['localip']}/32 0.0.0.0/0"; + } else { + $issue_ip_type = "set ipcp ranges {$l2tpcfg['localip']}/32 {$clientip}/32"; + } + + $mpdconf .=<<<EOD + +l2tp{$i}: + new -i l2tp{$i} l2tp{$i} l2tp{$i} + {$issue_ip_type} + load l2tp_standard + +EOD; + } + + $mpdconf .=<<<EOD + +l2tp_standard: + set bundle disable multilink + set bundle enable compression + set bundle yes crypt-reqd + set ipcp yes vjcomp + # set ipcp ranges 131.188.69.161/32 131.188.69.170/28 + set ccp yes mppc + set iface disable on-demand + set iface enable proxy-arp + set iface up-script /usr/local/sbin/vpn-linkup + set iface down-script /usr/local/sbin/vpn-linkdown + set link yes acfcomp protocomp + set link no pap chap + {$paporchap} + set link keep-alive 10 180 + +EOD; + + if (is_ipaddr($l2tpcfg['wins'])) { + $mpdconf .= " set ipcp nbns {$l2tpcfg['wins']}\n"; + } + if (is_ipaddr($l2tpcfg['dns1'])) { + $mpdconf .= " set ipcp dns " . $l2tpcfg['dns1']; + if (is_ipaddr($l2tpcfg['dns2'])) { + $mpdconf .= " " . $l2tpcfg['dns2']; + } + $mpdconf .= "\n"; + } elseif (isset ($config['dnsmasq']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (isset ($config['unbound']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $mpdconf .= " set ipcp dns " . join(" ", $syscfg['dnsserver']) . "\n"; + } + + if (isset ($l2tpcfg['radius']['enable'])) { + $mpdconf .=<<<EOD + set radius server {$l2tpcfg['radius']['server']} "{$l2tpcfg['radius']['secret']}" + set radius retries 3 + set radius timeout 10 + set auth enable radius-auth + +EOD; + + if (isset ($l2tpcfg['radius']['accounting'])) { + $mpdconf .=<<<EOD + set auth enable radius-acct + +EOD; + } + } + + fwrite($fd, $mpdconf); + fclose($fd); + unset($mpdconf); + + /* write mpd.links */ + $fd = fopen("{$g['varetc_path']}/l2tp-vpn/mpd.links", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.links in vpn_l2tp_configure().") . "\n"); + return 1; + } + + $mpdlinks = ""; + + for ($i = 0; $i < $l2tpcfg['n_l2tp_units']; $i++) { + $mpdlinks .=<<<EOD + +l2tp{$i}: + set link type l2tp + set l2tp enable incoming + set l2tp disable originate + +EOD; + if (!empty($l2tpcfg['secret'])) { + $mpdlinks .= "set l2tp secret {$l2tpcfg['secret']}\n"; + } + } + + fwrite($fd, $mpdlinks); + fclose($fd); + unset($mpdlinks); + + /* write mpd.secret */ + $fd = fopen("{$g['varetc_path']}/l2tp-vpn/mpd.secret", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.secret in vpn_l2tp_configure().") . "\n"); + return 1; + } + + $mpdsecret = "\n\n"; + + if (is_array($l2tpcfg['user'])) { + foreach ($l2tpcfg['user'] as $user) { + $mpdsecret .= "{$user['name']} \"{$user['password']}\" {$user['ip']}\n"; + } + } + + fwrite($fd, $mpdsecret); + fclose($fd); + unset($mpdsecret); + chmod("{$g['varetc_path']}/l2tp-vpn/mpd.secret", 0600); + + vpn_netgraph_support(); + + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd4 -b -d {$g['varetc_path']}/l2tp-vpn -p {$g['varrun_path']}/l2tp-vpn.pid -s l2tps l2tps"); + + break; + + case 'redir': + break; + } + + if (platform_booting()) { + echo "done\n"; + } + + return 0; +} + +?> |