All rights reserved. Copyright (C) 2004 Peter Curran (peter@closeconsultants.com). 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 notices, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notices, 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('config.inc'); require_once('pfsense-utils.inc'); require_once('util.inc'); // Return the list of ciphers OpenVPN supports function openvpn_get_ciphers($pkg) { foreach ($pkg['fields']['field'] as $i => $field) { if ($field['fieldname'] == 'crypto') break; } $option_array = &$pkg['fields']['field'][$i]['options']['option']; $ciphers_out = shell_exec('openvpn --show-ciphers | grep "default key" | awk \'{print $1, "(" $2 "-" $3 ")";}\''); $ciphers = explode("\n", trim($ciphers_out)); sort($ciphers); foreach ($ciphers as $cipher) { $value = explode(' ', $cipher); $value = $value[0]; $option_array[] = array('value' => $value, 'name' => $cipher); } } function openvpn_validate_port($value, $name) { $value = trim($value); if (!empty($value) && !(is_numeric($value) && ($value > 0) && ($value < 65535))) return "The field '$name' must contain a valid port, ranging from 0 to 65535."; return false; } function openvpn_validate_cidr($value, $name) { $value = trim($value); if (!empty($value)) { list($ip, $mask) = explode('/', $value); if (!is_ipaddr($ip) or !is_numeric($mask) or ($mask > 32) or ($mask < 0)) return "The field '$name' must contain a valid CIDR range."; } return false; } // Do the input validation function openvpn_validate_input($mode, $post, $input_errors) { $Mode = ucfirst($mode); if ($mode == 'server') { if ($result = openvpn_validate_port($post['local_port'], 'Local port')) $input_errors[] = $result; if ($result = openvpn_validate_cidr($post['addresspool'], 'Address pool')) $input_errors[] = $result; if ($result = openvpn_validate_cidr($post['local_network'], 'Local network')) $input_errors[] = $result; /* check for port in use - update of existing entries not possible because $_GET['act'] is not passed from pkg_edit.php :-( mfuchs $portinuse = shell_exec('sockstat | grep '.$post['local_port'].' | grep '.strtolower($post['protocol'])); if (!empty($portinuse)) $input_errors[] = 'The port '.$post['local_port'].'/'.strtolower($post['protocol']).' is already in use.'; */ if (!empty($post['dhcp_dns'])) { $servers = explode(';', $post['dhcp_dns']); foreach ($servers as $server) if (!is_ipaddr($server)) {$input_errors[] = 'The field \'DHCP-Opt.: DNS-Server\' must contain a valid IP address and no whitespaces.'; break;}} if (!empty($post['dhcp_wins'])) { $servers = explode(';', $post['dhcp_wins']); foreach ($servers as $server) if (!is_ipaddr($server)) {$input_errors[] = 'The field \'DHCP-Opt.: WINS-Server\' must contain a valid IP address and no whitespaces.'; break;}} if (!empty($post['dhcp_nbdd'])) { $servers = explode(';', $post['dhcp_nbdd']); foreach ($servers as $server) if (!is_ipaddr($server)) {$input_errors[] = 'The field \'DHCP-Opt.: NBDD-Server\' must contain a valid IP address and no whitespaces.'; break;}} if (!empty($post['dhcp_ntp'])) { $servers = explode(';', $post['dhcp_ntp']); foreach ($servers as $server) if (!is_ipaddr($server)) {$input_errors[] = 'The field \'DHCP-Opt.: NTP-Server\' must contain a valid IP address and no whitespaces.'; break;}} if (isset($post['maxclients']) && $post['maxclients'] != "") { if (!is_numeric($post['maxclients'])) $input_errors[] = 'The field \'Maximum clients\' must be numeric.'; } } else { // Client mode if ($result = openvpn_validate_port($post['serverport'], 'Server port')) $input_errors[] = $result; $server_addr = trim($post['serveraddr']); if (!empty($value) && !(is_domain($server_addr) || is_ipaddr($server_addr))) $input_errors[] = 'The field \'Server address\' must contain a valid IP address or domain name.'; if ($result = openvpn_validate_cidr($post['interface_ip'], 'Interface IP')) $input_errors[] = $result; if ($post['auth_method'] == 'shared_key') { if (empty($post['interface_ip'])) $input_errors[] = 'The field \'Interface IP\' is required.'; } if (isset($post['proxy_hostname']) && $post['proxy_hostname'] != "") { if (!is_domain($post['proxy_hostname']) || is_ipaddr($post['proxy_hostname'])) $input_errors[] = 'The field \'Proxy Host\' must contain a valid IP address or domain name.'; if (!is_port($post['proxy_port'])) $input_errors[] = 'The field \'Proxy port\' must contain a valid port number.'; if ($post['protocol'] != "TCP") $input_errors[] = 'The protocol must be TCP to use a HTTP proxy server.'; } if (isset($post['use_shaper']) && $post['use_shaper'] != "") { if (!is_numeric($post['use_shaper'])) $input_errors[] = 'The field \'Limit outgoing bandwidth\' must be numeric.'; } } if ($result = openvpn_validate_cidr($post['remote_network'], 'Remote network')) $input_errors[] = $result; if ($_POST['auth_method'] == 'shared_key') { $reqfields[] = 'shared_key'; $reqfieldsn[] = 'Shared key'; } else { $req = explode(' ', "ca_cert {$mode}_cert {$mode}_key"); $reqn = array( 'CA certificate', ucfirst($mode) . ' certificate', ucfirst($mode) . ' key'); $reqfields = array_merge($reqfields, $req); $reqfieldsn = array_merge($reqfieldsn, $reqn); if ($mode == 'server') { $reqfields[] = 'dh_params'; $reqfieldsn[] = 'DH parameters'; } } do_input_validation($post, $reqfields, $reqfieldsn, &$input_errors); $value = trim($post['shared_key']); $items = array(); if ($_POST['auth_method'] == 'shared_key') { $items[] = array( 'field' => 'shared_key', 'string' => 'OpenVPN Static key V1', 'name' => 'Shared key'); } else { $items[] = array( 'field' => 'ca_cert', 'string' => 'CERTIFICATE', 'name' => 'CA certificate'); $items[] = array( 'field' => "{$mode}_cert", 'string' => 'CERTIFICATE', 'name' => "$Mode certificate"); $items[] = array( 'field' => "{$mode}_key", 'string' => 'RSA PRIVATE KEY', 'name' => "$Mode key"); $items[] = array( 'field' => 'tls', 'string' => 'OpenVPN Static key V1', 'name' => 'TLS'); if ($mode == 'server') { $items[] = array( 'field' => 'dh_params', 'string' => 'DH PARAMETERS', 'name' => 'DH parameters'); $items[] = array( 'field' => 'crl', 'string' => 'X509 CRL', 'name' => 'CRL'); } } foreach ($items as $item) { $value = trim($_POST[$item['field']]); $string = $item['string']; if ($value && (!strstr($value, "-----BEGIN {$string}-----") || !strstr($value, "-----END {$string}-----"))) $input_errors[] = "The field '{$item['name']}' does not appear to be valid"; } } function openvpn_validate_input_csc($post, $input_errors) { if ($result = openvpn_validate_cidr($post['ifconfig_push'], 'Interface IP')) $input_errors[] = $result; if ($post['push_reset'] != 'on') { if (!empty($post['dhcp_domainname'])) $input_errors[] = 'It makes no sense to unselect push reset and configure dhcp-options'; elseif (!empty($post['dhcp_dns'])) $input_errors[] = 'It makes no sense to unselect push reset and configure dhcp-options'; elseif (!empty($post['dhcp_wins'])) $input_errors[] = 'It makes no sense to unselect push reset and configure dhcp-options'; elseif (!empty($post['dhcp_nbdd'])) $input_errors[] = 'It makes no sense to unselect push reset and configure dhcp-options'; elseif (!empty($post['dhcp_ntp'])) $input_errors[] = 'It makes no sense to unselect push reset and configure dhcp-options'; elseif ($post['dhcp_nbttype']) $input_errors[] = 'It makes no sense to unselect push reset and configure dhcp-options'; elseif (!empty($post['dhcp_nbtscope'])) $input_errors[] = 'It makes no sense to unselect push reset and configure dhcp-options'; elseif ($post['dhcp_nbtdisable']) $input_errors[] = 'It makes no sense to unselect push reset and configure dhcp-options'; } else { if (!empty($post['dhcp_dns'])) { $servers = explode(';', $post['dhcp_dns']); foreach ($servers as $server) if (!is_ipaddr($server)) {$input_errors[] = 'The field \'DHCP-Opt.: DNS-Server\' must contain a valid IP address and no whitespaces.'; break;}} if (!empty($post['dhcp_wins'])) { $servers = explode(';', $post['dhcp_wins']); foreach ($servers as $server) if (!is_ipaddr($server)) {$input_errors[] = 'The field \'DHCP-Opt.: WINS-Server\' must contain a valid IP address and no whitespaces.'; break;}} if (!empty($post['dhcp_nbdd'])) { $servers = explode(';', $post['dhcp_nbdd']); foreach ($servers as $server) if (!is_ipaddr($server)) {$input_errors[] = 'The field \'DHCP-Opt.: NBDD-Server\' must contain a valid IP address and no whitespaces.'; break;}} if (!empty($post['dhcp_ntp'])) { $servers = explode(';', $post['dhcp_ntp']); foreach ($servers as $server) if (!is_ipaddr($server)) {$input_errors[] = 'The field \'DHCP-Opt.: NTP-Server\' must contain a valid IP address and no whitespaces.'; break;}} }} // Rewrite the settings function openvpn_reconfigure($mode, $id) { global $g, $config; $settings = $config['installedpackages']["openvpn$mode"]['config'][$id]; if ($settings['disable']) return; $lport = 1194 + $id; // Set the keys up // Note that the keys' extension is also the directive that goes to the config file $base_file = $g['varetc_path'] . "/openvpn_{$mode}{$id}."; $keys = array(); if ($settings['auth_method'] == 'shared_key') $keys[] = array('field' => 'shared_key', 'ext' => 'secret', 'directive' => 'secret'); else { $keys[] = array('field' => 'ca_cert', 'ext' => 'ca', 'directive' => 'ca'); $keys[] = array('field' => "{$mode}_cert", 'ext' => 'cert', 'directive' => 'cert'); $keys[] = array('field' => "{$mode}_key", 'ext' => 'key', 'directive' => 'key'); if ($mode == 'server') $keys[] = array('field' => 'dh_params', 'ext' => 'dh', 'directive' => 'dh'); if ($settings['crl']) $keys[] = array('field' => 'crl', 'ext' => 'crl', 'directive' => 'crl-verify'); if ($settings['tls']) $keys[] = array('field' => 'tls', 'ext' => 'tls', 'directive' => 'tls-auth'); } foreach($keys as $key) { $filename = $base_file . $key['ext']; file_put_contents($filename, base64_decode($settings[$key['field']])); chown($filename, 'nobody'); chgrp($filename, 'nobody'); } $pidfile = $g['varrun_path'] . "/openvpn_{$mode}{$id}.pid"; $proto = ($settings['protocol'] == 'UDP' ? 'udp' : "tcp-{$mode}"); $cipher = $settings['crypto']; $openvpn_conf = << $settings) openvpn_resync($mode, $id); } } openvpn_create_cscdir(); if (is_array($config['installedpackages']['openvpncsc']['config'])) { foreach ($config['installedpackages']['openvpncsc']['config'] as $id => $csc) openvpn_resync_csc($id); } /* give speedy machines time to settle */ sleep(5); /* reload the filter policy */ filter_configure(); } function openvpn_print_javascript($mode) { $javascript = << EOD; print($javascript); } function openvpn_print_javascript2() { $javascript = << EOD; print($javascript); } ?>