Project

General

Profile

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

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

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

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

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

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

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

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

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

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

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

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

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

    
434
/******************/
435
/* Misc functions */
436

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

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

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

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

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

    
463

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

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

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

    
559
?>
(8-8/14)