From 46bc6e545a17e77202aaf01ec0cd8d5a46567525 Mon Sep 17 00:00:00 2001 From: Renato Botelho Date: Tue, 25 Aug 2015 08:08:24 -0300 Subject: Move main pfSense content to src/ --- src/etc/inc/vpn.inc | 2056 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2056 insertions(+) create mode 100644 src/etc/inc/vpn.inc (limited to 'src/etc/inc/vpn.inc') 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 @@ +. + 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 = << $pconfig) { + if ($id == $config['ipsec']['client']['user_source'] && $pconfig['type'] == "radius") { + $strongswan .= << 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 .= <<= $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 =<< 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 = << 1) ? $pptpdcfg['radius']['server']['port'] : 1812; + $acctport = $authport + 1; + $mpdconf .=<< 1) ? $pptpdcfg['radius']['server2']['port'] : 1812; + $acctport = $authport + 1; + $mpdconf .=<< -- cgit v1.1