Project

General

Profile

Download (12.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
	'myaddress' => array( 'desc' => 'My IP address', 'mobile' => true ),
40
	'address' => array( 'desc' => 'IP address', 'mobile' => true ),
41
	'fqdn' => array( 'desc' => 'Distinguished name', 'mobile' => true ),
42
	'user_fqdn' => array( 'desc' => 'User distinguished name', 'mobile' => true ),
43
	'asn1dn' => array( 'desc' => 'ASN.1 distinguished Name', 'mobile' => true ),
44
	'keyid tag' => array( 'desc' => 'KeyID tag', 'mobile' => true ),
45
	'dyn_dns' => array( 'desc' => 'Dynamic DNS', 'mobile' => true ));
46
47
$peer_identifier_list = array(
48
	'peeraddress' => array( 'desc' => 'Peer IP address', 'mobile' => false ),
49
	'address' => array( 'desc' => 'IP address', 'mobile' => false ),
50
	'fqdn' => array( 'desc' => 'Distinguished name', 'mobile' => true ),
51
	'user_fqdn' => array( 'desc' => 'User distinguished name', 'mobile' => true ),
52
	'asn1dn' => array( 'desc' => 'ASN.1 distinguished Name', 'mobile' => true ),
53
	'keyid tag' => array( 'desc' =>'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
	'sha1' => 'SHA1',
71
	'md5' => 'MD5');
72
73
$p2_halgos = array(
74
	'hmac_sha1' => 'SHA1',
75
	'hmac_md5' => 'MD5');
76
77
$p1_authentication_methods = array(
78
	'hybrid_rsa_server' => array( 'name' => 'Hybrid RSA + Xauth', 'mobile' => true ),
79 1703e5c5 sullrich
	'xauth_rsa_server' => array( 'name' => 'Mutual RSA + Xauth', 'mobile' => true ),
80
	'xauth_psk_server' => array( 'name' => 'Mutual PSK + Xauth', 'mobile' => true ),
81 3462a529 Matthew Grooms
	'rsasig' => array( 'name' => 'Mutual RSA', 'mobile' => false ),
82
	'pre_shared_key' => array( 'name' => 'Mutual PSK', 'mobile' => false ) );
83
84 4b96b367 mgrooms
$p2_modes = array(
85 98790f61 Seth Mos
	'tunnel' => 'Tunnel IPv4',
86
	'tunnel6' => 'Tunnel IPv6',
87 4b96b367 mgrooms
	'transport' => 'Transport');
88
89 3462a529 Matthew Grooms
$p2_protos = array(
90
	'esp' => 'ESP',
91
	'ah' => 'AH');
92
93
$p2_pfskeygroups = array(
94
	'0' => 'off',
95
	'1' => '1',
96
	'2' => '2',
97
	'5' => '5');
98
99 d799787e Matthew Grooms
/*
100
 * ikeid management functions
101
 */
102
103
function ipsec_ikeid_used($ikeid) {
104
	global $config;
105
106
	foreach ($config['ipsec']['phase1'] as $ph1ent)
107
		if( $ikeid == $ph1ent['ikeid'] )
108
			return true;
109
110
	return false;
111
}
112
113
function ipsec_ikeid_next() {
114
115
	$ikeid = 1;
116
	while(ipsec_ikeid_used($ikeid))
117
		$ikeid++;
118
119
	return $ikeid;
120
}
121
122 a93e56c5 Matthew Grooms
/*
123
 * Return phase1 local address
124
 */
125
function ipsec_get_phase1_src(& $ph1ent) {
126
127 25f6730a Pierre POMES
	if ($ph1ent['interface']) {
128
		if (!is_ipaddr($ph1ent['interface'])) {
129
			$if = $ph1ent['interface'];
130 e79b24ab Seth Mos
			if($ph1ent['protocol'] == "inet6") {
131
				$interfaceip = get_interface_ipv6($if);
132
			} else {
133
				$interfaceip = get_interface_ip($if);
134
			}
135 25f6730a Pierre POMES
		} else {
136
			$interfaceip=$ph1ent['interface'];
137
		}
138 e79b24ab Seth Mos
	} else {
139 924876a8 Ermal Lu?i
		$if = "wan";
140 e79b24ab Seth Mos
		if($ph1ent['protocol'] == "inet6") {
141
			$interfaceip = get_interface_ipv6($if);
142
		} else {
143
			$interfaceip = get_interface_ip($if);
144
		}
145 25f6730a Pierre POMES
	}
146 a93e56c5 Matthew Grooms
147
	return $interfaceip;
148
}
149
150 3462a529 Matthew Grooms
/*
151
 * Return phase1 local address
152
 */
153
function ipsec_get_phase1_dst(& $ph1ent) {
154 df82fae1 smos
	global $g;
155 3462a529 Matthew Grooms
156
	$rg = $ph1ent['remote-gateway'];
157 33d5cb7a smos
	if (!is_ipaddr($rg)) {
158
		if(! $g['booting'])
159
			return resolve_retry($rg);
160
	}
161 0af7398a Matthew Grooms
	if(!is_ipaddr($rg))
162
		return false;
163
164 3462a529 Matthew Grooms
	return $rg;
165
}
166
167 a93e56c5 Matthew Grooms
/*
168
 * Return phase2 idinfo in cidr format
169
 */
170
function ipsec_idinfo_to_cidr(& $idinfo,$addrbits = false) {
171
	global $config;
172
173
	switch ($idinfo['type'])
174
	{
175
		case "address":
176 98790f61 Seth Mos
			if ($addrbits) {
177
				if($idinfo['mode'] == "tunnel6") {
178
					return $idinfo['address']."/128";
179
				} else {
180
					return $idinfo['address']."/32";
181
				}
182
			} else {
183 a93e56c5 Matthew Grooms
				return $idinfo['address'];
184 98790f61 Seth Mos
			}
185 a93e56c5 Matthew Grooms
		case "network":
186
			return $idinfo['address']."/".$idinfo['netbits'];
187 63017a73 Ermal Lu?i
		case "none":
188 3462a529 Matthew Grooms
		case "mobile":
189
			return "0.0.0.0/0";
190 a55e9c70 Ermal Lu?i
		default:
191 98790f61 Seth Mos
			if($idinfo['mode'] == "tunnel6") {
192
				$address = get_interface_ipv6($idinfo['type']);
193
				$netbits = get_interface_subnetv6($idinfo['type']);
194
				$address = gen_subnetv6($address,$netbits);
195
				return $address."/".$netbits;
196
			} else {
197
				$address = get_interface_ip($idinfo['type']);
198
				$netbits = get_interface_subnet($idinfo['type']);
199
				$address = gen_subnet($address,$netbits);
200
				return $address."/".$netbits;
201
			}
202
	}
203 a93e56c5 Matthew Grooms
}
204
205
/*
206
 * Return phase2 idinfo in address/netmask format
207
 */
208
function ipsec_idinfo_to_subnet(& $idinfo,$addrbits = false) {
209
	global $config;
210
211
	switch ($idinfo['type'])
212
	{
213
		case "address":
214 98790f61 Seth Mos
			if ($addrbits) {
215
				if($idinfo['mode'] == "tunnel6") {
216
					return $idinfo['address']."/128";
217
				} else {
218
					return $idinfo['address']."/255.255.255.255";
219
				}
220
			} else {
221 a93e56c5 Matthew Grooms
				return $idinfo['address'];
222 98790f61 Seth Mos
			}
223 63017a73 Ermal Lu?i
		case "none":
224 a93e56c5 Matthew Grooms
		case "network":
225
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
226 3462a529 Matthew Grooms
		case "mobile":
227
			return "0.0.0.0/0";
228 63017a73 Ermal Lu?i
		default:
229 98790f61 Seth Mos
			if($idinfo['mode'] == "tunnel6") {
230
				$address = get_interface_ipv6($idinfo['type']);
231
				$netbits = get_interface_subnetv6($idinfo['type']);
232
				$address = gen_subnetv6($address,$netbits);
233
				return $address."/".$netbits;
234
			} else {
235
				$address = get_interface_ip($idinfo['type']);
236
				$netbits = get_interface_subnet($idinfo['type']);
237
				$address = gen_subnet($address,$netbits);
238
				return $address."/".$netbits;
239
			}
240
	}
241 a93e56c5 Matthew Grooms
}
242
243
/*
244
 *  Return phase2 idinfo in text format
245
 */
246
function ipsec_idinfo_to_text(& $idinfo) {
247
248
    switch ($idinfo['type'])
249
    {
250
        case "address":
251
            return $idinfo['address'];
252
        case "network":
253
            return $idinfo['address']."/".$idinfo['netbits'];
254 63017a73 Ermal Lu?i
	case "mobile":
255
		return "Mobile Client";
256
	case "none":
257
		return "None";
258 a93e56c5 Matthew Grooms
        default:
259
            return strtoupper($idinfo['type']);
260
    }
261
}
262
263
/*
264
 * Return phase1 association for phase2
265
 */
266
function ipsec_lookup_phase1(& $ph2ent,& $ph1ent)
267
{
268
    global $config;
269
    $a_phase1 = $config['ipsec']['phase1'];
270
271
    if (is_array($a_phase1) && count($a_phase1)) {
272
        foreach ($a_phase1 as $ph1tmp) {
273
            if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) {
274
                $ph1ent = $ph1tmp;
275
                return $ph1ent;
276
            }
277
        }
278
    }
279
280
    return false;
281
}
282
283
/*
284
 * Check phase1 communications status
285
 */
286
function ipsec_phase1_status(& $ph1ent) {
287
288
	$loc_ip = get_ipsec_tunnel_src($ph1ent);
289
	$rmt_ip = $ph1ent['remote-gateway'];
290
291
	if(ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
292
		return true;
293
294
	return false;
295
}
296
297
/*
298
 * Check phase2 communications status
299
 */
300
function ipsec_phase2_status(& $spd,& $sad,& $ph1ent,& $ph2ent) {
301
302
	$loc_ip = ipsec_get_phase1_src($ph1ent);
303 6c4f3b54 Seth Mos
	$rmt_ip = resolve_retry(ipsec_get_phase1_dst($ph1ent));
304 a93e56c5 Matthew Grooms
305
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true);
306
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true);
307
308
	/* check for established SA in both directions */
309
	if( ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id) &&
310
		ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$loc_id))
311
		return true;
312
313
	return false;
314
}
315
316
/*
317
 * Return ISAKMP SA details
318
 */
319
function ipsec_lookup_isakmp_sa($in_srcip,$in_dstip) {
320
	/* TODO : use racconctl to lookup iskamp SA */
321
	return NULL;
322
}
323
324
/*
325
 * Return IPsec SA details
326
 */
327
function ipsec_lookup_ipsec_sa(& $spd,& $sad,$dir,$in_srcip,$in_dstip,$in_srcid,$in_dstid) {
328
329
	/* match the phase1/2 to an SP */
330
331
	foreach($spd as $sp) {
332
333
		/* match direction */
334
335
		if($dir != $sp['dir'])
336
			continue;
337
338
		/* match IPs */
339
340
		if($in_srcip != $sp['src'])
341
			continue;
342
		if($in_dstip != $sp['dst'])
343
			continue;
344
345
		/* add netbits for address IDs */
346
347
		$sp_srcid = $sp['srcid'];
348
		$sp_dstid = $sp['dstid'];
349
350
		if (!strstr($sp_srcid,"/"))
351
			$sp_srcid .= '/32';
352
		if (!strstr($sp_dstid,"/"))
353
			$sp_dstid .= '/32';
354
355
		/* match IDs */
356
357
		if($in_srcid != $sp_srcid)
358
			continue;
359
		if($in_dstid != $sp_dstid)
360
			continue;
361
362
		/* match the SP to a unique SA by reqid */
363
364
		foreach($sad as $sa) {
365
366
			/* match REQIDs */
367
368
			if($sa[reqid] != $sp[reqid])
369
				continue;
370
371
			/* sanitize for NAT-T ports */
372
373
			$sa_srcip = $sa['src'];
374
			$sa_dstip = $sa['dst'];
375
376
			if (strstr($sa_srcip,"["))
377
				$sa_srcip = substr($sa_srcip,0,strcspn($sa_srcip,"["));
378
			if (strstr($sa_dstip,"["))
379
				$sa_dstip = substr($sa_dstip,0,strcspn($sa_dstip,"["));
380
381
			/* match IPs */
382
383
			if($in_srcip != $sa_srcip)
384
				continue;
385
			if($in_dstip != $sa_dstip)
386
				continue;
387
388
			return $sa;
389
		}
390
	}
391
392
	return NULL;
393
}
394
395
/*
396
 * Return dump of SPD table
397
 */
398
function ipsec_dump_spd()
399
{
400
	$fd = @popen("/usr/local/sbin/setkey -DP", "r");
401
	$spd = array();
402
	if ($fd) {
403
		while (!feof($fd)) {
404
			$line = chop(fgets($fd));
405
			if (!$line)
406
				continue;
407
			if ($line == "No SPD entries.")
408
				break;
409
			if ($line[0] != "\t") {
410
				if (is_array($cursp))
411
					$spd[] = $cursp;
412
				$cursp = array();
413
				$linea = explode(" ", $line);
414
				$cursp['srcid'] = substr($linea[0], 0, strpos($linea[0], "["));
415
				$cursp['dstid'] = substr($linea[1], 0, strpos($linea[1], "["));
416
				$i = 0;
417
			} else if (is_array($cursp)) {
418
				$linea = explode(" ", trim($line));
419
				switch($i)
420
				{
421
					case 1:
422
						if ($linea[1] == "none")	/* don't show default anti-lockout rule */
423
							unset($cursp);
424
						else
425
							$cursp['dir'] = $linea[0];
426
						break;
427
					case 2:
428
						$upperspec = explode("/", $linea[0]);
429
						$cursp['proto'] = $upperspec[0];
430
						list($cursp['src'], $cursp['dst']) = explode("-", $upperspec[2]);
431
						$cursp['reqid'] =  substr($upperspec[3], strpos($upperspec[3], "#")+1);
432
						break;
433
				}
434
			}
435
			$i++;
436
		}
437
		if (is_array($cursp) && count($cursp))
438
			$spd[] = $cursp;
439
		pclose($fd);
440
	}
441
442
	return $spd;
443
}
444
445
/*
446
 * Return dump of SAD table
447
 */
448
function ipsec_dump_sad()
449
{
450
	$fd = @popen("/usr/local/sbin/setkey -D", "r");
451
	$sad = array();
452
	if ($fd) {
453
		while (!feof($fd)) {
454
			$line = chop(fgets($fd));
455
			if (!$line)
456
				continue;
457
			if ($line == "No SAD entries.")
458
				break;
459
			if ($line[0] != "\t")
460
			{
461
				if (is_array($cursa))
462
					$sad[] = $cursa;
463
				$cursa = array();
464
				list($cursa['src'],$cursa['dst']) = explode(" ", $line);
465
				$i = 0;
466
			}
467
			else
468
			{
469
				$linea = explode(" ", trim($line));
470
				switch ($i) {
471
					case 1:
472
						$cursa['proto'] = $linea[0];
473
						$cursa['spi'] = substr($linea[2], strpos($linea[2], "x")+1, -1);
474
						$reqid = substr($linea[3], strpos($linea[3], "=")+1);
475
						$cursa['reqid'] = substr($reqid, 0, strcspn($reqid,"("));
476
						break;
477
					case 2:
478
						$cursa['ealgo'] = $linea[1];
479
						break;
480
					case 3:
481
						$cursa['aalgo'] = $linea[1];
482
						break;
483
				}
484
			}
485
			$i++;
486
		}
487
		if (is_array($cursa) && count($cursa))
488
			$sad[] = $cursa;
489
		pclose($fd);
490
	}
491
492
	return $sad;
493
}
494
495 958420c5 jim-p
function ipsec_mobilekey_sort() {
496
	global $config;
497
498
	function mobilekeycmp($a, $b) {
499
		return strcmp($a['ident'][0], $b['ident'][0]);
500
	}
501
502
	usort($config['ipsec']['mobilekey'], "mobilekeycmp");
503
}
504
505 8f5c3d8d Pierre POMES
function ipsec_get_number_of_phase2($ikeid) {
506
	global $config;
507
    	$a_phase2 = $config['ipsec']['phase2'];
508
509
	$nbph2=0;
510
511
    	if (is_array($a_phase2) && count($a_phase2)) {
512
        	foreach ($a_phase2 as $ph2tmp) {
513
            		if ($ph2tmp['ikeid'] == $ikeid) {
514
				$nbph2++;
515
			}
516
		}
517
	}
518
519
	return $nbph2;
520
}
521
522
?>