Project

General

Profile

Download (14.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2

    
3
/* $Id$ */
4
/*
5
	$RCSfile$
6
	Copyright (C) 2006  Fernando Lemos
7
	All rights reserved.
8

    
9
	Copyright (C) 2005 Peter Allgeyer <allgeyer_AT_web.de>
10
	All rights reserved.
11

    
12
	Copyright (C) 2004 Peter Curran (peter@closeconsultants.com).
13
	All rights reserved.
14

    
15
	Redistribution and use in source and binary forms, with or without
16
	modification, are permitted provided that the following conditions are met:
17

    
18
	1. Redistributions of source code must retain the above copyright notices,
19
	   this list of conditions and the following disclaimer.
20

    
21
	2. Redistributions in binary form must reproduce the above copyright
22
	   notices, this list of conditions and the following disclaimer in the
23
	   documentation and/or other materials provided with the distribution.
24

    
25
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
29
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
	POSSIBILITY OF SUCH DAMAGE.
35
*/
36

    
37
require_once('config.inc');
38
require_once('pfsense-utils.inc');
39
require_once('util.inc');
40

    
41
// Return the list of ciphers OpenVPN supports
42
function openvpn_get_ciphers($pkg) {
43
		foreach ($pkg['fields']['field'] as $i => $field) {
44
			if ($field['fieldname'] == 'crypto') break;
45
		}
46
		$option_array = &$pkg['fields']['field'][$i]['options']['option'];
47
		$ciphers_out = shell_exec('openvpn --show-ciphers | grep "default key" | awk \'{print $1, "(" $2 "-" $3 ")";}\'');
48
		$ciphers = explode("\n", trim($ciphers_out));
49
		sort($ciphers);
50
		foreach ($ciphers as $cipher) {
51
			$value = explode(' ', $cipher);
52
			$value = $value[0];
53
			$option_array[] = array('value' => $value, 'name' => $cipher);
54
		}
55
}
56

    
57

    
58
function openvpn_validate_port($value, $name) {
59
	$value = trim($value);
60
	if (!empty($value) && !(is_numeric($value) && ($value > 0) && ($value < 65535)))
61
		return "The field '$name' must contain a valid port, ranging from 0 to 65535.";
62
	return false;
63
}
64

    
65

    
66
function openvpn_validate_cidr($value, $name) {
67
	$value = trim($value);
68
	if (!empty($value)) {
69
		list($ip, $mask) = explode('/', $value);
70
		if (!is_ipaddr($ip) or !is_numeric($mask) or ($mask > 32) or ($mask < 0))
71
			return "The field '$name' must contain a valid CIDR range.";
72
	}
73
	return false;
74
}
75

    
76

    
77
// Do the input validation
78
function openvpn_validate_input($mode, $post, $input_errors) {
79
	$Mode = ucfirst($mode);
80

    
81
	if ($mode == 'server') {
82
		if ($result = openvpn_validate_port($post['local_port'], 'Local port'))
83
			$input_errors[] = $result;
84

    
85
		if ($result = openvpn_validate_cidr($post['addresspool'], 'Address pool'))
86
			$input_errors[] = $result;
87

    
88
		if ($result = openvpn_validate_cidr($post['local_network'], 'Local network'))
89
			$input_errors[] = $result;
90
	}
91

    
92
	else { // Client mode
93
		if ($result = openvpn_validate_port($post['serverport'], 'Server port'))
94
			$input_errors[] = $result;
95

    
96
		$server_addr = trim($post['serveraddr']);
97
		if (!empty($value) && !(is_domain($server_addr) || is_ipaddr($server_addr)))
98
			$input_errors[] = 'The field \'Server address\' must contain a valid IP address or domain name.';
99

    
100
		if ($result = openvpn_validate_cidr($post['interface_ip'], 'Interface IP'))
101
			$input_errors[] = $result;
102

    
103
		if ($post['auth_method'] == 'shared_key') {
104
			if (empty($post['interface_ip']))
105
				$input_errors[] = 'The field \'Interface IP\' is required.';
106
		}
107
		if (isset($post['proxy_hostname']) && $post['proxy_hostname'] != "") {
108
			if (!is_domain($post['proxy_hostname']) || is_ipaddr($post['proxy_hostname']))
109
				$input_errors[] = 'The field \'Proxy Host\' must contain a valid IP address or domain name.';
110
			if (!is_port($post['proxy_port']))
111
				$input_errors[] = 'The field \'Proxy port\' must contain a valid port number.';
112
			if ($post['protocol'] != "TCP")
113
				$input_errors[] = 'The protocol must be TCP to use a HTTP proxy server.';
114
		}
115
		if (isset($post['use_shaper']) && $post['use_shaper'] != "") {
116
			if (!is_numeric($post['use_shaper']))
117
				$input_errors[] = 'The field \'Limit outgoing bandwith\' must be numeric.';
118
		}
119

    
120
	}
121

    
122
	if ($result = openvpn_validate_cidr($post['remote_network'], 'Remote network'))
123
		$input_errors[] = $result;
124

    
125
	if ($_POST['auth_method'] == 'shared_key') {
126
		$reqfields[] = 'shared_key';
127
		$reqfieldsn[] = 'Shared key';
128
	}
129
	else {
130
		$req = explode(' ', "ca_cert {$mode}_cert {$mode}_key");
131
		$reqn = array(	'CA certificate',
132
				ucfirst($mode) . ' certificate',
133
				ucfirst($mode) . ' key');
134
		$reqfields = array_merge($reqfields, $req);
135
		$reqfieldsn = array_merge($reqfieldsn, $reqn);
136
		if ($mode == 'server') {
137
			$reqfields[] = 'dh_params';
138
			$reqfieldsn[] = 'DH parameters';
139
		}
140
	}
141
	do_input_validation($post, $reqfields, $reqfieldsn, &$input_errors);
142

    
143
	$value = trim($post['shared_key']);
144
	$items = array();
145
	if ($_POST['auth_method'] == 'shared_key') {
146
		$items[] = array(	'field' => 'shared_key',
147
					'string' => 'OpenVPN Static key V1',
148
					'name' => 'Shared key');
149
	}
150
	else {
151
		$items[] = array(	'field' => 'ca_cert',
152
					'string' => 'CERTIFICATE',
153
					'name' => 'CA certificate');
154
		$items[] = array(	'field' => "{$mode}_cert",
155
					'string' => 'CERTIFICATE',
156
					'name' => "$Mode certificate");
157
		$items[] = array(	'field' => "{$mode}_key",
158
					'string' => 'RSA PRIVATE KEY',
159
					'name' => "$Mode key");
160
		if ($mode == 'server') {
161
			$items[] = array(	'field' => 'dh_params',
162
						'string' => 'DH PARAMETERS',
163
						'name' => 'DH parameters');
164
			$items[] = array(	'field' => 'crl',
165
						'string' => 'X509 CRL',
166
						'name' => 'CRL');
167
		}
168
	}
169
	foreach ($items as $item) {
170
		$value = trim($_POST[$item['field']]);
171
		$string = $item['string'];
172
		if ($value && (!strstr($value, "-----BEGIN {$string}-----") || !strstr($value, "-----END {$string}-----")))
173
			$input_errors[] = "The field '{$item['name']}' does not appear to be valid";
174
	}
175
}
176

    
177

    
178
function openvpn_validate_input_csc($post, $input_errors) {
179
	if ($result = openvpn_validate_cidr($post['ifconfig_push'], 'Interface IP'))
180
		$input_errors[] = $result;
181
}
182

    
183

    
184
// Rewrite the settings
185
function openvpn_reconfigure($mode, $id) {
186
	global $g, $config;
187

    
188
	$settings = $config['installedpackages']["openvpn$mode"]['config'][$id];
189
	if ($settings['disable']) return;
190

    
191
	$lport = 1194 + $id;
192

    
193
	// Set the keys up
194
	// Note that the keys' extension is also the directive that goes to the config file
195
	$base_file = $g['varetc_path'] . "/openvpn_{$mode}{$id}.";
196
	$keys = array();
197
	if ($settings['auth_method'] == 'shared_key')
198
		$keys[] = array('field' => 'shared_key', 'ext'  => 'secret', 'directive' => 'secret');
199
	else {
200
		$keys[] = array('field' => 'ca_cert', 'ext' => 'ca', 'directive' => 'ca');
201
		$keys[] = array('field' => "{$mode}_cert", 'ext' => 'cert', 'directive' => 'cert');
202
		$keys[] = array('field' => "{$mode}_key", 'ext' => 'key', 'directive' => 'key');
203
		if ($mode == 'server')
204
			$keys[] = array('field' => 'dh_params', 'ext' => 'dh', 'directive' => 'dh');
205
		if ($settings['crl'])
206
			$keys[] = array('field' => 'crl', 'ext' => 'crl', 'directive' => 'crl-verify');
207
	}
208
	foreach($keys as $key) {
209
		$filename = $base_file . $key['ext'];
210
		file_put_contents($filename, base64_decode($settings[$key['field']]));
211
		chown($filename, 'nobody');
212
		chgrp($filename, 'nobody');
213
	}
214

    
215
	$pidfile = $g['varrun_path'] . "/openvpn_{$mode}{$id}.pid";
216
	$proto = ($settings['protocol'] == 'UDP' ? 'udp' : "tcp-{$mode}");
217
	$cipher = $settings['crypto'];
218
	$openvpn_conf = <<<EOD
219
writepid $pidfile
220
#user nobody
221
#group nobody
222
daemon
223
keepalive 10 60
224
ping-timer-rem
225
persist-tun
226
persist-key
227
dev tun
228
proto $proto
229
cipher $cipher
230
up /etc/rc.filter_configure
231
down /etc/rc.filter_configure
232

    
233
EOD;
234

    
235
	// Mode-specific stuff
236
	if ($mode == 'server') {
237
		list($ip, $mask) = explode('/', $settings['addresspool']);
238
		$mask = gen_subnet_mask($mask);
239

    
240
		// Using a shared key or not dynamically assigning IPs to the clients
241
		if (($settings['auth_method'] == 'shared_key') || ($settings['nopool'] == 'on')) {
242
			if ($settings['auth_method'] == 'pki') $openvpn_conf .= "tls-server\n";
243

    
244
			$baselong = ip2long($ip) & ip2long($mask);
245
			$ip1 = long2ip($baselong + 1);
246
			$ip2 = long2ip($baselong + 2);
247
			$openvpn_conf .= "ifconfig $ip1 $ip2\n";
248
		}
249
		// Using a PKI
250
		else if ($settings['auth_method'] == 'pki') {
251
			if ($settings['client2client']) $openvpn_conf .= "client-to-client\n";
252
			$openvpn_conf .= "server $ip $mask\n";
253
			$csc_dir = "{$g['varetc_path']}/openvpn_csc";
254
			$openvpn_conf .= "client-config-dir $csc_dir\n";
255
		}
256

    
257
		// We can push routes
258
		if (!empty($settings['local_network'])) {
259
			list($ip, $mask) = explode('/', $settings['local_network']);
260
			$mask = gen_subnet_mask($mask);
261
			$openvpn_conf .= "push \"route $ip $mask\"\n";
262
		}
263

    
264
		// The port we'll listen at
265
		$openvpn_conf .= "lport {$settings['local_port']}\n";
266

    
267
	}
268

    
269
	else { // $mode == client
270
		// The remote server
271
		$openvpn_conf .= "remote {$settings['serveraddr']} {$settings['serverport']}\n";
272

    
273
		if ($settings['auth_method'] == 'pki') $openvpn_conf .= "client\n";
274

    
275
		if ($settings['use_dynamicport']) $openvpn_conf .= "nobind\n";
276
			else
277
			// The port we'll listen at
278
			$openvpn_conf .= "lport {$lport}\n";
279

    
280
		if (!empty($settings['use_shaper'])) $openvpn_conf .= "shaper {$settings['use_shaper']}\n";
281

    
282
		if (!empty($settings['interface_ip'])) {
283
			// Configure the IPs according to the address pool
284
			list($ip, $mask) = explode('/', $settings['interface_ip']);
285
			$mask = gen_subnet_mask($mask);
286
			$baselong = ip2long($ip) & ip2long($mask);
287
			$ip1 = long2ip($baselong + 1);
288
			$ip2 = long2ip($baselong + 2);
289
			$openvpn_conf .= "ifconfig $ip2 $ip1\n";
290
		}
291
		if (isset($settings['proxy_hostname']) && $settings['proxy_hostname'] != "") {
292
			/* ;http-proxy-retry # retry on connection failures */
293
			$openvpn_conf .= "http-proxy {$settings['proxy_hostname']} {$settings['proxy_port']}\n";
294
		}
295
	}
296

    
297
	// Add the routes if they're set
298
	if (!empty($settings['remote_network'])) {
299
		list($ip, $mask) = explode('/', $settings['remote_network']);
300
		$mask = gen_subnet_mask($mask);
301
		$openvpn_conf .= "route $ip $mask\n";
302
	}
303

    
304
	// Write the settings for the keys
305
	foreach ($keys as $key)
306
		$openvpn_conf .= $key['directive'] . ' ' . $base_file . $key['ext'] . "\n";
307

    
308
	if ($settings['use_lzo']) $openvpn_conf .= "comp-lzo\n";
309

    
310
	if ($settings['dynamic_ip']) {
311
		$openvpn_conf .= "persist-remote-ip\n";
312
		$openvpn_conf .= "float\n";
313
	}
314

    
315
	if (!empty($settings['custom_options'])) {
316
		$options = explode(';', $settings['custom_options']);
317
		if (is_array($options)) {
318
			foreach ($options as $option)
319
				$openvpn_conf .= "$option\n";
320
		}
321
		else {
322
			$openvpn_conf .= "{$settings['custom_options']}\n";
323
		}
324
	}
325

    
326
	file_put_contents($g['varetc_path'] . "/openvpn_{$mode}{$id}.conf", $openvpn_conf);
327
}
328

    
329

    
330
function openvpn_resync_csc($id) {
331
	global $g, $config;
332

    
333
	$settings = $config['installedpackages']['openvpncsc']['config'][$id];
334

    
335
	if ($settings['disable'] == 'on') return;
336

    
337
	$conf = '';
338
	if ($settings['block'] == 'on') $conf .= "disable\n";
339
	if ($settings['push_reset'] == 'on') $conf .= "push-reset\n";
340
	if (!empty($settings['ifconfig_push'])) {
341
		list($ip, $mask) = explode('/', $settings['ifconfig_push']);
342
		$baselong = ip2long($ip) & gen_subnet_mask_long($mask);
343
		$conf .= 'ifconfig-push ' . long2ip($baselong + 1) . ' ' . long2ip($baselong + 2) . "\n";
344
	}
345
	if (!empty($settings['custom_options'])) {
346
		$options = explode(';', $settings['custom_options']);
347
		if (is_array($options)) {
348
			foreach ($options as $option)
349
				$conf .= "$option\n";
350
		}
351
		else {
352
			$conf .= "{$settings['custom_options']}\n";
353
		}
354
	}
355

    
356
	$filename = "{$g['varetc_path']}/openvpn_csc/{$settings['commonname']}";
357
	file_put_contents($filename, $conf);
358
	chown($filename, 'nobody');
359
	chgrp($filename, 'nogroup');
360
}
361

    
362

    
363
function openvpn_restart($mode, $id) {
364
	global $g, $config;
365

    
366
	$pidfile = $g['varrun_path'] . "/openvpn_{$mode}{$id}.pid";
367
	killbypid($pidfile);
368
	sleep(2);
369

    
370
	$settings = $config['installedpackages']["openvpn$mode"]['config'][$id];
371
	if ($settings['disable']) return;
372

    
373
	$configfile = $g['varetc_path'] . "/openvpn_{$mode}{$id}.conf";
374
	mwexec_bg("nohup openvpn --config $configfile");
375
	touch("{$g['tmp_path']}/filter_dirty");
376
}
377

    
378

    
379
// Resync the configuration and restart the VPN
380
function openvpn_resync($mode, $id) {
381
	openvpn_reconfigure($mode, $id);
382
	openvpn_restart($mode, $id);
383
}
384

    
385
function openvpn_create_cscdir() {
386
	global $g;
387

    
388
	$csc_dir = "{$g['varetc_path']}/openvpn_csc";
389
	if (is_dir($csc_dir))
390
		rmdir_recursive($csc_dir);
391
	make_dirs($csc_dir);
392
	chown($csc_dir, 'nobody');
393
	chgrp($csc_dir, 'nobody');
394
}
395

    
396
// Resync and restart all VPNs
397
function openvpn_resync_all() {
398
	global $config;
399

    
400
	foreach (array('server', 'client') as $mode) {
401
		if (is_array($config['installedpackages']["openvpn$mode"]['config'])) {
402
			foreach ($config['installedpackages']["openvpn$mode"]['config'] as $id => $settings)
403
				openvpn_resync($mode, $id);
404
		}
405
	}
406

    
407
	openvpn_create_cscdir();
408
	if (is_array($config['installedpackages']['openvpncsc']['config'])) {
409
		foreach ($config['installedpackages']['openvpncsc']['config'] as $id => $csc)
410
			openvpn_resync_csc($id);
411
	}
412
	
413
	/* give speedy machines time to settle */
414
	sleep(5);
415
	
416
	/* reload the filter policy */
417
	filter_configure();
418
	
419
}
420

    
421

    
422
function openvpn_print_javascript($mode) {
423
	$javascript = <<<EOD
424
<script language="JavaScript">
425
<!--
426
function onAuthMethodChanged() {
427
	var method = document.iform.auth_method;
428
	var endis = (method.options[method.selectedIndex].value == 'shared_key');
429

    
430
	document.iform.shared_key.disabled = !endis;
431
	document.iform.ca_cert.disabled = endis;
432
	document.iform.{$mode}_cert.disabled = endis;
433
	document.iform.{$mode}_key.disabled = endis;
434

    
435
EOD;
436
	if ($mode == 'server') {
437
		$javascript .= <<<EOD
438
	document.iform.dh_params.disabled = endis;
439
	document.iform.crl.disabled = endis;
440
	document.iform.nopool.disabled = endis;
441
	document.iform.local_network.disabled = endis;
442
	document.iform.client2client.disabled = endis;
443

    
444
EOD;
445
	}
446

    
447
	else { // Client mode
448
		$javascript .= "\tdocument.iform.remote_network.disabled = !endis;\n";
449
	}
450

    
451
	$javascript .= <<<EOD
452
}
453
//-->
454
</script>
455

    
456
EOD;
457
	print($javascript);
458
}
459

    
460

    
461
function openvpn_print_javascript2() {
462
	$javascript = <<<EOD
463
<script language="JavaScript">
464
<!--
465
	onAuthMethodChanged();
466
//-->
467
</script>
468

    
469
EOD;
470
	print($javascript);
471
}
472
?>
(14-14/29)