Project

General

Profile

Download (15.7 KB) Statistics
| Branch: | Tag: | Revision:
1
<?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

    
32
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/setkey
33
	pfSense_MODULE:	ipsec
34

    
35
*/
36

    
37
/* IPsec defines */
38
$my_identifier_list = array(
39
	'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

    
47
$peer_identifier_list = array(
48
	'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

    
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
	'md5' => 'MD5',
71
	'sha1' => 'SHA1',
72
	'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

    
88
$p2_halgos = array(
89
	'hmac_md5' => 'MD5',
90
	'hmac_sha1' => 'SHA1',
91
	'hmac_sha256' => 'SHA256',
92
	'hmac_sha384' => 'SHA384',
93
	'hmac_sha512' => 'SHA512'
94
);
95

    
96
$p1_authentication_methods = array(
97
	'hybrid_rsa_server' => array( 'name' => 'Hybrid RSA + Xauth', 'mobile' => true ),
98
	'xauth_rsa_server' => array( 'name' => 'Mutual RSA + Xauth', 'mobile' => true ),
99
	'xauth_psk_server' => array( 'name' => 'Mutual PSK + Xauth', 'mobile' => true ),
100
	'rsasig' => array( 'name' => 'Mutual RSA', 'mobile' => false ),
101
	'pre_shared_key' => array( 'name' => 'Mutual PSK', 'mobile' => false ) );
102

    
103
$p2_modes = array(
104
	'tunnel' => 'Tunnel IPv4',
105
	'tunnel6' => 'Tunnel IPv6',
106
	'transport' => 'Transport');
107

    
108
$p2_protos = array(
109
	'esp' => 'ESP',
110
	'ah' => 'AH');
111

    
112
$p2_pfskeygroups = array(
113
	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

    
124
/*
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
/*
148
 * Return phase1 local address
149
 */
150
function ipsec_get_phase1_src(& $ph1ent) {
151

    
152
	if ($ph1ent['interface']) {
153
		if (!is_ipaddr($ph1ent['interface'])) {
154
			if ($ph1ent['protocol'] == "inet6") { 
155
				$if = get_failover_interface($ph1ent['interface'], "inet6");
156
				$interfaceip = get_interface_ipv6($if);
157
			} else {
158
				$if = get_failover_interface($ph1ent['interface']);
159
				$interfaceip = get_interface_ip($if);
160
			}
161
		} else {
162
			$interfaceip=$ph1ent['interface'];
163
		}
164
	} else {
165
		$if = "wan";
166
		if ($ph1ent['protocol'] == "inet6")
167
			$interfaceip = get_interface_ipv6($if);
168
		else
169
			$interfaceip = get_interface_ip($if);
170
	}
171

    
172
	return $interfaceip;
173
}
174

    
175
/*
176
 * Return phase1 local address
177
 */
178
function ipsec_get_phase1_dst(& $ph1ent) {
179
	global $g;
180

    
181
	if (empty($ph1ent['remote-gateway']))
182
		return false;
183
	$rg = $ph1ent['remote-gateway'];
184
	if (!is_ipaddr($rg)) {
185
		if(! $g['booting'])
186
			return resolve_retry($rg);
187
	}
188
	if(!is_ipaddr($rg))
189
		return false;
190

    
191
	return $rg;
192
}
193

    
194
/*
195
 * Return phase2 idinfo in cidr format
196
 */
197
function ipsec_idinfo_to_cidr(& $idinfo, $addrbits = false, $mode = "") {
198
	global $config;
199

    
200
	switch ($idinfo['type']) {
201
		case "address":
202
			if ($addrbits) {
203
				if ($mode == "tunnel6")
204
					return $idinfo['address']."/128";
205
				else
206
					return $idinfo['address']."/32";
207
			} else
208
				return $idinfo['address'];
209
			break; /* NOTREACHED */
210
		case "network":
211
			return "{$idinfo['address']}/{$idinfo['netbits']}";
212
			break; /* NOTREACHED */
213
		case "none":
214
		case "mobile":
215
			return "0.0.0.0/0";
216
			break; /* NOTREACHED */
217
		default:
218
			if (empty($mode) && !empty($idinfo['mode']))
219
				$mode = $idinfo['mode'];
220

    
221
			if ($mode == "tunnel6") {
222
				$address = get_interface_ipv6($idinfo['type']);
223
				$netbits = get_interface_subnetv6($idinfo['type']);
224
				$address = gen_subnetv6($address,$netbits);
225
				return "{$address}/{$netbits}";
226
			} else {
227
				$address = get_interface_ip($idinfo['type']);
228
				$netbits = get_interface_subnet($idinfo['type']);
229
				$address = gen_subnet($address,$netbits);
230
				return "{$address}/{$netbits}";
231
			}
232
			break; /* NOTREACHED */
233
	}
234
}
235

    
236
/*
237
 * Return phase2 idinfo in address/netmask format
238
 */
239
function ipsec_idinfo_to_subnet(& $idinfo,$addrbits = false) {
240
	global $config;
241

    
242
	switch ($idinfo['type']) {
243
		case "address":
244
			if ($addrbits) {
245
				if ($idinfo['mode'] == "tunnel6")
246
					return $idinfo['address']."/128";
247
				else
248
					return $idinfo['address']."/255.255.255.255";
249
			} else
250
				return $idinfo['address'];
251
			break; /* NOTREACHED */
252
		case "none":
253
		case "network":
254
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
255
			break; /* NOTREACHED */
256
		case "mobile":
257
			return "0.0.0.0/0";
258
			break; /* NOTREACHED */
259
		default:
260
			if ($idinfo['mode'] == "tunnel6") {
261
				$address = get_interface_ipv6($idinfo['type']);
262
				$netbits = get_interface_subnetv6($idinfo['type']);
263
				$address = gen_subnetv6($address,$netbits);
264
				return $address."/".$netbits;
265
			} else {
266
				$address = get_interface_ip($idinfo['type']);
267
				$netbits = get_interface_subnet($idinfo['type']);
268
				$address = gen_subnet($address,$netbits);
269
				return $address."/".$netbits;
270
			}
271
			break; /* NOTREACHED */
272
	}
273
}
274

    
275
/*
276
 *  Return phase2 idinfo in text format
277
 */
278
function ipsec_idinfo_to_text(& $idinfo) {
279
	global $config;
280

    
281
	switch ($idinfo['type']) {
282
        case "address":
283
		return $idinfo['address'];
284
		break; /* NOTREACHED */
285
        case "network":
286
		return $idinfo['address']."/".$idinfo['netbits'];
287
		break; /* NOTREACHED */
288
	case "mobile":
289
		return gettext("Mobile Client");
290
		break; /* NOTREACHED */
291
	case "none":
292
		return gettext("None");
293
		break; /* NOTREACHED */
294
        default:
295
		if (!empty($config['interfaces'][$idinfo['type']]))
296
			return convert_friendly_interface_to_friendly_descr($idinfo['type']);
297
		else
298
			return strtoupper($idinfo['type']);
299
		break; /* NOTREACHED */
300
	}
301
}
302

    
303
/*
304
 * Return phase1 association for phase2
305
 */
306
function ipsec_lookup_phase1(& $ph2ent,& $ph1ent) {
307
	global $config;
308

    
309
	if (!is_array($config['ipsec']))
310
		return;
311
	if (!is_array($config['ipsec']['phase1']))
312
		return;
313
	if (empty($config['ipsec']['phase1']))
314
		return;
315

    
316
	foreach ($config['ipsec']['phase1'] as $ph1tmp) {
317
	    if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) {
318
		$ph1ent = $ph1tmp;
319
		return $ph1ent;
320
	    }
321
	}
322

    
323
	return false;
324
}
325

    
326
/*
327
 * Check phase1 communications status
328
 */
329
function ipsec_phase1_status(& $ph1ent) {
330

    
331
	$loc_ip = get_ipsec_tunnel_src($ph1ent);
332
	$rmt_ip = $ph1ent['remote-gateway'];
333

    
334
	if (ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
335
		return true;
336

    
337
	return false;
338
}
339

    
340
/*
341
 * Check phase2 communications status
342
 */
343
function ipsec_phase2_status(& $spd,& $sad,& $ph1ent,& $ph2ent) {
344

    
345
	$loc_ip = ipsec_get_phase1_src($ph1ent);
346
	$rmt_ip = ipsec_get_phase1_dst($ph1ent);
347

    
348
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true,$ph2ent['mode']);
349
	if (!empty($ph2ent['natlocalid']))
350
		$natloc_id = ipsec_idinfo_to_cidr($ph2ent['natlocalid'],true,$ph2ent['mode']);
351
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true,$ph2ent['mode']);
352

    
353
	/* check for established SA in both directions */
354
	if( ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id)) {
355
		if (empty($ph2ent['natlocalid']) && ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$loc_id))
356
			return true;
357
		else if (!empty($ph2ent['natlocalid']) && ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id))
358
			return true;
359
	}
360

    
361
	return false;
362
}
363

    
364
/*
365
 * Return ISAKMP SA details
366
 */
367
function ipsec_lookup_isakmp_sa($in_srcip,$in_dstip) {
368
	/* TODO : use racconctl to lookup iskamp SA */
369
	return NULL;
370
}
371

    
372
/*
373
 * Return IPsec SA details
374
 */
375
function ipsec_lookup_ipsec_sa(& $spd,& $sad,$dir,$in_srcip,$in_dstip,$in_srcid,$in_dstid) {
376

    
377
	/* match the phase1/2 to an SP */
378
	$in_srcip = ipsec_fixup_ip($in_srcip);
379
	$in_dstip = ipsec_fixup_ip($in_dstip);
380
	$in_srcid = ipsec_fixup_ip($in_srcid);
381
	$in_dstid = ipsec_fixup_ip($in_dstid);
382

    
383
	foreach($spd as $sp) {
384

    
385
		/* match direction */
386

    
387
		if($dir != $sp['dir'])
388
			continue;
389

    
390
		/* match IPs */
391

    
392
		if($in_srcip != ipsec_fixup_ip($sp['src']))
393
			continue;
394
		if($in_dstip != ipsec_fixup_ip($sp['dst']))
395
			continue;
396

    
397
		/* add netbits for address IDs */
398

    
399
		$sp_srcid = $sp['srcid'];
400
		$sp_dstid = $sp['dstid'];
401

    
402
		if (!strstr($sp_srcid,"/")) {
403
			if (is_ipaddrv4($sp_srcid))
404
				$sp_srcid .= '/32';
405
			elseif (is_ipaddrv6($sp_srcid))
406
				$sp_srcid .= '/128';
407
		}
408
		if (!strstr($sp_dstid,"/")) {
409
			if (is_ipaddrv4($sp_dstid))
410
				$sp_dstid .= '/32';
411
			elseif (is_ipaddrv6($sp_dstid))
412
				$sp_dstid .= '/128';
413
		}
414

    
415
		/* match IDs */
416

    
417
		if($in_srcid != ipsec_fixup_ip($sp_srcid))
418
			continue;
419
		if($in_dstid != ipsec_fixup_ip($sp_dstid))
420
			continue;
421

    
422
		/* match the SP to a unique SA by reqid */
423

    
424
		foreach($sad as $sa) {
425

    
426
			/* match REQIDs */
427

    
428
			if($sa[reqid] != $sp[reqid])
429
				continue;
430

    
431
			/* sanitize for NAT-T ports */
432

    
433
			$sa_srcip = $sa['src'];
434
			$sa_dstip = $sa['dst'];
435

    
436
			if (strstr($sa_srcip,"["))
437
				$sa_srcip = substr($sa_srcip,0,strcspn($sa_srcip,"["));
438
			if (strstr($sa_dstip,"["))
439
				$sa_dstip = substr($sa_dstip,0,strcspn($sa_dstip,"["));
440

    
441
			/* match IPs */
442

    
443
			if($in_srcip != ipsec_fixup_ip($sa_srcip))
444
				continue;
445
			if($in_dstip != ipsec_fixup_ip($sa_dstip))
446
				continue;
447

    
448
			return $sa;
449
		}
450
	}
451

    
452
	return NULL;
453
}
454

    
455
/*
456
 * Return dump of SPD table
457
 */
458
function ipsec_dump_spd()
459
{
460
	$fd = @popen("/usr/local/sbin/setkey -DP", "r");
461
	$spd = array();
462
	if ($fd) {
463
		while (!feof($fd)) {
464
			$line = chop(fgets($fd));
465
			if (!$line)
466
				continue;
467
			if ($line == "No SPD entries.")
468
				break;
469
			if ($line[0] != "\t") {
470
				if (is_array($cursp))
471
					$spd[] = $cursp;
472
				$cursp = array();
473
				$linea = explode(" ", $line);
474
				$cursp['srcid'] = substr($linea[0], 0, strpos($linea[0], "["));
475
				$cursp['dstid'] = substr($linea[1], 0, strpos($linea[1], "["));
476
				$i = 0;
477
			} else if (is_array($cursp)) {
478
				$linea = explode(" ", trim($line));
479
				switch($i)
480
				{
481
					case 1:
482
						if ($linea[1] == "none")	/* don't show default anti-lockout rule */
483
							unset($cursp);
484
						else
485
							$cursp['dir'] = $linea[0];
486
						break;
487
					case 2:
488
						$upperspec = explode("/", $linea[0]);
489
						$cursp['proto'] = $upperspec[0];
490
						list($cursp['src'], $cursp['dst']) = explode("-", $upperspec[2]);
491
						$cursp['reqid'] =  substr($upperspec[3], strpos($upperspec[3], "#")+1);
492
						break;
493
				}
494
			}
495
			$i++;
496
		}
497
		if (is_array($cursp) && count($cursp))
498
			$spd[] = $cursp;
499
		pclose($fd);
500
	}
501

    
502
	return $spd;
503
}
504

    
505
/*
506
 * Return dump of SAD table
507
 */
508
function ipsec_dump_sad()
509
{
510
	$fd = @popen("/usr/local/sbin/setkey -D", "r");
511
	$sad = array();
512
	if ($fd) {
513
		while (!feof($fd)) {
514
			$line = chop(fgets($fd));
515
			if (!$line || $line[0] == " ")
516
				continue;
517
			if ($line == "No SAD entries.")
518
				break;
519
			if ($line[0] != "\t")
520
			{
521
				if (is_array($cursa))
522
					$sad[] = $cursa;
523
				$cursa = array();
524
				list($cursa['src'],$cursa['dst']) = explode(" ", $line);
525
				$i = 0;
526
			}
527
			else
528
			{
529
				$linea = explode(" ", trim($line));
530
				switch ($i) {
531
					case 1:
532
						$cursa['proto'] = $linea[0];
533
						$cursa['spi'] = substr($linea[2], strpos($linea[2], "x")+1, -1);
534
						$reqid = substr($linea[3], strpos($linea[3], "=")+1);
535
						$cursa['reqid'] = substr($reqid, 0, strcspn($reqid,"("));
536
						break;
537
					case 2:
538
						$cursa['ealgo'] = $linea[1];
539
						break;
540
					case 3:
541
						$cursa['aalgo'] = $linea[1];
542
						break;
543
					case 8:
544
						$sadata = explode("(", $linea[1]);
545
						$cursa['data'] = $sadata[0] . " B";
546
						break;
547
				}
548
			}
549
			$i++;
550
		}
551
		if (is_array($cursa) && count($cursa))
552
			$sad[] = $cursa;
553
		pclose($fd);
554
	}
555

    
556
	return $sad;
557
}
558

    
559
/*
560
 * Return dump of mobile user list
561
 */
562
function ipsec_dump_mobile() {
563
	$command = "/usr/local/sbin/racoonctl show-users";
564
	$fd = @popen($command, "r");
565
	$mobile = array();
566
	if ($fd) {
567
		while (!feof($fd)) {
568
			$user = array();
569
			$line = chop(fgets($fd));
570
			if (!$line)
571
				continue;
572
			if ($line == "User|Source|Destination|CreatedOn|SPI")
573
				continue;
574

    
575
			// jim|192.168.20.243:4500|192.168.20.5:24146|2012-05-25 09:54:39|989d10e1e2d4eca4:7243830d5fd2afe7
576
			$linea = explode("|", trim($line));
577
			$user['username'] = $linea[0];
578
			$user['local'] = $linea[1];
579
			$user['remote'] = $linea[2];
580
			$user['logintime'] = $linea[3];
581
			$user['spi'] = $linea[4];
582
			if (!empty($user['username']))
583
				$mobile[] = $user;
584
		}
585
		pclose($fd);
586
	}
587

    
588
	return $mobile;
589
}
590

    
591
function ipsec_mobilekey_sort() {
592
	global $config;
593

    
594
	function mobilekeycmp($a, $b) {
595
		return strcmp($a['ident'][0], $b['ident'][0]);
596
	}
597

    
598
	usort($config['ipsec']['mobilekey'], "mobilekeycmp");
599
}
600

    
601
function ipsec_get_number_of_phase2($ikeid) {
602
	global $config;
603
    	$a_phase2 = $config['ipsec']['phase2'];
604

    
605
	$nbph2=0;
606

    
607
    	if (is_array($a_phase2) && count($a_phase2)) {
608
        	foreach ($a_phase2 as $ph2tmp) {
609
            		if ($ph2tmp['ikeid'] == $ikeid) {
610
				$nbph2++;
611
			}
612
		}
613
	}
614

    
615
	return $nbph2;
616
}
617

    
618
function ipsec_disconnect_mobile($username) {
619
	if (empty($username))
620
		return false;
621
	exec("/usr/local/sbin/racoonctl logout-user " . escapeshellarg($username));
622
}
623

    
624
function ipsec_fixup_ip($ipaddr) {
625
	if (is_ipaddrv6($ipaddr) || is_subnetv6($ipaddr))
626
		return Net_IPv6::compress(Net_IPv6::uncompress($ipaddr));
627
	else
628
		return $ipaddr;
629
}
630

    
631
?>
(28-28/66)