Project

General

Profile

Download (14 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('util.inc');
39

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

    
56

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

    
64

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

    
75

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

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

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

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

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

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

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

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

    
115
	}
116

    
117
	if ($result = openvpn_validate_cidr($post['remote_network'], 'Remote network'))
118
		$input_errors[] = $result;
119

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

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

    
172

    
173
function openvpn_validate_input_csc($post, $input_errors) {
174
	if ($result = openvpn_validate_cidr($post['ifconfig_push'], 'Interface IP'))
175
		$input_errors[] = $result;
176
}
177

    
178

    
179
// Rewrite the settings
180
function openvpn_reconfigure($mode, $id) {
181
	global $g, $config;
182

    
183
	$settings = $config['installedpackages']["openvpn$mode"]['config'][$id];
184
	if ($settings['disable']) return;
185

    
186
	$lport = 1194 + $id;
187

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

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

    
228
EOD;
229

    
230
	// Mode-specific stuff
231
	if ($mode == 'server') {
232
		list($ip, $mask) = explode('/', $settings['addresspool']);
233
		$mask = gen_subnet_mask($mask);
234

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

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

    
252
		// We can push routes
253
		if (!empty($settings['local_network'])) {
254
			list($ip, $mask) = explode('/', $settings['local_network']);
255
			$mask = gen_subnet_mask($mask);
256
			$openvpn_conf .= "push \"route $ip $mask\"\n";
257
		}
258

    
259
		// The port we'll listen at
260
		$openvpn_conf .= "lport {$settings['local_port']}\n";
261

    
262
	}
263

    
264
	else { // $mode == client
265
		// The remote server
266
		$openvpn_conf .= "remote {$settings['serveraddr']} {$settings['serverport']}\n";
267

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

    
270
		if ($settings['use_dynamicport']) $openvpn_conf .= "nobind\n";
271
			else
272
			// The port we'll listen at
273
			$openvpn_conf .= "lport {$lport}\n";
274

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

    
290
	// Add the routes if they're set
291
	if (!empty($settings['remote_network'])) {
292
		list($ip, $mask) = explode('/', $settings['remote_network']);
293
		$mask = gen_subnet_mask($mask);
294
		$openvpn_conf .= "route $ip $mask\n";
295
	}
296

    
297
	// Write the settings for the keys
298
	foreach ($keys as $key)
299
		$openvpn_conf .= $key['directive'] . ' ' . $base_file . $key['ext'] . "\n";
300

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

    
303
	if ($settings['dynamic_ip']) {
304
		$openvpn_conf .= "persist-remote-ip\n";
305
		$openvpn_conf .= "float\n";
306
	}
307

    
308
	if (!empty($settings['custom_options'])) {
309
		$options = explode(';', $settings['custom_options']);
310
		if (is_array($options)) {
311
			foreach ($options as $option)
312
				$openvpn_conf .= "$option\n";
313
		}
314
		else {
315
			$openvpn_conf .= "{$settings['custom_options']}\n";
316
		}
317
	}
318

    
319
	file_put_contents($g['varetc_path'] . "/openvpn_{$mode}{$id}.conf", $openvpn_conf);
320
}
321

    
322

    
323
function openvpn_resync_csc($id) {
324
	global $g, $config;
325

    
326
	$settings = $config['installedpackages']['openvpncsc']['config'][$id];
327

    
328
	if ($settings['disable'] == 'on') return;
329

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

    
349
	openvpn_create_cscdir();
350
	$filename = "{$g['varetc_path']}/openvpn_csc/{$settings['commonname']}";
351
	file_put_contents($filename, $conf);
352
	chown($filename, 'nobody');
353
	chgrp($filename, 'nogroup');
354
}
355

    
356

    
357
function openvpn_restart($mode, $id) {
358
	global $g, $config;
359

    
360
	$pidfile = $g['varrun_path'] . "/openvpn_{$mode}{$id}.pid";
361
	killbypid($pidfile);
362
	sleep(2);
363

    
364
	$settings = $config['installedpackages']["openvpn$mode"]['config'][$id];
365
	if ($settings['disable']) return;
366

    
367
	$configfile = $g['varetc_path'] . "/openvpn_{$mode}{$id}.conf";
368
	mwexec_bg("nohup openvpn --config $configfile");
369
	touch("{$g['tmp_path']}/filter_dirty");
370
}
371

    
372

    
373
// Resync the configuration and restart the VPN
374
function openvpn_resync($mode, $id) {
375
	openvpn_reconfigure($mode, $id);
376
	openvpn_restart($mode, $id);
377
}
378

    
379
function openvpn_create_cscdir() {
380
	global $g;
381

    
382
	$csc_dir = "{$g['varetc_path']}/openvpn_csc";
383
	if (!is_dir($csc_dir)) {
384
		mkdir($csc_dir);
385
		chown($csc_dir, 'nobody');
386
		chgrp($csc_dir, 'nobody');
387
	}
388
}
389

    
390
// Resync and restart all VPNs
391
function openvpn_resync_all() {
392
	global $config;
393

    
394
	foreach (array('server', 'client') as $mode) {
395
		if (is_array($config['installedpackages']["openvpn$mode"]['config'])) {
396
			foreach ($config['installedpackages']["openvpn$mode"]['config'] as $id => $settings)
397
				openvpn_resync($mode, $id);
398
		}
399
	}
400

    
401
	openvpn_create_cscdir();
402
	if (is_array($config['installedpackages']['openvpncsc']['config'])) {
403
		foreach ($config['installedpackages']['openvpncsc']['config'] as $id => $csc)
404
			openvpn_resync_csc($id);
405
	}
406
	
407
	/* give speedy machines time to settle */
408
	sleep(5);
409
	
410
	/* reload the filter policy */
411
	filter_configure();
412
	
413
}
414

    
415

    
416
function openvpn_print_javascript($mode) {
417
	$javascript = <<<EOD
418
<script language="JavaScript">
419
<!--
420
function onAuthMethodChanged() {
421
	var method = document.iform.auth_method;
422
	var endis = (method.options[method.selectedIndex].value == 'shared_key');
423

    
424
	document.iform.shared_key.disabled = !endis;
425
	document.iform.ca_cert.disabled = endis;
426
	document.iform.{$mode}_cert.disabled = endis;
427
	document.iform.{$mode}_key.disabled = endis;
428

    
429
EOD;
430
	if ($mode == 'server') {
431
		$javascript .= <<<EOD
432
	document.iform.dh_params.disabled = endis;
433
	document.iform.crl.disabled = endis;
434
	document.iform.nopool.disabled = endis;
435
	document.iform.local_network.disabled = endis;
436
	document.iform.client2client.disabled = endis;
437

    
438
EOD;
439
	}
440

    
441
	else { // Client mode
442
		$javascript .= "\tdocument.iform.remote_network.disabled = !endis;\n";
443
	}
444

    
445
	$javascript .= <<<EOD
446
}
447
//-->
448
</script>
449

    
450
EOD;
451
	print($javascript);
452
}
453

    
454

    
455
function openvpn_print_javascript2() {
456
	$javascript = <<<EOD
457
<script language="JavaScript">
458
<!--
459
	onAuthMethodChanged();
460
//-->
461
</script>
462

    
463
EOD;
464
	print($javascript);
465
}
466
?>
(13-13/27)