Project

General

Profile

Download (17.4 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
// DHCP-Options logic-check
92
	if (!empty($post['dhcp_dns'])) {
93
    $servers = explode(';', $post['dhcp_dns']);
94
      foreach ($servers as $server) if (!is_ipaddr($server)) 
95
        {$input_errors[] = 'The field \'DHCP-Opt.: DNS-Server\' must contain a valid IP address and no whitespaces.';
96
        break;}}
97
	if (!empty($post['dhcp_wins'])) {
98
    $servers = explode(';', $post['dhcp_wins']);
99
      foreach ($servers as $server) if (!is_ipaddr($server)) 
100
        {$input_errors[] = 'The field \'DHCP-Opt.: WINS-Server\' must contain a valid IP address and no whitespaces.';
101
        break;}}
102
	if (!empty($post['dhcp_nbdd'])) {
103
    $servers = explode(';', $post['dhcp_nbdd']);
104
      foreach ($servers as $server) if (!is_ipaddr($server)) 
105
        {$input_errors[] = 'The field \'DHCP-Opt.: NBDD-Server\' must contain a valid IP address and no whitespaces.';
106
        break;}}
107
	if (!empty($post['dhcp_ntp'])) {
108
    $servers = explode(';', $post['dhcp_ntp']);
109
      foreach ($servers as $server) if (!is_ipaddr($server)) 
110
        {$input_errors[] = 'The field \'DHCP-Opt.: NTP-Server\' must contain a valid IP address and no whitespaces.';
111
        break;}}
112

    
113
	}
114

    
115
	else { // Client mode
116
		if ($result = openvpn_validate_port($post['serverport'], 'Server port'))
117
			$input_errors[] = $result;
118

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

    
123
		if ($result = openvpn_validate_cidr($post['interface_ip'], 'Interface IP'))
124
			$input_errors[] = $result;
125

    
126
		if ($post['auth_method'] == 'shared_key') {
127
			if (empty($post['interface_ip']))
128
				$input_errors[] = 'The field \'Interface IP\' is required.';
129
		}
130
		if (isset($post['proxy_hostname']) && $post['proxy_hostname'] != "") {
131
			if (!is_domain($post['proxy_hostname']) || is_ipaddr($post['proxy_hostname']))
132
				$input_errors[] = 'The field \'Proxy Host\' must contain a valid IP address or domain name.';
133
			if (!is_port($post['proxy_port']))
134
				$input_errors[] = 'The field \'Proxy port\' must contain a valid port number.';
135
			if ($post['protocol'] != "TCP")
136
				$input_errors[] = 'The protocol must be TCP to use a HTTP proxy server.';
137
		}
138
		if (isset($post['use_shaper']) && $post['use_shaper'] != "") {
139
			if (!is_numeric($post['use_shaper']))
140
				$input_errors[] = 'The field \'Limit outgoing bandwidth\' must be numeric.';
141
		}
142

    
143
	}
144

    
145
	if ($result = openvpn_validate_cidr($post['remote_network'], 'Remote network'))
146
		$input_errors[] = $result;
147

    
148
	if ($_POST['auth_method'] == 'shared_key') {
149
		$reqfields[] = 'shared_key';
150
		$reqfieldsn[] = 'Shared key';
151
	}
152
	else {
153
		$req = explode(' ', "ca_cert {$mode}_cert {$mode}_key");
154
		$reqn = array(	'CA certificate',
155
				ucfirst($mode) . ' certificate',
156
				ucfirst($mode) . ' key');
157
		$reqfields = array_merge($reqfields, $req);
158
		$reqfieldsn = array_merge($reqfieldsn, $reqn);
159
		if ($mode == 'server') {
160
			$reqfields[] = 'dh_params';
161
			$reqfieldsn[] = 'DH parameters';
162
		}
163
	}
164
	do_input_validation($post, $reqfields, $reqfieldsn, &$input_errors);
165

    
166
	$value = trim($post['shared_key']);
167
	$items = array();
168
	if ($_POST['auth_method'] == 'shared_key') {
169
		$items[] = array(	'field' => 'shared_key',
170
					'string' => 'OpenVPN Static key V1',
171
					'name' => 'Shared key');
172
	}
173
	else {
174
		$items[] = array(	'field' => 'ca_cert',
175
					'string' => 'CERTIFICATE',
176
					'name' => 'CA certificate');
177
		$items[] = array(	'field' => "{$mode}_cert",
178
					'string' => 'CERTIFICATE',
179
					'name' => "$Mode certificate");
180
		$items[] = array(	'field' => "{$mode}_key",
181
					'string' => 'RSA PRIVATE KEY',
182
					'name' => "$Mode key");
183
		if ($mode == 'server') {
184
			$items[] = array(	'field' => 'dh_params',
185
						'string' => 'DH PARAMETERS',
186
						'name' => 'DH parameters');
187
			$items[] = array(	'field' => 'crl',
188
						'string' => 'X509 CRL',
189
						'name' => 'CRL');
190
		}
191
	}
192
	foreach ($items as $item) {
193
		$value = trim($_POST[$item['field']]);
194
		$string = $item['string'];
195
		if ($value && (!strstr($value, "-----BEGIN {$string}-----") || !strstr($value, "-----END {$string}-----")))
196
			$input_errors[] = "The field '{$item['name']}' does not appear to be valid";
197
	}
198
}
199

    
200

    
201
function openvpn_validate_input_csc($post, $input_errors) {
202
	if ($result = openvpn_validate_cidr($post['ifconfig_push'], 'Interface IP'))
203
		$input_errors[] = $result;
204
}
205

    
206

    
207
// Rewrite the settings
208
function openvpn_reconfigure($mode, $id) {
209
	global $g, $config;
210

    
211
	$settings = $config['installedpackages']["openvpn$mode"]['config'][$id];
212
	if ($settings['disable']) return;
213

    
214
	$lport = 1194 + $id;
215

    
216
	// Set the keys up
217
	// Note that the keys' extension is also the directive that goes to the config file
218
	$base_file = $g['varetc_path'] . "/openvpn_{$mode}{$id}.";
219
	$keys = array();
220
	if ($settings['auth_method'] == 'shared_key')
221
		$keys[] = array('field' => 'shared_key', 'ext'  => 'secret', 'directive' => 'secret');
222
	else {
223
		$keys[] = array('field' => 'ca_cert', 'ext' => 'ca', 'directive' => 'ca');
224
		$keys[] = array('field' => "{$mode}_cert", 'ext' => 'cert', 'directive' => 'cert');
225
		$keys[] = array('field' => "{$mode}_key", 'ext' => 'key', 'directive' => 'key');
226
		if ($mode == 'server')
227
			$keys[] = array('field' => 'dh_params', 'ext' => 'dh', 'directive' => 'dh');
228
		if ($settings['crl'])
229
			$keys[] = array('field' => 'crl', 'ext' => 'crl', 'directive' => 'crl-verify');
230
	}
231
	foreach($keys as $key) {
232
		$filename = $base_file . $key['ext'];
233
		file_put_contents($filename, base64_decode($settings[$key['field']]));
234
		chown($filename, 'nobody');
235
		chgrp($filename, 'nobody');
236
	}
237

    
238
	$pidfile = $g['varrun_path'] . "/openvpn_{$mode}{$id}.pid";
239
	$proto = ($settings['protocol'] == 'UDP' ? 'udp' : "tcp-{$mode}");
240
	$cipher = $settings['crypto'];
241
	$openvpn_conf = <<<EOD
242
writepid $pidfile
243
#user nobody
244
#group nobody
245
daemon
246
keepalive 10 60
247
ping-timer-rem
248
persist-tun
249
persist-key
250
dev tun
251
proto $proto
252
cipher $cipher
253
up /etc/rc.filter_configure
254
down /etc/rc.filter_configure
255

    
256
EOD;
257

    
258
	// Mode-specific stuff
259
	if ($mode == 'server') {
260
		list($ip, $mask) = explode('/', $settings['addresspool']);
261
		$mask = gen_subnet_mask($mask);
262

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

    
267
			$baselong = ip2long($ip) & ip2long($mask);
268
			$ip1 = long2ip($baselong + 1);
269
			$ip2 = long2ip($baselong + 2);
270
			$openvpn_conf .= "ifconfig $ip1 $ip2\n";
271
		}
272
		// Using a PKI
273
		else if ($settings['auth_method'] == 'pki') {
274
			if ($settings['client2client']) $openvpn_conf .= "client-to-client\n";
275
			$openvpn_conf .= "server $ip $mask\n";
276
			$csc_dir = "{$g['varetc_path']}/openvpn_csc";
277
			$openvpn_conf .= "client-config-dir $csc_dir\n";
278
		}
279

    
280
		// We can push routes
281
		if (!empty($settings['local_network'])) {
282
			list($ip, $mask) = explode('/', $settings['local_network']);
283
			$mask = gen_subnet_mask($mask);
284
			$openvpn_conf .= "push \"route $ip $mask\"\n";
285
		}
286

    
287
		// The port we'll listen at
288
		$openvpn_conf .= "lport {$settings['local_port']}\n";
289

    
290
		// DHCP-Options
291
	  if (!empty($settings['dhcp_domainname'])) $openvpn_conf .= "push \"dhcp-option DOMAIN {$settings['dhcp_domainname']}\"\n";
292
	      	
293
    if (!empty($settings['dhcp_dns'])) {
294
	   	$servers = explode(';', $settings['dhcp_dns']);
295
   	  if (is_array($servers)) {
296
			 foreach ($servers as $server) $openvpn_conf .= "push \"dhcp-option DNS {$server}\"\n";
297
  	  }
298
		  else {
299
			$openvpn_conf .= "push \"dhcp-option DNS {$settings['dhcp_dns']}\"\n";
300
		  }
301
    }
302
  
303
    if (!empty($settings['dhcp_wins'])) {
304
	   	$servers = explode(';', $settings['dhcp_wins']);
305
   	  if (is_array($servers)) {
306
			 foreach ($servers as $server) $openvpn_conf .= "push \"dhcp-option WINS {$server}\"\n";
307
  	  }
308
		  else {
309
			$openvpn_conf .= "push \"dhcp-option WINS {$settings['dhcp_wins']}\"\n";
310
		  }
311
    }	
312

    
313
    if (!empty($settings['dhcp_nbdd'])) {
314
	   	$servers = explode(';', $settings['dhcp_nbdd']);
315
   	  if (is_array($servers)) {
316
			 foreach ($servers as $server) $openvpn_conf .= "push \"dhcp-option NBDD {$server}\"\n";
317
  	  }
318
		  else {
319
			$openvpn_conf .= "push \"dhcp-option NBDD {$settings['dhcp_nbdd']}\"\n";
320
		  }
321
    }	
322

    
323
    if (!empty($settings['dhcp_ntp'])) {
324
	   	$servers = explode(';', $settings['dhcp_ntp']);
325
   	  if (is_array($servers)) {
326
			 foreach ($servers as $server) $openvpn_conf .= "push \"dhcp-option NTP {$server}\"\n";
327
  	  }
328
		  else {
329
			$openvpn_conf .= "push \"dhcp-option NTP {$settings['dhcp_ntp']}\"\n";
330
		  }
331
    }	
332

    
333
  	if (!empty($settings['dhcp_nbttype']) && $settings['dhcp_nbttype'] !=0) $openvpn_conf .= "push \"dhcp-option NBT {$settings['dhcp_nbttype']}\"\n";
334
    if (!empty($settings['dhcp_nbtscope'])) $openvpn_conf .= "push \"dhcp-option NBS {$settings['dhcp_nbtscope']}\"\n";
335
   	if ($settings['dhcp_nbtdisable']) $openvpn_conf .= "push \"dhcp-option DISABLE-NBT\"\n";
336

    
337
	}
338

    
339
	else { // $mode == client
340
		// The remote server
341
		$openvpn_conf .= "remote {$settings['serveraddr']} {$settings['serverport']}\n";
342

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

    
345
		if ($settings['use_dynamicport']) $openvpn_conf .= "nobind\n";
346
			else
347
			// The port we'll listen at
348
			$openvpn_conf .= "lport {$lport}\n";
349

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

    
352
		if (!empty($settings['interface_ip'])) {
353
			// Configure the IPs according to the address pool
354
			list($ip, $mask) = explode('/', $settings['interface_ip']);
355
			$mask = gen_subnet_mask($mask);
356
			$baselong = ip2long($ip) & ip2long($mask);
357
			$ip1 = long2ip($baselong + 1);
358
			$ip2 = long2ip($baselong + 2);
359
			$openvpn_conf .= "ifconfig $ip2 $ip1\n";
360
		}
361
		if (isset($settings['proxy_hostname']) && $settings['proxy_hostname'] != "") {
362
			/* ;http-proxy-retry # retry on connection failures */
363
			$openvpn_conf .= "http-proxy {$settings['proxy_hostname']} {$settings['proxy_port']}\n";
364
		}
365
	}
366

    
367
	// Add the routes if they're set
368
	if (!empty($settings['remote_network'])) {
369
		list($ip, $mask) = explode('/', $settings['remote_network']);
370
		$mask = gen_subnet_mask($mask);
371
		$openvpn_conf .= "route $ip $mask\n";
372
	}
373

    
374
	// Write the settings for the keys
375
	foreach ($keys as $key)
376
		$openvpn_conf .= $key['directive'] . ' ' . $base_file . $key['ext'] . "\n";
377

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

    
380
	if ($settings['dynamic_ip']) {
381
		$openvpn_conf .= "persist-remote-ip\n";
382
		$openvpn_conf .= "float\n";
383
	}
384

    
385
	if (!empty($settings['custom_options'])) {
386
		$options = explode(';', $settings['custom_options']);
387
		if (is_array($options)) {
388
			foreach ($options as $option)
389
				$openvpn_conf .= "$option\n";
390
		}
391
		else {
392
			$openvpn_conf .= "{$settings['custom_options']}\n";
393
		}
394
	}
395

    
396
	file_put_contents($g['varetc_path'] . "/openvpn_{$mode}{$id}.conf", $openvpn_conf);
397
}
398

    
399

    
400
function openvpn_resync_csc($id) {
401
	global $g, $config;
402

    
403
	$settings = $config['installedpackages']['openvpncsc']['config'][$id];
404

    
405
	if ($settings['disable'] == 'on') {
406
		$filename = "{$g['varetc_path']}/openvpn_csc/{$settings['commonname']}";
407
		unlink_if_exists($filename);
408
		return;
409
	}
410

    
411
	$conf = '';
412
	if ($settings['block'] == 'on') $conf .= "disable\n";
413
	if ($settings['push_reset'] == 'on') $conf .= "push-reset\n";
414
	if (!empty($settings['ifconfig_push'])) {
415
		list($ip, $mask) = explode('/', $settings['ifconfig_push']);
416
		$baselong = ip2long($ip) & gen_subnet_mask_long($mask);
417
		$conf .= 'ifconfig-push ' . long2ip($baselong + 1) . ' ' . long2ip($baselong + 2) . "\n";
418
	}
419
	if (!empty($settings['custom_options'])) {
420
		$options = explode(';', $settings['custom_options']);
421
		if (is_array($options)) {
422
			foreach ($options as $option)
423
				$conf .= "$option\n";
424
		}
425
		else {
426
			$conf .= "{$settings['custom_options']}\n";
427
		}
428
	}
429

    
430
	$filename = "{$g['varetc_path']}/openvpn_csc/{$settings['commonname']}";
431
	file_put_contents($filename, $conf);
432
	chown($filename, 'nobody');
433
	chgrp($filename, 'nogroup');
434
}
435

    
436

    
437
function openvpn_restart($mode, $id) {
438
	global $g, $config;
439

    
440
	$pidfile = $g['varrun_path'] . "/openvpn_{$mode}{$id}.pid";
441
	killbypid($pidfile);
442
	sleep(2);
443

    
444
	$settings = $config['installedpackages']["openvpn$mode"]['config'][$id];
445
	if ($settings['disable']) return;
446

    
447
	$configfile = $g['varetc_path'] . "/openvpn_{$mode}{$id}.conf";
448
	mwexec_bg("nohup openvpn --config $configfile");
449
	touch("{$g['tmp_path']}/filter_dirty");
450
}
451

    
452

    
453
// Resync the configuration and restart the VPN
454
function openvpn_resync($mode, $id) {
455
	openvpn_reconfigure($mode, $id);
456
	openvpn_restart($mode, $id);
457
}
458

    
459
function openvpn_create_cscdir() {
460
	global $g;
461

    
462
	$csc_dir = "{$g['varetc_path']}/openvpn_csc";
463
	if (is_dir($csc_dir))
464
		rmdir_recursive($csc_dir);
465
	make_dirs($csc_dir);
466
	chown($csc_dir, 'nobody');
467
	chgrp($csc_dir, 'nobody');
468
}
469

    
470
// Resync and restart all VPNs
471
function openvpn_resync_all() {
472
	global $config;
473

    
474
	$started = false;
475

    
476
	foreach (array('server', 'client') as $mode) {
477
		if (is_array($config['installedpackages']["openvpn$mode"]['config'])) {
478
			foreach ($config['installedpackages']["openvpn$mode"]['config'] as $id => $settings)
479
				openvpn_resync($mode, $id);
480
			$started = true;
481
		}
482
	}
483

    
484
	openvpn_create_cscdir();
485
	if (is_array($config['installedpackages']['openvpncsc']['config'])) {
486
		foreach ($config['installedpackages']['openvpncsc']['config'] as $id => $csc)
487
			openvpn_resync_csc($id);
488
		$started = true;
489
	}
490
	
491
	/* give speedy machines time to settle */
492
	if($started) {
493
		sleep(5);
494
		/* reload the filter policy */
495
		filter_configure();
496
	}
497
	
498
}
499

    
500

    
501
function openvpn_print_javascript($mode) {
502
	$javascript = <<<EOD
503
<script language="JavaScript">
504
<!--
505
function onAuthMethodChanged() {
506
	var method = document.iform.auth_method;
507
	var endis = (method.options[method.selectedIndex].value == 'shared_key');
508

    
509
	document.iform.shared_key.disabled = !endis;
510
	document.iform.ca_cert.disabled = endis;
511
	document.iform.{$mode}_cert.disabled = endis;
512
	document.iform.{$mode}_key.disabled = endis;
513

    
514
EOD;
515
	if ($mode == 'server') {
516
		$javascript .= <<<EOD
517
	document.iform.dh_params.disabled = endis;
518
	document.iform.crl.disabled = endis;
519
	document.iform.nopool.disabled = endis;
520
	document.iform.local_network.disabled = endis;
521
	document.iform.client2client.disabled = endis;
522

    
523
EOD;
524
	}
525

    
526
	else { // Client mode
527
		$javascript .= "\tdocument.iform.remote_network.disabled = !endis;\n";
528
	}
529

    
530
	$javascript .= <<<EOD
531
}
532
//-->
533
</script>
534

    
535
EOD;
536
	print($javascript);
537
}
538

    
539

    
540
function openvpn_print_javascript2() {
541
	$javascript = <<<EOD
542
<script language="JavaScript">
543
<!--
544
	onAuthMethodChanged();
545
//-->
546
</script>
547

    
548
EOD;
549
	print($javascript);
550
}
551
?>
(13-13/27)