Project

General

Profile

Download (16.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/* $Id$ */
3
/*
4
	openvpn.inc
5
	
6
	Copyright (C) 2004 Peter Curran (peter@closeconsultants.com).
7
	All rights reserved.
8
	
9
	Redistribution and use in source and binary forms, with or without
10
	modification, are permitted provided that the following conditions are met:
11
	
12
	1. Redistributions of source code must retain the above copyright notice,
13
	   this list of conditions and the following disclaimer.
14
	
15
	2. Redistributions in binary form must reproduce the above copyright
16
	   notice, this list of conditions and the following disclaimer in the
17
	   documentation and/or other materials provided with the distribution.
18
	
19
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
	POSSIBILITY OF SUCH DAMAGE.
29
*/
30
	
31
/* include all configuration functions */
32
require_once("globals.inc");
33
require_once("config.inc");
34
require_once("functions.inc");
35

    
36
function ovpn_configure() {
37
	global $config;
38
	if (is_array($config['ovpn']['server']))
39
		ovpn_config_server();
40
	if (is_array($config['ovpn']['client']))
41
		ovpn_config_client();
42
	return;
43
}
44

    
45
function ovpn_link_tap() {
46
	/* Add a reference to the tap KLM.  If ref count = 1, load it */
47
	global $g;
48
	
49
	if (!is_file($g['vardb_path'] ."/ovpn_tap_link")){
50
		$link_count = 1;
51
		mwexec("/sbin/kldload if_tap");
52
		$fd = fopen($g['vardb_path'] ."/ovpn_tap_link", 'w');
53
	}
54
	else {
55
		$fd = fopen($g['vardb_path'] ."/ovpn_tap_link", 'r+');
56
		$link_count = fread($fd);
57
		$link_count ++;
58
	}
59
	fwrite($fd, $link_count);
60
	fclose($fd);
61
	return true;
62
}
63

    
64
function ovpn_unlink_tap() {
65
	/* Remove a reference to the tap KLM.  If ref count = 0, unload it */
66
	global $g;
67
	
68
	if (!is_file($g['vardb_path'] ."/ovpn_tap_link"))
69
		return false;  //no file, no links so why are we called?
70
		
71
	$fd = fopen($g['vardb_path'] ."/ovpn_tap_link", 'r+');
72
	$link_count = fread($fd);
73
	$link_count --;
74
	fwrite($fd, $link_count);
75
	fclose($fd);
76
		
77
	if ($link_count == 0)
78
		mwexec("/sbin/kldunload if_tap");
79
	return true;
80
}
81

    
82
/*****************************/	
83
/*  Server-related functions */
84

    
85
/* Configure the server */
86
function ovpn_config_server() {
87
	global $config, $g;
88
	
89
	if (isset($config['ovpn']['server']['enable'])) {
90
	
91
		if ($g['booting'])
92
			echo "Configuring OpenVPN... ";
93
		
94
		/* kill any running openvpn daemon */
95
		killbypid($g['varrun_path']."/ovpn_srv.pid");
96
		
97
		/* Remove old certs & keys */
98
		unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert.pem");
99
		unlink_if_exists("{$g['vardb_path']}/ovpn_srv_cert.pem");
100
		unlink_if_exists("{$g['vardb_path']}/ovpn_srv_key.pem");
101
		unlink_if_exists("{$g['vardb_path']}/ovpn_dh.pem");
102
		
103
		/* Copy the TLS-Server certs & keys to disk */
104
		$fd = @fopen("{$g['vardb_path']}/ovpn_ca_cert.pem", "w");
105
		if ($fd) {
106
			fwrite($fd, base64_decode($config['ovpn']['server']['ca_cert'])."\n");
107
			fclose($fd);	
108
		}
109
		$fd = @fopen("{$g['vardb_path']}/ovpn_srv_cert.pem", "w");
110
		if ($fd) {
111
			fwrite($fd, base64_decode($config['ovpn']['server']['srv_cert'])."\n");
112
			fclose($fd);	
113
		}
114
		$fd = @fopen("{$g['vardb_path']}/ovpn_srv_key.pem", "w");
115
		if ($fd) {
116
			fwrite($fd, base64_decode($config['ovpn']['server']['srv_key'])."\n");
117
			fclose($fd);	
118
		}
119
		$fd = @fopen("{$g['vardb_path']}/ovpn_dh.pem", "w");
120
		if ($fd) {
121
			fwrite($fd, base64_decode($config['ovpn']['server']['dh_param'])."\n");
122
			fclose($fd);	
123
		}
124
		
125
		/* Start the openvpn daemon */
126
		mwexec("/usr/local/sbin/openvpn " . ovpn_srv_config_generate());
127
		
128
		if ($g['booting'])
129
			/* Send the boot message */
130
			echo "done.\n";
131
	}
132
	else {
133
		if (!$g['booting']){
134
			/* stop any processes, unload the tap module */
135
			/* Remove old certs & keys */
136
			unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert.pem");
137
			unlink_if_exists("{$g['vardb_path']}/ovpn_srv_cert.pem");
138
			unlink_if_exists("{$g['vardb_path']}/ovpn_srv_key.pem");
139
			unlink_if_exists("{$g['vardb_path']}/ovpn_dh.pem");
140
			killbypid("{$g['varrun_path']}/ovpn_srv.pid");
141
			if ($config['ovpn']['server']['tun_iface'] == 'tap0')
142
				ovpn_unlink_tap();
143
		}
144
	}
145
	return 0;
146
}
147

    
148
/* Generate the config for a OpenVPN server */
149
function ovpn_srv_config_generate() {
150
	global $config, $g;
151
	$server = $config['ovpn']['server'];
152
	
153
	/* First the generic stuff:
154
		- We are a server
155
		- We are a TLS Server (for authentication)
156
		- We will run without privilege
157
	*/
158
	$ovpn_config = "--daemon --user nobody --group nobody --verb {$server['verb']} ";
159
	
160
	/* pid file */
161
	$ovpn_config .= "--writepid {$g['varrun_path']}/ovpn_srv.pid ";
162
	
163
	/* interface */
164
	$ovpn_config .= "--dev {$server['tun_iface']} ";
165
	
166
	/* port */
167
	$ovpn_config .= "--port {$server['port']} ";
168
	
169
	/* Interface binding - 1 or all */
170
	if ($server['bind_iface'] != 'all') {
171
		if ($ipaddr = ovpn_get_ip($server['bind_iface']))
172
			$ovpn_config .= "--local $ipaddr ";
173
		else
174
			return "Interface bridged";
175
		
176
	}
177
		
178
	/* Client to client routing (off by default) */
179
	if (isset($server['cli2cli']))
180
		$ovpn_config .= "--client-to-client ";
181
	
182
	/* Set maximum simultaneous clients */
183
	$ovpn_config .= "--max-clients {$server['maxcli']} ";
184
	 
185
	/* New --server macro simplifies config */
186
	$mask = ovpn_calc_mask($server['prefix']);
187
	$ovpn_config .= "--server {$server['ipblock']} {$mask} ";
188
	
189
	/* TLS-Server params */
190
	$ovpn_config .= "--ca {$g['vardb_path']}/ovpn_ca_cert.pem ";
191
	$ovpn_config .= "--cert {$g['vardb_path']}/ovpn_srv_cert.pem ";
192
	$ovpn_config .= "--key {$g['vardb_path']}/ovpn_srv_key.pem ";
193
	$ovpn_config .= "--dh {$g['vardb_path']}/ovpn_dh.pem ";
194
	
195
	/* Data channel encryption cipher*/
196
	$ovpn_config .= "--cipher {$server['crypto']} ";
197
	
198
	/* Duplicate CNs */
199
	if (isset($server['dupcn']))
200
		$ovpn_config .= "--duplicate-cn ";
201
		
202
	/* Client push - redirect gateway */
203
	if (isset($server['psh_options']['redir'])){
204
		if (isset($server['psh_options']['redir_loc']))
205
			$ovpn_config .= "--push \"redirect-gateway 'local'\" ";
206
		else
207
			$ovpn_config .= "--push \"redirect-gateway\" ";
208
	}
209
			
210
	/* Client push - route delay */
211
	if (isset($server['psh_options']['rte_delay']))
212
		$ovpn_config .= "--push \"route-delay {$server['psh_options']['rte_delay']}\" ";
213
		
214
	/* Client push - ping (note we set both server and client) */
215
	if (isset ($server['psh_options']['ping'])){
216
		$ovpn_config .= "--ping {$server['psh_options']['ping']} ";
217
		$ovpn_config .= "--push \"ping {$server['psh_options']['ping']}\" ";
218
	}
219
	
220
	/* Client push - ping-restart (note server uses 2 x client interval) */
221
	if (isset ($server['psh_options']['pingrst'])){
222
		$interval = $server['psh_options']['pingrst'];
223
		$ovpn_config .= "--ping-restart " . ($interval * 2) . " ";
224
		$ovpn_config .= "--push \"ping-restart $interval\" ";
225
	}
226
	
227
	/* Client push - ping-exit (set on client) */
228
	if (isset ($server['psh_options']['pingexit'])){
229
		$ovpn_config .= "--ping-exit {$server['psh_options']['pingexit']} ";
230
		$ovpn_config .= "--push \"ping-exit {$server['psh_options']['pingexit']}\" ";
231
	}
232
	
233
	/* Client push - inactive (set on client) */
234
	if (isset ($server['psh_options']['inact'])){
235
		$ovpn_config .= "--inactive {$server['psh_options']['pingexit']} ";
236
		$ovpn_config .= "--push \"inactive {$server['psh_options']['inact']}\" ";
237
	}
238
	
239
	//trigger_error("OVPN: $ovpn_config", E_USER_NOTICE);
240
	return $ovpn_config;
241
}
242

    
243
/* Define an OVPN Server tunnel interface in the interfaces array and assign a name */
244
function ovpn_server_iface(){
245
	global $config, $g;
246
	
247
	$i = 1;
248
	while (true) {
249
		$ifname = 'opt' . $i;
250
		if (is_array($config['interfaces'][$ifname])) {
251
			if ((isset($config['interfaces'][$ifname]['ovpn']))
252
			     && ($config['interfaces'][$ifname]['ovpn'] == 'server'))
253
				/* Already an interface defined - overwrite */
254
				break;
255
		}
256
		else {
257
			/* No existing entry, this is first unused */
258
			$config['interfaces'][$ifname] = array();
259
			break;
260
		}
261
		$i++;
262
	}
263
	$config['interfaces'][$ifname]['descr'] = "OVPN server";
264
	$config['interfaces'][$ifname]['if'] = $config['ovpn']['server']['tun_iface'];
265
	$config['interfaces'][$ifname]['ipaddr'] = long2ip( ip2long($config['ovpn']['server']['ipblock']) + 1);
266
	$config['interfaces'][$ifname]['subnet'] = $config['ovpn']['server']['prefix'];
267
	$config['interfaces'][$ifname]['enable'] = isset($config['ovpn']['server']['enable']) ? true : false;
268
	$config['interfaces'][$ifname]['ovpn'] = 'server';
269
			
270
	write_config();
271
	
272
	return "OpenVPN server interface defined";
273
}
274

    
275
/********************************************************/
276
/* Client related functions */
277
function ovpn_config_client() {
278
	/* Boot time configuration */
279
	global $config, $g;
280
	
281
	foreach ($config['ovpn']['client']['tunnel'] as $id => $client) {
282
		if (isset($client['enable'])) {
283
	
284
			if ($g['booting'])
285
				echo "Starting OpenVPN client $id... ";
286
		
287
			/* kill any running openvpn daemon */
288
			killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid");
289
		
290
			/* Remove old certs & keys */
291
			unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem");
292
			unlink_if_exists("{$g['vardb_path']}/ovpn_cli_cert_{$id}.pem");
293
			unlink_if_exists("{$g['vardb_path']}/ovpn_cli_key_{$id}.pem");
294
		
295
			/* Copy the TLS-Client certs & keys to disk */
296
			/*$fd = @fopen("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem", "w");*/
297
			$fd = fopen("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem", "w");
298
			if ($fd) {
299
				fwrite($fd, base64_decode($client['ca_cert'])."\n");
300
				fclose($fd);	
301
			}
302
			else
303
				trigger_error("OVPN: No open for CA", E_USER_NOTICE);
304
			$fd = fopen($g['vardb_path']."/ovpn_cli_cert_".$id.".pem", "w");
305
			if ($fd) {
306
				fwrite($fd, base64_decode($client['cli_cert'])."\n");
307
				fclose($fd);	
308
			}
309
			$fd = fopen($g['vardb_path']."/ovpn_cli_key_".$id.".pem", "w");
310
			if ($fd) {
311
				fwrite($fd, base64_decode($client['cli_key'])."\n");
312
				fclose($fd);	
313
			}
314
				
315
			/* Start openvpn for this client */
316
			mwexec("/usr/local/sbin/openvpn " . ovpn_cli_config_generate($id));
317
		
318
			if ($g['booting'])
319
				/* Send the boot message */
320
				echo "done.\n";
321
		}
322
		else {
323
			if (!$g['booting']){
324
				/* stop any processes, unload the tap module */
325
				/* Remove old certs & keys */
326
				unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem");
327
				unlink_if_exists("{$g['vardb_path']}/ovpn_cli_cert_{$id}.pem");
328
				unlink_if_exists("{$g['vardb_path']}/ovpn_cli_key_{$id}.pem");
329
				killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid");
330
				if ($client['type'] == "tap")
331
					ovpn_unlink_tap();
332
			}
333
		}
334
	}
335
	return 0;
336
	
337
}
338

    
339
/* Kill off a running client process */
340
function ovpn_client_kill($id) {
341
	global $g;
342
	
343
	killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid");
344
	return 0;
345
}
346

    
347
function ovpn_cli_config_generate($id) {
348
	/* configure the named client */
349
	global $config, $g;
350
	$client = $config['ovpn']['client']['tunnel'];
351
	
352
	/* Client support in 2.0 is very simple */
353
	
354
	$ovpn_config = "--client --daemon --verb 1 ";
355
	
356
	/* pid file */
357
	$ovpn_config .= "--writepid {$g['varrun_path']}/ovpn_client{$id}.pid ";
358
	
359
	/* interface */
360
	$ovpn_config .= "--dev {$client[$id]['if']} ";
361
	
362
	/* protocol */
363
	$ovpn_config .= "--proto {$client[$id]['proto']} ";
364
	
365
	/* port */
366
	$ovpn_config .= "--lport {$client[$id]['cport']} ";
367
	
368
	/* server location */
369
	$ovpn_config .= "--remote {$client[$id]['saddr']} {$client[$id]['sport']} ";
370
	
371
	/* TLS-Server params */
372
	$ovpn_config .= "--ca {$g['vardb_path']}/ovpn_ca_cert_{$id}.pem ";
373
	$ovpn_config .= "--cert {$g['vardb_path']}/ovpn_cli_cert_{$id}.pem ";
374
	$ovpn_config .= "--key {$g['vardb_path']}/ovpn_cli_key_{$id}.pem ";
375
		
376
	/* Data channel encryption cipher*/
377
	$ovpn_config .= "--cipher {$client[$id]['crypto']} ";
378
	
379
	//trigger_error("OVPN: $ovpn_config", E_USER_NOTICE);
380
	return $ovpn_config;
381
}
382

    
383
/* Define an OVPN tunnel interface in the interfaces array for each client */
384
function ovpn_client_iface(){
385
	global $config;
386
		
387
	foreach ($config['ovpn']['client']['tunnel'] as $id => $client) {
388
		if (isset($client['enable'])) {
389
			$i = 1;
390
			while (true) {
391
				$ifname = 'opt' . $i;
392
				if (is_array($config['interfaces'][$ifname])) {
393
					if ((isset($config['interfaces'][$ifname]['ovpn']))
394
			     		     && ($config['interfaces'][$ifname]['ovpn'] == "client{$id}"))
395
						/* Already an interface defined - overwrite */
396
						break;
397
				}
398
				else {
399
					/* No existing entry, this is first unused */
400
					$config['interfaces'][$ifname] = array();
401
					break;
402
				}
403
				$i++;
404
			}
405
			if (isset($client['descr']))
406
				$config['interfaces'][$ifname]['descr'] = $client['descr'];
407
			else
408
				$config['interfaces'][$ifname]['descr'] = "OVPN client-{$id}";
409
			$config['interfaces'][$ifname]['if'] = $client['if'];
410
			$config['interfaces'][$ifname]['ipaddr'] = "0.0.0.0";
411
			$config['interfaces'][$ifname]['subnet'] = "0";
412
			$config['interfaces'][$ifname]['enable'] = isset($client['enable']) ? true : false;
413
			$config['interfaces'][$ifname]['ovpn'] = "client{$id}";
414
			write_config();
415
		}
416
	}
417
	return "OpenVPN client interfaces defined";
418
}
419

    
420
/* Delete a client interface definition */
421
function ovpn_client_iface_del($id) {
422
	global $config;
423
	
424
	$i = 1;
425
	while (true) {
426
		$ifname = 'opt' . $i;
427
		if (is_array($config['interfaces'][$ifname])) {
428
			if ((isset($config['interfaces'][$ifname]['ovpn']))
429
			     && ($config['interfaces'][$ifname]['ovpn'] == "client{$id}"))
430
			     unset($config['interfaces'][$ifname]);
431
		}
432
	}
433
}
434

    
435
/******************/
436
/* Misc functions */
437

    
438
/* Calculate the last address in a range given the start and /prefix */
439
function ovpn_calc_end($start, $prefix){
440

    
441
	$first = ip2long($start);
442
	$last = pow(2,(32 - $prefix)) - 1 + $first;
443
	return long2ip($last);
444
}
445

    
446
/* Calculate a mask given a /prefix */
447
function ovpn_calc_mask($prefix){
448

    
449
	return long2ip(ip2long("255.255.255.255") - (pow( 2, (32 - $prefix)) - 1));
450
}
451

    
452
/* Read in a file from the $_FILES array */
453
function ovpn_get_file($file){
454
	global $g;
455
	
456
	if (!is_uploaded_file($_FILES[$file]['tmp_name'])){
457
		trigger_error("Bad file upload".$_FILES[$file]['error'], E_USER_NOTICE);
458
		return NULL;
459
	}
460
	$contents = file_get_contents($_FILES[$file]['tmp_name']);
461
	return $contents;
462
}
463

    
464

    
465
/* Get the IP address of a specified interface */
466
function ovpn_get_ip($iface){
467
	global $config;
468
	
469
	if ($iface == 'wan')
470
		return get_current_wan_address();
471
		
472
	if ($config['interfaces'][$iface]['bridge'])
473
		/* No bridging (yet) */
474
		return false;
475
	return $config['interfaces'][$iface]['ipaddr'];
476
}
477
	
478
/* Get a list of the cipher options supported by OpenVPN */
479
function ovpn_get_cipher_list(){
480
	
481
/*	exec("/usr/local/sbin/openvpn --show-ciphers", $raw);
482
	print_r ($raw);
483
	
484
	$ciphers = preg_grep('/ bit default key /', $raw);
485
	
486
	for($i = 0; $i <count($ciphers); $i++){
487
		$tmp = explode(' ',$ciphers[$i]);
488
		$cipher_list["$tmp[0]"] = "{$tmp[0]} ({$tmp[1]} {$tmp[2]})";
489
	}
490
*/
491
	$cipher_list = array('DES-CBC' => 'DES-CBC (64 bit)',
492
			     'RC2-CBC' => 'RC2-CBC (128 bit)',
493
			     'DES-EDE-CBC' => 'DES-EDE-CBC (128 bit)',
494
			     'DES-EDE3-CBC' => 'DES-EDE3-CBC (192 bit)',
495
			     'DESX-CBC' => 'DESX-CBC (192 bit)',
496
			     'BF-CBC' => 'BF-CBC (128 bit)',
497
			     'RC2-40-CBC' => 'RC2-40-CBC (40 bit)',
498
			     'CAST5-CBC' => 'CAST5-CBC (128 bit)',
499
			     'RC5-CBC' => 'RC5-CBC (128 bit)',
500
			     'RC2-64-CBC' => 'RC2-64-CBC (64 bit)',
501
			     'AES-128-CBC' => 'AES-128-CBC (128 bit)',
502
			     'AES-192-CBC' => 'AES-192-CBC (192 bit)',
503
			     'AES-256-CBC' => 'AES-256-CBC (256 bit)');
504
	return $cipher_list;
505
}
506
		
507
	
508
/* Build a list of the current real interfaces */
509
function ovpn_real_interface_list(){
510
	global $config;
511
	
512
	$interfaces = array('all' => 'ALL',
513
			    'lan' => 'LAN',
514
			    'wan' => 'WAN');
515
	for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
516
		if (isset($config['interfaces']['opt' . $i]['ovpn']))
517
			/* Hide our own interface */
518
			break;
519
		if (isset($config['interfaces']['opt' . $i]['enable']))
520
			$interfaces['opt' . $i] = $config['interfaces']['opt' . $i]['descr'];
521
	}
522
	return $interfaces;
523
}
524

    
525
	
526
/* lock openvpn information, decide that the lock file is stale after
527
   10 seconds */
528
function ovpn_lock() {
529
	
530
	global $g;
531
	
532
	$lockfile = "{$g['varrun_path']}/ovpn.lock";
533
	
534
	$n = 0;
535
	while ($n < 10) {
536
		/* open the lock file in append mode to avoid race condition */
537
		if ($fd = @fopen($lockfile, "x")) {
538
			/* succeeded */
539
			fclose($fd);
540
			return;
541
		} else {
542
			/* file locked, wait and try again */
543
			sleep(1);
544
			$n++;
545
		}
546
	}
547
}
548

    
549
/* unlock configuration file */
550
function ovpn_unlock() {
551
	
552
	global $g;
553
	
554
	$lockfile = "{$g['varrun_path']}/ovpn.lock";
555
	
556
	if (file_exists($lockfile))
557
		unlink($lockfile);
558
}
559

    
560
?>
(9-9/19)