Project

General

Profile

Download (13.8 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

    
224
EOD;
225

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
312

    
313
function openvpn_resync_csc($id) {
314
	global $g, $config;
315

    
316
	$settings = $config['installedpackages']['openvpncsc']['config'][$id];
317

    
318
	if ($settings['disable'] == 'on') return;
319

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

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

    
346

    
347
function openvpn_restart($mode, $id) {
348
	global $g, $config;
349

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

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

    
357
	$configfile = $g['varetc_path'] . "/openvpn_{$mode}{$id}.conf";
358
	mwexec("openvpn --down \"touch /tmp/filter_dirty\" --ipchange \"touch /tmp/filter_dirty\" --learn-address \"touch /tmp/filter_dirty\" --route-up \"touch /tmp/filter_dirty\" --up touch \"/tmp/filter_dirty\" --config $configfile");
359
	touch("{$g['tmp_path']}/filter_dirty");
360
}
361

    
362

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

    
369
function openvpn_create_cscdir() {
370
	global $g;
371

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

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

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

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

    
398

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

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

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

    
421
EOD;
422
	}
423

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

    
428
	$javascript .= <<<EOD
429
}
430
//-->
431
</script>
432

    
433
EOD;
434
	print($javascript);
435
}
436

    
437

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

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