Project

General

Profile

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

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

    
226
EOD;
227

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

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

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

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

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

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

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

    
267
		if (!empty($settings['interface_ip'])) {
268
			// Configure the IPs according to the address pool
269
			list($ip, $mask) = explode('/', $settings['interface_ip']);
270
			$mask = gen_subnet_mask($mask);
271
			$baselong = ip2long($ip) & ip2long($mask);
272
			$ip1 = long2ip($baselong + 1);
273
			$ip2 = long2ip($baselong + 2);
274
			$openvpn_conf .= "ifconfig $ip2 $ip1\n";
275
		}
276
		if (isset($settings['proxy_hostname']) && $settings['proxy_hostname'] != "") {
277
			/* ;http-proxy-retry # retry on connection failures */
278
			$openvpn_conf .= "http-proxy {$settings['proxy_hostname']} {$settings['proxy_port']}\n";
279
		}
280
	}
281

    
282
	// Add the routes if they're set
283
	if (!empty($settings['remote_network'])) {
284
		list($ip, $mask) = explode('/', $settings['remote_network']);
285
		$mask = gen_subnet_mask($mask);
286
		$openvpn_conf .= "route $ip $mask\n";
287
	}
288

    
289
	// Write the settings for the keys
290
	foreach ($keys as $key)
291
		$openvpn_conf .= $key['directive'] . ' ' . $base_file . $key['ext'] . "\n";
292

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

    
295
	if ($settings['dynamic_ip']) {
296
		$openvpn_conf .= "persist-remote-ip\n";
297
		$openvpn_conf .= "float\n";
298
	}
299

    
300
	if (!empty($settings['custom_options'])) {
301
		$options = explode(';', $settings['custom_options']);
302
		if (is_array($options)) {
303
			foreach ($options as $option)
304
				$openvpn_conf .= "$option\n";
305
		}
306
		else {
307
			$openvpn_conf .= "{$settings['custom_options']}\n";
308
		}
309
	}
310

    
311
	file_put_contents($g['varetc_path'] . "/openvpn_{$mode}{$id}.conf", $openvpn_conf);
312
}
313

    
314

    
315
function openvpn_resync_csc($id) {
316
	global $g, $config;
317

    
318
	$settings = $config['installedpackages']['openvpncsc']['config'][$id];
319

    
320
	if ($settings['disable'] == 'on') return;
321

    
322
	$conf = '';
323
	if ($settings['block'] == 'on') $conf .= "disable\n";
324
	if ($settings['push_reset'] == 'on') $conf .= "push-reset\n";
325
	if (!empty($settings['ifconfig_push'])) {
326
		list($ip, $mask) = explode('/', $settings['ifconfig_push']);
327
		$baselong = ip2long($ip) & gen_subnet_mask_long($mask);
328
		$conf .= 'ifconfig-push ' . long2ip($baselong + 1) . ' ' . long2ip($baselong + 2) . "\n";
329
	}
330
	if (!empty($settings['custom_options'])) {
331
		$options = explode(';', $settings['custom_options']);
332
		if (is_array($options)) {
333
			foreach ($options as $option)
334
				$conf .= "$option\n";
335
		}
336
		else {
337
			$conf .= "{$settings['custom_options']}\n";
338
		}
339
	}
340

    
341
	openvpn_create_cscdir();
342
	$filename = "{$g['varetc_path']}/openvpn_csc/{$settings['commonname']}";
343
	file_put_contents($filename, $conf);
344
	chown($filename, 'nobody');
345
	chgrp($filename, 'nogroup');
346
}
347

    
348

    
349
function openvpn_restart($mode, $id) {
350
	global $g, $config;
351

    
352
	$pidfile = $g['varrun_path'] . "/openvpn_{$mode}{$id}.pid";
353
	killbypid($pidfile);
354
	sleep(2);
355

    
356
	$settings = $config['installedpackages']["openvpn$mode"]['config'][$id];
357
	if ($settings['disable']) return;
358

    
359
	$configfile = $g['varetc_path'] . "/openvpn_{$mode}{$id}.conf";
360
	mwexec_bg("nohup openvpn --config $configfile");
361
	touch("{$g['tmp_path']}/filter_dirty");
362
}
363

    
364

    
365
// Resync the configuration and restart the VPN
366
function openvpn_resync($mode, $id) {
367
	openvpn_reconfigure($mode, $id);
368
	openvpn_restart($mode, $id);
369
}
370

    
371
function openvpn_create_cscdir() {
372
	global $g;
373

    
374
	$csc_dir = "{$g['varetc_path']}/openvpn_csc";
375
	if (!is_dir($csc_dir)) {
376
		mkdir($csc_dir);
377
		chown($csc_dir, 'nobody');
378
		chgrp($csc_dir, 'nobody');
379
	}
380
}
381

    
382
// Resync and restart all VPNs
383
function openvpn_resync_all() {
384
	global $config;
385

    
386
	foreach (array('server', 'client') as $mode) {
387
		if (is_array($config['installedpackages']["openvpn$mode"]['config'])) {
388
			foreach ($config['installedpackages']["openvpn$mode"]['config'] as $id => $settings)
389
				openvpn_resync($mode, $id);
390
		}
391
	}
392

    
393
	openvpn_create_cscdir();
394
	if (is_array($config['installedpackages']['openvpncsc']['config'])) {
395
		foreach ($config['installedpackages']['openvpncsc']['config'] as $id => $csc)
396
			openvpn_resync_csc($id);
397
	}
398
}
399

    
400

    
401
function openvpn_print_javascript($mode) {
402
	$javascript = <<<EOD
403
<script language="JavaScript">
404
<!--
405
function onAuthMethodChanged() {
406
	var method = document.iform.auth_method;
407
	var endis = (method.options[method.selectedIndex].value == 'shared_key');
408

    
409
	document.iform.shared_key.disabled = !endis;
410
	document.iform.ca_cert.disabled = endis;
411
	document.iform.{$mode}_cert.disabled = endis;
412
	document.iform.{$mode}_key.disabled = endis;
413

    
414
EOD;
415
	if ($mode == 'server') {
416
		$javascript .= <<<EOD
417
	document.iform.dh_params.disabled = endis;
418
	document.iform.crl.disabled = endis;
419
	document.iform.nopool.disabled = endis;
420
	document.iform.local_network.disabled = endis;
421
	document.iform.client2client.disabled = endis;
422

    
423
EOD;
424
	}
425

    
426
	else { // Client mode
427
		$javascript .= "\tdocument.iform.remote_network.disabled = !endis;\n";
428
	}
429

    
430
	$javascript .= <<<EOD
431
}
432
//-->
433
</script>
434

    
435
EOD;
436
	print($javascript);
437
}
438

    
439

    
440
function openvpn_print_javascript2() {
441
	$javascript = <<<EOD
442
<script language="JavaScript">
443
<!--
444
	onAuthMethodChanged();
445
//-->
446
</script>
447

    
448
EOD;
449
	print($javascript);
450
}
451
?>
(13-13/27)