Project

General

Profile

Download (16.6 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
	/* Set protocol being used (TCP or UDP) */
240
	if ($server['psh_options']['proto'] == 'TCP') {
241
		$ovpn_config .= "--proto tcp-server ";
242
	}
243
	
244
	//trigger_error("OVPN: $ovpn_config", E_USER_NOTICE);
245
	return $ovpn_config;
246
}
247

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

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

    
344
/* Kill off a running client process */
345
function ovpn_client_kill($id) {
346
	global $g;
347
	
348
	killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid");
349
	return 0;
350
}
351

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

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

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

    
440
/******************/
441
/* Misc functions */
442

    
443
/* Calculate the last address in a range given the start and /prefix */
444
function ovpn_calc_end($start, $prefix){
445

    
446
	$first = ip2long($start);
447
	$last = pow(2,(32 - $prefix)) - 1 + $first;
448
	return long2ip($last);
449
}
450

    
451
/* Calculate a mask given a /prefix */
452
function ovpn_calc_mask($prefix){
453

    
454
	return long2ip(ip2long("255.255.255.255") - (pow( 2, (32 - $prefix)) - 1));
455
}
456

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

    
469

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

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

    
554
/* unlock configuration file */
555
function ovpn_unlock() {
556
	
557
	global $g;
558
	
559
	$lockfile = "{$g['varrun_path']}/ovpn.lock";
560
	
561
	if (file_exists($lockfile))
562
		unlink($lockfile);
563
}
564

    
565
?>
(11-11/23)