diff options
Diffstat (limited to 'etc/inc/openvpn.inc')
-rw-r--r-- | etc/inc/openvpn.inc | 299 |
1 files changed, 218 insertions, 81 deletions
diff --git a/etc/inc/openvpn.inc b/etc/inc/openvpn.inc index ae7b60a..be584e9 100644 --- a/etc/inc/openvpn.inc +++ b/etc/inc/openvpn.inc @@ -1,49 +1,41 @@ <?php -require('config.inc'); - -/* $Id$ */ -/* - openvpn.inc - part of pfSense (www.pfSense.com) - (C)2006 Fernando Lemos - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ +require_once('config.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)); + 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); - } + 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; } @@ -51,36 +43,46 @@ function openvpn_get_ciphers($pkg) { function openvpn_validate_input($mode, $post, $input_errors) { $Mode = ucfirst($mode); - $port = trim($post['port']); - if ($port && (!is_numeric($port) || ($port < 0) || ($port > 65535))) - $input_errors[] = 'The field \'Port\' should contain a valid port number, between 1 and 65536.'; - if ($mode == 'client') { - $server_port = trim($post['serverport']); - if ($server_port && (!is_numeric($server_port) || ($server_port < 0) || ($port > 65535))) - $input_errors[] = 'The field \'Server port\' should contain a valid port number, between 1 and 65536.'; - } - - $reqfields = array('local_ip', 'remote_ip'); - $reqfieldsn = array('Local IP', 'Remote IP'); - foreach($reqfields as $i => $field) { - $value = trim($post[$field]); - if ($value and (!is_ipaddr($value))) - $input_errors[] = "The field '{$reqfieldsn[$i]}' must contain a valid IP address"; + 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; } - if ($mode == 'client') { + else { // Client mode + if ($result = openvpn_validate_port($post['serverport'], 'Server port')) + $input_errors[] = $result; + $server_addr = trim($post['serveraddr']); - if ($value && !(is_domain($server_addr) || is_ipaddr($server_addr))) + 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.'; - } - $value = trim($post['ipblock']); - if ($value) { - list($ip, $mask) = explode('/', $value); - if (!is_ipaddr($ip) or !is_numeric($mask) or ($mask > 32) or ($mask < 0)) - $input_errors[] = "The field 'IP block' must contain a valid CIDR range."; + 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 ($settings['protocol'] != "TCP") + $input_errors[] = 'The protocol must be TCP to use a HTTP proxy server.'; + } + } + 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'; @@ -134,6 +136,12 @@ function openvpn_validate_input($mode, $post, $input_errors) { } +function openvpn_validate_input_csc($post, $input_errors) { + if ($result = openvpn_validate_cidr($post['ifconfig_push'], 'Interface IP')) + $input_errors[] = $result; +} + + // Rewrite the settings function openvpn_reconfigure($mode, $id) { global $g, $config; @@ -141,8 +149,8 @@ function openvpn_reconfigure($mode, $id) { $settings = $config['installedpackages']["openvpn$mode"]['config'][$id]; if ($settings['disable']) return; - // Set up the keys - // Note that the keys' extension is the directive that goes to the config file + // 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') @@ -163,13 +171,11 @@ function openvpn_reconfigure($mode, $id) { chgrp($filename, 'nobody'); } + $pidfile = $g['varrun_path'] . "/openvpn_{$mode}{$id}.pid"; $proto = ($settings['protocol'] == 'UDP' ? 'udp' : "tcp-{$mode}"); - $port = $settings['port']; - $ifconfig = $settings['local_ip'] . ' ' . $settings['remote_ip']; - list($route_ip, $route_mask) = explode('/', $settings['ipblock']); - $route_mask = gen_subnet_mask($route_mask); $cipher = $settings['crypto']; $openvpn_conf = <<<EOD +writepid $pidfile user nobody group nobody daemon @@ -179,27 +185,131 @@ persist-tun persist-key dev tun proto $proto -port $port -ifconfig $ifconfig -route $route_ip $route_mask cipher $cipher EOD; - if ($settings['auth_method'] == 'pki') - $openvpn_conf .= "tls-$mode\n"; + + // Mode-specific stuff + if ($mode == 'server') { + list($ip, $mask) = explode('/', $settings['addresspool']); + $mask = gen_subnet_mask($mask); + + // Using a shared key or not dynamically assigning IPs to the clients + if (($settings['auth_method'] == 'shared_key') || ($settings['nopool'] == 'on')) { + if ($settings['auth_method'] == 'pki') $openvpn_conf .= "tls-server\n"; + + $baselong = ip2long($ip) & ip2long($mask); + $ip1 = long2ip($baselong + 1); + $ip2 = long2ip($baselong + 2); + $openvpn_conf .= "ifconfig $ip1 $ip2\n"; + } + // Using a PKI + else if ($settings['auth_method'] == 'pki') { + if ($settings['client2client']) $openvpn_conf .= "client-to-client\n"; + $openvpn_conf .= "server $ip $mask\n"; + $csc_dir = "{$g['varetc_path']}/openvpn_csc"; + $openvpn_conf .= "client-config-dir $csc_dir\n"; + } + + // We can push routes + if (!empty($settings['local_network'])) { + list($ip, $mask) = explode('/', $settings['local_network']); + $mask = gen_subnet_mask($mask); + $openvpn_conf .= "push \"route $ip $mask\"\n"; + } + + // The port we'll listen at + $openvpn_conf .= "lport {$settings['local_port']}\n"; + } + + else { // $mode == client + // The remote server + $openvpn_conf .= "remote {$settings['serveraddr']} {$settings['serverport']}\n"; + + if ($settings['auth_method'] == 'pki') $openvpn_conf .= "client\n"; + + if (!empty($settings['interface_ip'])) { + // Configure the IPs according to the address pool + list($ip, $mask) = explode('/', $settings['interface_ip']); + $mask = gen_subnet_mask($mask); + $baselong = ip2long($ip) & ip2long($mask); + $ip1 = long2ip($baselong + 1); + $ip2 = long2ip($baselong + 2); + $openvpn_conf .= "ifconfig $ip2 $ip1\n"; + } + if (isset($settings['proxy_hostname']) && $settings['proxy_hostname'] != "") { + /* ;http-proxy-retry # retry on connection failures */ + $openvpn_conf .= "http-proxy {$settings['proxy_hostname']} {$settings['proxy_port']}\n"; + } + } + + // Add the routes if they're set + if (!empty($settings['remote_network'])) { + list($ip, $mask) = explode('/', $settings['remote_network']); + $mask = gen_subnet_mask($mask); + $openvpn_conf .= "route $ip $mask\n"; + } // Write the settings for the keys foreach ($keys as $key) $openvpn_conf .= $key['directive'] . ' ' . $base_file . $key['ext'] . "\n"; - if ($mode == 'client') $openvpn_conf .= 'remote ' . $settings['serveraddr'] . ' ' .$settings['serverport'] . "\n"; if ($settings['use_lzo']) $openvpn_conf .= "comp-lzo\n"; - if ($settings['dynamic_ip']) $openvpn_conf .= "persist-remote-ip\n"; + + if ($settings['dynamic_ip']) { + $openvpn_conf .= "persist-remote-ip\n"; + $openvpn_conf .= "float\n"; + } + + if (!empty($settings['custom_options'])) { + $options = explode(';', $settings['custom_options']); + if (is_array($options)) { + foreach ($options as $option) + $openvpn_conf .= "$option\n"; + } + else { + $openvpn_conf .= "{$settings['custom_options']}\n"; + } + } file_put_contents($g['varetc_path'] . "/openvpn_{$mode}{$id}.conf", $openvpn_conf); } +function openvpn_resync_csc($id) { + global $g, $config; + + $settings = $config['installedpackages']['openvpncsc']['config'][$id]; + + if ($settings['disable'] == 'on') return; + + $conf = ''; + if ($settings['block'] == 'on') $conf .= "disable\n"; + if ($settings['push_reset'] == 'on') $conf .= "push-reset\n"; + if (!empty($settings['ifconfig_push'])) { + list($ip, $mask) = explode('/', $settings['ifconfig_push']); + $baselong = ip2long($ip) & gen_subnet_mask_long($mask); + $conf .= 'ifconfig-push ' . long2ip($baselong + 1) . ' ' . long2ip($baselong + 2) . "\n"; + } + if (!empty($settings['custom_options'])) { + $options = explode(';', $settings['custom_options']); + if (is_array($options)) { + foreach ($options as $option) + $conf .= "$option\n"; + } + else { + $conf .= "{$settings['custom_options']}\n"; + } + } + + openvpn_create_cscdir(); + $filename = "{$g['varetc_path']}/openvpn_csc/{$settings['commonname']}"; + file_put_contents($filename, $conf); + chown($filename, 'nobody'); + chgrp($filename, 'nogroup'); +} + + function openvpn_restart($mode, $id) { global $g, $config; @@ -211,7 +321,7 @@ function openvpn_restart($mode, $id) { if ($settings['disable']) return; $configfile = $g['varetc_path'] . "/openvpn_{$mode}{$id}.conf"; - mwexec("openvpn --config $configfile --writepid $pidfile"); + mwexec("openvpn --config $configfile"); } @@ -221,6 +331,16 @@ function openvpn_resync($mode, $id) { openvpn_restart($mode, $id); } +function openvpn_create_cscdir() { + global $g; + + $csc_dir = "{$g['varetc_path']}/openvpn_csc"; + if (!is_dir($csc_dir)) { + mkdir($csc_dir); + chown($csc_dir, 'nobody'); + chgrp($csc_dir, 'nobody'); + } +} // Resync and restart all VPNs function openvpn_resync_all() { @@ -232,6 +352,12 @@ function openvpn_resync_all() { 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); + } } @@ -250,9 +376,20 @@ function onAuthMethodChanged() { EOD; if ($mode == 'server') { - $javascript .= "\tdocument.iform.dh_params.disabled = endis;\n"; - $javascript .= "\tdocument.iform.crl.disabled = endis;\n"; + $javascript .= <<<EOD + document.iform.dh_params.disabled = endis; + document.iform.crl.disabled = endis; + document.iform.nopool.disabled = endis; + document.iform.local_network.disabled = endis; + document.iform.client2client.disabled = endis; + +EOD; + } + + else { // Client mode + $javascript .= "\tdocument.iform.remote_network.disabled = !endis;\n"; } + $javascript .= <<<EOD } //--> @@ -274,4 +411,4 @@ function openvpn_print_javascript2() { EOD; print($javascript); } -?> +?>
\ No newline at end of file |