Project

General

Profile

Download (14.5 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 = get_failover_interface($ph1ent['interface']);
155
			if($ph1ent['protocol'] == "inet6") {
156
				$interfaceip = get_interface_ipv6($if);
157
			} else {
158
				$interfaceip = get_interface_ip($if);
159
			}
160
		} else {
161
			$interfaceip=$ph1ent['interface'];
162
		}
163
	} else {
164
		$if = "wan";
165
		if($ph1ent['protocol'] == "inet6") {
166
			$interfaceip = get_interface_ipv6($if);
167
		} else {
168
			$interfaceip = get_interface_ip($if);
169
		}
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
	if (empty($ph1ent['remote-gateway']))
181
		return false;
182
	$rg = $ph1ent['remote-gateway'];
183
	if (!is_ipaddr($rg)) {
184
		if(! $g['booting'])
185
			return resolve_retry($rg);
186
	}
187
	if(!is_ipaddr($rg))
188
		return false;
189

    
190
	return $rg;
191
}
192

    
193
/*
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
			if ($addrbits) {
203
				if($idinfo['mode'] == "tunnel6") {
204
					return $idinfo['address']."/128";
205
				} else {
206
					return $idinfo['address']."/32";
207
				}
208
			} else {
209
				return $idinfo['address'];
210
			}
211
		case "network":
212
			return $idinfo['address']."/".$idinfo['netbits'];
213
		case "none":
214
		case "mobile":
215
			return "0.0.0.0/0";
216
		default:
217
			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
}
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
			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
				return $idinfo['address'];
248
			}
249
		case "none":
250
		case "network":
251
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
252
		case "mobile":
253
			return "0.0.0.0/0";
254
		default:
255
			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
}
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
	case "mobile":
281
		return gettext("Mobile Client");
282
	case "none":
283
		return gettext("None");
284
        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
	$rmt_ip = ipsec_get_phase1_dst($ph1ent);
330

    
331
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true);
332
	if (!empty($ph2ent['natlocalid']))
333
		$natloc_id = ipsec_idinfo_to_cidr($ph2ent['natlocalid'],true);
334
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true);
335

    
336
	/* check for established SA in both directions */
337
	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

    
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
					case 8:
515
						$sadata = explode("(", $linea[1]);
516
						$cursa['data'] = $sadata[0] . " B";
517
						break;
518
				}
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
/*
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
			if (!empty($user['username']))
554
				$mobile[] = $user;
555
		}
556
		pclose($fd);
557
	}
558

    
559
	return $mobile;
560
}
561

    
562
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
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
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
?>
(28-28/67)