Project

General

Profile

Download (14.5 KB) Statistics
| Branch: | Tag: | Revision:
1 a93e56c5 Matthew Grooms
<?php
2
/*
3
	ipsec.inc
4
	Copyright (C) 2007 Scott Ullrich
5
	Copyright (C) 2008 Shrew Soft Inc
6
	All rights reserved.
7
8
	Parts of this code was originally based on vpn_ipsec_sad.php
9
	Copyright (C) 2003-2004 Manuel Kasper
10
11
	Redistribution and use in source and binary forms, with or without
12
	modification, are permitted provided that the following conditions are met:
13
14
	1. Redistributions of source code must retain the above copyright notice,
15
	   this list of conditions and the following disclaimer.
16
17
	2. Redistributions in binary form must reproduce the above copyright
18
	   notice, this list of conditions and the following disclaimer in the
19
	   documentation and/or other materials provided with the distribution.
20
21
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
23
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
25
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
	POSSIBILITY OF SUCH DAMAGE.
31 523855b0 Scott Ullrich
32
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/setkey
33
	pfSense_MODULE:	ipsec
34
35 a93e56c5 Matthew Grooms
*/
36
37 3462a529 Matthew Grooms
/* IPsec defines */
38
$my_identifier_list = array(
39 b1fd7536 Carlos Eduardo Ramos
	'myaddress' => array( 'desc' => gettext('My IP address'), 'mobile' => true ),
40
	'address' => array( 'desc' => gettext('IP address'), 'mobile' => true ),
41
	'fqdn' => array( 'desc' => gettext('Distinguished name'), 'mobile' => true ),
42
	'user_fqdn' => array( 'desc' => gettext('User distinguished name'), 'mobile' => true ),
43
	'asn1dn' => array( 'desc' => gettext('ASN.1 distinguished Name'), 'mobile' => true ),
44
	'keyid tag' => array( 'desc' => gettext('KeyID tag'), 'mobile' => true ),
45
	'dyn_dns' => array( 'desc' => gettext('Dynamic DNS'), 'mobile' => true ));
46 3462a529 Matthew Grooms
47
$peer_identifier_list = array(
48 b1fd7536 Carlos Eduardo Ramos
	'peeraddress' => array( 'desc' => gettext('Peer IP address'), 'mobile' => false ),
49
	'address' => array( 'desc' => gettext('IP address'), 'mobile' => false ),
50
	'fqdn' => array( 'desc' => gettext('Distinguished name'), 'mobile' => true ),
51
	'user_fqdn' => array( 'desc' => gettext('User distinguished name'), 'mobile' => true ),
52
	'asn1dn' => array( 'desc' => gettext('ASN.1 distinguished Name'), 'mobile' => true ),
53
	'keyid tag' => array( 'desc' =>gettext('KeyID tag'), 'mobile' => true ));
54 3462a529 Matthew Grooms
55
$p1_ealgos = array(
56
	'aes' => array( 'name' => 'AES', 'keysel' => array( 'lo' => 128, 'hi' => 256, 'step' => 64 ) ),
57
	'blowfish' => array( 'name' => 'Blowfish', 'keysel' => array( 'lo' => 128, 'hi' => 256, 'step' => 8 ) ),
58
	'3des' => array( 'name' => '3DES' ),
59
	'cast128' => array( 'name' => 'CAST128' ),
60
	'des' => array( 'name' => 'DES' ));
61
62
$p2_ealgos = array(
63
	'aes' => array( 'name' => 'AES', 'keysel' => array( 'lo' => 128, 'hi' => 256, 'step' => 64 ) ),
64
	'blowfish' => array( 'name' => 'Blowfish', 'keysel' => array( 'lo' => 128, 'hi' => 256, 'step' => 8 ) ),
65
	'3des' => array( 'name' => '3DES' ),
66
	'cast128' => array( 'name' => 'CAST128' ),
67
	'des' => array( 'name' => 'DES' ));
68
69
$p1_halgos = array(
70 665340db jim-p
	'md5' => 'MD5',
71 3462a529 Matthew Grooms
	'sha1' => 'SHA1',
72 665340db jim-p
	'sha256' => 'SHA256',
73
	'sha384' => 'SHA384',
74
	'sha512' => 'SHA512'
75
);
76
77
$p1_dhgroups = array(
78
	1  => '1 (768 bit)',
79
	2  => '2 (1024 bit)',
80
	5  => '5 (1536 bit)',
81
	14 => '14 (2048 bit)',
82
	15 => '15 (3072 bit)',
83
	16 => '16 (4096 bit)',
84
	17 => '17 (6144 bit)',
85
	18 => '18 (8192 bit)'
86
);
87 3462a529 Matthew Grooms
88
$p2_halgos = array(
89 665340db jim-p
	'hmac_md5' => 'MD5',
90 3462a529 Matthew Grooms
	'hmac_sha1' => 'SHA1',
91 665340db jim-p
	'hmac_sha256' => 'SHA256',
92
	'hmac_sha384' => 'SHA384',
93
	'hmac_sha512' => 'SHA512'
94
);
95 3462a529 Matthew Grooms
96
$p1_authentication_methods = array(
97
	'hybrid_rsa_server' => array( 'name' => 'Hybrid RSA + Xauth', 'mobile' => true ),
98 1703e5c5 sullrich
	'xauth_rsa_server' => array( 'name' => 'Mutual RSA + Xauth', 'mobile' => true ),
99
	'xauth_psk_server' => array( 'name' => 'Mutual PSK + Xauth', 'mobile' => true ),
100 3462a529 Matthew Grooms
	'rsasig' => array( 'name' => 'Mutual RSA', 'mobile' => false ),
101
	'pre_shared_key' => array( 'name' => 'Mutual PSK', 'mobile' => false ) );
102
103 4b96b367 mgrooms
$p2_modes = array(
104 98790f61 Seth Mos
	'tunnel' => 'Tunnel IPv4',
105
	'tunnel6' => 'Tunnel IPv6',
106 4b96b367 mgrooms
	'transport' => 'Transport');
107
108 3462a529 Matthew Grooms
$p2_protos = array(
109
	'esp' => 'ESP',
110
	'ah' => 'AH');
111
112
$p2_pfskeygroups = array(
113 665340db jim-p
	0 => 'off',
114
	1  => '1 (768 bit)',
115
	2  => '2 (1024 bit)',
116
	5  => '5 (1536 bit)',
117
	14 => '14 (2048 bit)',
118
	15 => '15 (3072 bit)',
119
	16 => '16 (4096 bit)',
120
	17 => '17 (6144 bit)',
121
	18 => '18 (8192 bit)'
122
);
123 3462a529 Matthew Grooms
124 d799787e Matthew Grooms
/*
125
 * ikeid management functions
126
 */
127
128
function ipsec_ikeid_used($ikeid) {
129
	global $config;
130
131
	foreach ($config['ipsec']['phase1'] as $ph1ent)
132
		if( $ikeid == $ph1ent['ikeid'] )
133
			return true;
134
135
	return false;
136
}
137
138
function ipsec_ikeid_next() {
139
140
	$ikeid = 1;
141
	while(ipsec_ikeid_used($ikeid))
142
		$ikeid++;
143
144
	return $ikeid;
145
}
146
147 a93e56c5 Matthew Grooms
/*
148
 * Return phase1 local address
149
 */
150
function ipsec_get_phase1_src(& $ph1ent) {
151
152 25f6730a Pierre POMES
	if ($ph1ent['interface']) {
153
		if (!is_ipaddr($ph1ent['interface'])) {
154 6dbffeda smos
			$if = get_failover_interface($ph1ent['interface']);
155 e79b24ab Seth Mos
			if($ph1ent['protocol'] == "inet6") {
156
				$interfaceip = get_interface_ipv6($if);
157
			} else {
158
				$interfaceip = get_interface_ip($if);
159
			}
160 25f6730a Pierre POMES
		} else {
161
			$interfaceip=$ph1ent['interface'];
162
		}
163 e79b24ab Seth Mos
	} else {
164 924876a8 Ermal Lu?i
		$if = "wan";
165 e79b24ab Seth Mos
		if($ph1ent['protocol'] == "inet6") {
166
			$interfaceip = get_interface_ipv6($if);
167
		} else {
168
			$interfaceip = get_interface_ip($if);
169
		}
170 25f6730a Pierre POMES
	}
171 a93e56c5 Matthew Grooms
172
	return $interfaceip;
173
}
174
175 3462a529 Matthew Grooms
/*
176
 * Return phase1 local address
177
 */
178
function ipsec_get_phase1_dst(& $ph1ent) {
179 df82fae1 smos
	global $g;
180 a6222c03 jim-p
	if (empty($ph1ent['remote-gateway']))
181 2f3554bb jim-p
		return false;
182 3462a529 Matthew Grooms
	$rg = $ph1ent['remote-gateway'];
183 33d5cb7a smos
	if (!is_ipaddr($rg)) {
184
		if(! $g['booting'])
185
			return resolve_retry($rg);
186
	}
187 0af7398a Matthew Grooms
	if(!is_ipaddr($rg))
188
		return false;
189
190 3462a529 Matthew Grooms
	return $rg;
191
}
192
193 a93e56c5 Matthew Grooms
/*
194
 * Return phase2 idinfo in cidr format
195
 */
196
function ipsec_idinfo_to_cidr(& $idinfo,$addrbits = false) {
197
	global $config;
198
199
	switch ($idinfo['type'])
200
	{
201
		case "address":
202 98790f61 Seth Mos
			if ($addrbits) {
203
				if($idinfo['mode'] == "tunnel6") {
204
					return $idinfo['address']."/128";
205
				} else {
206
					return $idinfo['address']."/32";
207
				}
208
			} else {
209 a93e56c5 Matthew Grooms
				return $idinfo['address'];
210 98790f61 Seth Mos
			}
211 a93e56c5 Matthew Grooms
		case "network":
212
			return $idinfo['address']."/".$idinfo['netbits'];
213 63017a73 Ermal Lu?i
		case "none":
214 3462a529 Matthew Grooms
		case "mobile":
215
			return "0.0.0.0/0";
216 a55e9c70 Ermal Lu?i
		default:
217 98790f61 Seth Mos
			if($idinfo['mode'] == "tunnel6") {
218
				$address = get_interface_ipv6($idinfo['type']);
219
				$netbits = get_interface_subnetv6($idinfo['type']);
220
				$address = gen_subnetv6($address,$netbits);
221
				return $address."/".$netbits;
222
			} else {
223
				$address = get_interface_ip($idinfo['type']);
224
				$netbits = get_interface_subnet($idinfo['type']);
225
				$address = gen_subnet($address,$netbits);
226
				return $address."/".$netbits;
227
			}
228
	}
229 a93e56c5 Matthew Grooms
}
230
231
/*
232
 * Return phase2 idinfo in address/netmask format
233
 */
234
function ipsec_idinfo_to_subnet(& $idinfo,$addrbits = false) {
235
	global $config;
236
237
	switch ($idinfo['type'])
238
	{
239
		case "address":
240 98790f61 Seth Mos
			if ($addrbits) {
241
				if($idinfo['mode'] == "tunnel6") {
242
					return $idinfo['address']."/128";
243
				} else {
244
					return $idinfo['address']."/255.255.255.255";
245
				}
246
			} else {
247 a93e56c5 Matthew Grooms
				return $idinfo['address'];
248 98790f61 Seth Mos
			}
249 63017a73 Ermal Lu?i
		case "none":
250 a93e56c5 Matthew Grooms
		case "network":
251
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
252 3462a529 Matthew Grooms
		case "mobile":
253
			return "0.0.0.0/0";
254 63017a73 Ermal Lu?i
		default:
255 98790f61 Seth Mos
			if($idinfo['mode'] == "tunnel6") {
256
				$address = get_interface_ipv6($idinfo['type']);
257
				$netbits = get_interface_subnetv6($idinfo['type']);
258
				$address = gen_subnetv6($address,$netbits);
259
				return $address."/".$netbits;
260
			} else {
261
				$address = get_interface_ip($idinfo['type']);
262
				$netbits = get_interface_subnet($idinfo['type']);
263
				$address = gen_subnet($address,$netbits);
264
				return $address."/".$netbits;
265
			}
266
	}
267 a93e56c5 Matthew Grooms
}
268
269
/*
270
 *  Return phase2 idinfo in text format
271
 */
272
function ipsec_idinfo_to_text(& $idinfo) {
273
274
    switch ($idinfo['type'])
275
    {
276
        case "address":
277
            return $idinfo['address'];
278
        case "network":
279
            return $idinfo['address']."/".$idinfo['netbits'];
280 63017a73 Ermal Lu?i
	case "mobile":
281 b1fd7536 Carlos Eduardo Ramos
		return gettext("Mobile Client");
282 63017a73 Ermal Lu?i
	case "none":
283 b1fd7536 Carlos Eduardo Ramos
		return gettext("None");
284 a93e56c5 Matthew Grooms
        default:
285
            return strtoupper($idinfo['type']);
286
    }
287
}
288
289
/*
290
 * Return phase1 association for phase2
291
 */
292
function ipsec_lookup_phase1(& $ph2ent,& $ph1ent)
293
{
294
    global $config;
295
    $a_phase1 = $config['ipsec']['phase1'];
296
297
    if (is_array($a_phase1) && count($a_phase1)) {
298
        foreach ($a_phase1 as $ph1tmp) {
299
            if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) {
300
                $ph1ent = $ph1tmp;
301
                return $ph1ent;
302
            }
303
        }
304
    }
305
306
    return false;
307
}
308
309
/*
310
 * Check phase1 communications status
311
 */
312
function ipsec_phase1_status(& $ph1ent) {
313
314
	$loc_ip = get_ipsec_tunnel_src($ph1ent);
315
	$rmt_ip = $ph1ent['remote-gateway'];
316
317
	if(ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
318
		return true;
319
320
	return false;
321
}
322
323
/*
324
 * Check phase2 communications status
325
 */
326
function ipsec_phase2_status(& $spd,& $sad,& $ph1ent,& $ph2ent) {
327
328
	$loc_ip = ipsec_get_phase1_src($ph1ent);
329 a55be495 jim-p
	$rmt_ip = ipsec_get_phase1_dst($ph1ent);
330 a93e56c5 Matthew Grooms
331
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true);
332 f3c338b3 Ermal
	if (!empty($ph2ent['natlocalid']))
333
		$natloc_id = ipsec_idinfo_to_cidr($ph2ent['natlocalid'],true);
334 a93e56c5 Matthew Grooms
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true);
335
336
	/* check for established SA in both directions */
337 f3c338b3 Ermal
	if( ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id)) {
338
		if (empty($ph2ent['natlocalid']) && ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$loc_id))
339
			return true;
340
		else if (!empty($ph2ent['natlocalid']) && ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$natloc_id))
341
			return true;
342
	}
343 a93e56c5 Matthew Grooms
344
	return false;
345
}
346
347
/*
348
 * Return ISAKMP SA details
349
 */
350
function ipsec_lookup_isakmp_sa($in_srcip,$in_dstip) {
351
	/* TODO : use racconctl to lookup iskamp SA */
352
	return NULL;
353
}
354
355
/*
356
 * Return IPsec SA details
357
 */
358
function ipsec_lookup_ipsec_sa(& $spd,& $sad,$dir,$in_srcip,$in_dstip,$in_srcid,$in_dstid) {
359
360
	/* match the phase1/2 to an SP */
361
362
	foreach($spd as $sp) {
363
364
		/* match direction */
365
366
		if($dir != $sp['dir'])
367
			continue;
368
369
		/* match IPs */
370
371
		if($in_srcip != $sp['src'])
372
			continue;
373
		if($in_dstip != $sp['dst'])
374
			continue;
375
376
		/* add netbits for address IDs */
377
378
		$sp_srcid = $sp['srcid'];
379
		$sp_dstid = $sp['dstid'];
380
381
		if (!strstr($sp_srcid,"/"))
382
			$sp_srcid .= '/32';
383
		if (!strstr($sp_dstid,"/"))
384
			$sp_dstid .= '/32';
385
386
		/* match IDs */
387
388
		if($in_srcid != $sp_srcid)
389
			continue;
390
		if($in_dstid != $sp_dstid)
391
			continue;
392
393
		/* match the SP to a unique SA by reqid */
394
395
		foreach($sad as $sa) {
396
397
			/* match REQIDs */
398
399
			if($sa[reqid] != $sp[reqid])
400
				continue;
401
402
			/* sanitize for NAT-T ports */
403
404
			$sa_srcip = $sa['src'];
405
			$sa_dstip = $sa['dst'];
406
407
			if (strstr($sa_srcip,"["))
408
				$sa_srcip = substr($sa_srcip,0,strcspn($sa_srcip,"["));
409
			if (strstr($sa_dstip,"["))
410
				$sa_dstip = substr($sa_dstip,0,strcspn($sa_dstip,"["));
411
412
			/* match IPs */
413
414
			if($in_srcip != $sa_srcip)
415
				continue;
416
			if($in_dstip != $sa_dstip)
417
				continue;
418
419
			return $sa;
420
		}
421
	}
422
423
	return NULL;
424
}
425
426
/*
427
 * Return dump of SPD table
428
 */
429
function ipsec_dump_spd()
430
{
431
	$fd = @popen("/usr/local/sbin/setkey -DP", "r");
432
	$spd = array();
433
	if ($fd) {
434
		while (!feof($fd)) {
435
			$line = chop(fgets($fd));
436
			if (!$line)
437
				continue;
438
			if ($line == "No SPD entries.")
439
				break;
440
			if ($line[0] != "\t") {
441
				if (is_array($cursp))
442
					$spd[] = $cursp;
443
				$cursp = array();
444
				$linea = explode(" ", $line);
445
				$cursp['srcid'] = substr($linea[0], 0, strpos($linea[0], "["));
446
				$cursp['dstid'] = substr($linea[1], 0, strpos($linea[1], "["));
447
				$i = 0;
448
			} else if (is_array($cursp)) {
449
				$linea = explode(" ", trim($line));
450
				switch($i)
451
				{
452
					case 1:
453
						if ($linea[1] == "none")	/* don't show default anti-lockout rule */
454
							unset($cursp);
455
						else
456
							$cursp['dir'] = $linea[0];
457
						break;
458
					case 2:
459
						$upperspec = explode("/", $linea[0]);
460
						$cursp['proto'] = $upperspec[0];
461
						list($cursp['src'], $cursp['dst']) = explode("-", $upperspec[2]);
462
						$cursp['reqid'] =  substr($upperspec[3], strpos($upperspec[3], "#")+1);
463
						break;
464
				}
465
			}
466
			$i++;
467
		}
468
		if (is_array($cursp) && count($cursp))
469
			$spd[] = $cursp;
470
		pclose($fd);
471
	}
472
473
	return $spd;
474
}
475
476
/*
477
 * Return dump of SAD table
478
 */
479
function ipsec_dump_sad()
480
{
481
	$fd = @popen("/usr/local/sbin/setkey -D", "r");
482
	$sad = array();
483
	if ($fd) {
484
		while (!feof($fd)) {
485
			$line = chop(fgets($fd));
486
			if (!$line)
487
				continue;
488
			if ($line == "No SAD entries.")
489
				break;
490
			if ($line[0] != "\t")
491
			{
492
				if (is_array($cursa))
493
					$sad[] = $cursa;
494
				$cursa = array();
495
				list($cursa['src'],$cursa['dst']) = explode(" ", $line);
496
				$i = 0;
497
			}
498
			else
499
			{
500
				$linea = explode(" ", trim($line));
501
				switch ($i) {
502
					case 1:
503
						$cursa['proto'] = $linea[0];
504
						$cursa['spi'] = substr($linea[2], strpos($linea[2], "x")+1, -1);
505
						$reqid = substr($linea[3], strpos($linea[3], "=")+1);
506
						$cursa['reqid'] = substr($reqid, 0, strcspn($reqid,"("));
507
						break;
508
					case 2:
509
						$cursa['ealgo'] = $linea[1];
510
						break;
511
					case 3:
512
						$cursa['aalgo'] = $linea[1];
513
						break;
514 f451ea09 jim-p
					case 8:
515
						$sadata = explode("(", $linea[1]);
516
						$cursa['data'] = $sadata[0] . " B";
517
						break;
518 a93e56c5 Matthew Grooms
				}
519
			}
520
			$i++;
521
		}
522
		if (is_array($cursa) && count($cursa))
523
			$sad[] = $cursa;
524
		pclose($fd);
525
	}
526
527
	return $sad;
528
}
529
530 6e0b68bf jim-p
/*
531
 * Return dump of mobile user list
532
 */
533
function ipsec_dump_mobile() {
534
	$command = "/usr/local/sbin/racoonctl show-users";
535
	$fd = @popen($command, "r");
536
	$mobile = array();
537
	if ($fd) {
538
		while (!feof($fd)) {
539
			$user = array();
540
			$line = chop(fgets($fd));
541
			if (!$line)
542
				continue;
543
			if ($line == "User|Source|Destination|CreatedOn|SPI")
544
				continue;
545
546
			// jim|192.168.20.243:4500|192.168.20.5:24146|2012-05-25 09:54:39|989d10e1e2d4eca4:7243830d5fd2afe7
547
			$linea = explode("|", trim($line));
548
			$user['username'] = $linea[0];
549
			$user['local'] = $linea[1];
550
			$user['remote'] = $linea[2];
551
			$user['logintime'] = $linea[3];
552
			$user['spi'] = $linea[4];
553 52ec5df8 jim-p
			if (!empty($user['username']))
554
				$mobile[] = $user;
555 6e0b68bf jim-p
		}
556
		pclose($fd);
557
	}
558
559
	return $mobile;
560
}
561
562 958420c5 jim-p
function ipsec_mobilekey_sort() {
563
	global $config;
564
565
	function mobilekeycmp($a, $b) {
566
		return strcmp($a['ident'][0], $b['ident'][0]);
567
	}
568
569
	usort($config['ipsec']['mobilekey'], "mobilekeycmp");
570
}
571
572 8f5c3d8d Pierre POMES
function ipsec_get_number_of_phase2($ikeid) {
573
	global $config;
574
    	$a_phase2 = $config['ipsec']['phase2'];
575
576
	$nbph2=0;
577
578
    	if (is_array($a_phase2) && count($a_phase2)) {
579
        	foreach ($a_phase2 as $ph2tmp) {
580
            		if ($ph2tmp['ikeid'] == $ikeid) {
581
				$nbph2++;
582
			}
583
		}
584
	}
585
586
	return $nbph2;
587
}
588
589 6e0b68bf jim-p
function ipsec_disconnect_mobile($username) {
590
	if (empty($username))
591
		return false;
592
	exec("/usr/local/sbin/racoonctl logout-user " . escapeshellarg($username));
593
}
594
595 8f5c3d8d Pierre POMES
?>