Project

General

Profile

Download (12.8 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
	'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 2f3554bb jim-p
	if (!$ph1ent['remote-gateway'])
156
		return false;
157 3462a529 Matthew Grooms
	$rg = $ph1ent['remote-gateway'];
158 33d5cb7a smos
	if (!is_ipaddr($rg)) {
159
		if(! $g['booting'])
160
			return resolve_retry($rg);
161
	}
162 0af7398a Matthew Grooms
	if(!is_ipaddr($rg))
163
		return false;
164
165 3462a529 Matthew Grooms
	return $rg;
166
}
167
168 a93e56c5 Matthew Grooms
/*
169
 * Return phase2 idinfo in cidr format
170
 */
171
function ipsec_idinfo_to_cidr(& $idinfo,$addrbits = false) {
172
	global $config;
173
174
	switch ($idinfo['type'])
175
	{
176
		case "address":
177 98790f61 Seth Mos
			if ($addrbits) {
178
				if($idinfo['mode'] == "tunnel6") {
179
					return $idinfo['address']."/128";
180
				} else {
181
					return $idinfo['address']."/32";
182
				}
183
			} else {
184 a93e56c5 Matthew Grooms
				return $idinfo['address'];
185 98790f61 Seth Mos
			}
186 a93e56c5 Matthew Grooms
		case "network":
187
			return $idinfo['address']."/".$idinfo['netbits'];
188 63017a73 Ermal Lu?i
		case "none":
189 3462a529 Matthew Grooms
		case "mobile":
190
			return "0.0.0.0/0";
191 a55e9c70 Ermal Lu?i
		default:
192 98790f61 Seth Mos
			if($idinfo['mode'] == "tunnel6") {
193
				$address = get_interface_ipv6($idinfo['type']);
194
				$netbits = get_interface_subnetv6($idinfo['type']);
195
				$address = gen_subnetv6($address,$netbits);
196
				return $address."/".$netbits;
197
			} else {
198
				$address = get_interface_ip($idinfo['type']);
199
				$netbits = get_interface_subnet($idinfo['type']);
200
				$address = gen_subnet($address,$netbits);
201
				return $address."/".$netbits;
202
			}
203
	}
204 a93e56c5 Matthew Grooms
}
205
206
/*
207
 * Return phase2 idinfo in address/netmask format
208
 */
209
function ipsec_idinfo_to_subnet(& $idinfo,$addrbits = false) {
210
	global $config;
211
212
	switch ($idinfo['type'])
213
	{
214
		case "address":
215 98790f61 Seth Mos
			if ($addrbits) {
216
				if($idinfo['mode'] == "tunnel6") {
217
					return $idinfo['address']."/128";
218
				} else {
219
					return $idinfo['address']."/255.255.255.255";
220
				}
221
			} else {
222 a93e56c5 Matthew Grooms
				return $idinfo['address'];
223 98790f61 Seth Mos
			}
224 63017a73 Ermal Lu?i
		case "none":
225 a93e56c5 Matthew Grooms
		case "network":
226
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
227 3462a529 Matthew Grooms
		case "mobile":
228
			return "0.0.0.0/0";
229 63017a73 Ermal Lu?i
		default:
230 98790f61 Seth Mos
			if($idinfo['mode'] == "tunnel6") {
231
				$address = get_interface_ipv6($idinfo['type']);
232
				$netbits = get_interface_subnetv6($idinfo['type']);
233
				$address = gen_subnetv6($address,$netbits);
234
				return $address."/".$netbits;
235
			} else {
236
				$address = get_interface_ip($idinfo['type']);
237
				$netbits = get_interface_subnet($idinfo['type']);
238
				$address = gen_subnet($address,$netbits);
239
				return $address."/".$netbits;
240
			}
241
	}
242 a93e56c5 Matthew Grooms
}
243
244
/*
245
 *  Return phase2 idinfo in text format
246
 */
247
function ipsec_idinfo_to_text(& $idinfo) {
248
249
    switch ($idinfo['type'])
250
    {
251
        case "address":
252
            return $idinfo['address'];
253
        case "network":
254
            return $idinfo['address']."/".$idinfo['netbits'];
255 63017a73 Ermal Lu?i
	case "mobile":
256 b1fd7536 Carlos Eduardo Ramos
		return gettext("Mobile Client");
257 63017a73 Ermal Lu?i
	case "none":
258 b1fd7536 Carlos Eduardo Ramos
		return gettext("None");
259 a93e56c5 Matthew Grooms
        default:
260
            return strtoupper($idinfo['type']);
261
    }
262
}
263
264
/*
265
 * Return phase1 association for phase2
266
 */
267
function ipsec_lookup_phase1(& $ph2ent,& $ph1ent)
268
{
269
    global $config;
270
    $a_phase1 = $config['ipsec']['phase1'];
271
272
    if (is_array($a_phase1) && count($a_phase1)) {
273
        foreach ($a_phase1 as $ph1tmp) {
274
            if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) {
275
                $ph1ent = $ph1tmp;
276
                return $ph1ent;
277
            }
278
        }
279
    }
280
281
    return false;
282
}
283
284
/*
285
 * Check phase1 communications status
286
 */
287
function ipsec_phase1_status(& $ph1ent) {
288
289
	$loc_ip = get_ipsec_tunnel_src($ph1ent);
290
	$rmt_ip = $ph1ent['remote-gateway'];
291
292
	if(ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
293
		return true;
294
295
	return false;
296
}
297
298
/*
299
 * Check phase2 communications status
300
 */
301
function ipsec_phase2_status(& $spd,& $sad,& $ph1ent,& $ph2ent) {
302
303
	$loc_ip = ipsec_get_phase1_src($ph1ent);
304 6c4f3b54 Seth Mos
	$rmt_ip = resolve_retry(ipsec_get_phase1_dst($ph1ent));
305 a93e56c5 Matthew Grooms
306
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true);
307
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true);
308
309
	/* check for established SA in both directions */
310
	if( ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id) &&
311
		ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$loc_id))
312
		return true;
313
314
	return false;
315
}
316
317
/*
318
 * Return ISAKMP SA details
319
 */
320
function ipsec_lookup_isakmp_sa($in_srcip,$in_dstip) {
321
	/* TODO : use racconctl to lookup iskamp SA */
322
	return NULL;
323
}
324
325
/*
326
 * Return IPsec SA details
327
 */
328
function ipsec_lookup_ipsec_sa(& $spd,& $sad,$dir,$in_srcip,$in_dstip,$in_srcid,$in_dstid) {
329
330
	/* match the phase1/2 to an SP */
331
332
	foreach($spd as $sp) {
333
334
		/* match direction */
335
336
		if($dir != $sp['dir'])
337
			continue;
338
339
		/* match IPs */
340
341
		if($in_srcip != $sp['src'])
342
			continue;
343
		if($in_dstip != $sp['dst'])
344
			continue;
345
346
		/* add netbits for address IDs */
347
348
		$sp_srcid = $sp['srcid'];
349
		$sp_dstid = $sp['dstid'];
350
351
		if (!strstr($sp_srcid,"/"))
352
			$sp_srcid .= '/32';
353
		if (!strstr($sp_dstid,"/"))
354
			$sp_dstid .= '/32';
355
356
		/* match IDs */
357
358
		if($in_srcid != $sp_srcid)
359
			continue;
360
		if($in_dstid != $sp_dstid)
361
			continue;
362
363
		/* match the SP to a unique SA by reqid */
364
365
		foreach($sad as $sa) {
366
367
			/* match REQIDs */
368
369
			if($sa[reqid] != $sp[reqid])
370
				continue;
371
372
			/* sanitize for NAT-T ports */
373
374
			$sa_srcip = $sa['src'];
375
			$sa_dstip = $sa['dst'];
376
377
			if (strstr($sa_srcip,"["))
378
				$sa_srcip = substr($sa_srcip,0,strcspn($sa_srcip,"["));
379
			if (strstr($sa_dstip,"["))
380
				$sa_dstip = substr($sa_dstip,0,strcspn($sa_dstip,"["));
381
382
			/* match IPs */
383
384
			if($in_srcip != $sa_srcip)
385
				continue;
386
			if($in_dstip != $sa_dstip)
387
				continue;
388
389
			return $sa;
390
		}
391
	}
392
393
	return NULL;
394
}
395
396
/*
397
 * Return dump of SPD table
398
 */
399
function ipsec_dump_spd()
400
{
401
	$fd = @popen("/usr/local/sbin/setkey -DP", "r");
402
	$spd = array();
403
	if ($fd) {
404
		while (!feof($fd)) {
405
			$line = chop(fgets($fd));
406
			if (!$line)
407
				continue;
408
			if ($line == "No SPD entries.")
409
				break;
410
			if ($line[0] != "\t") {
411
				if (is_array($cursp))
412
					$spd[] = $cursp;
413
				$cursp = array();
414
				$linea = explode(" ", $line);
415
				$cursp['srcid'] = substr($linea[0], 0, strpos($linea[0], "["));
416
				$cursp['dstid'] = substr($linea[1], 0, strpos($linea[1], "["));
417
				$i = 0;
418
			} else if (is_array($cursp)) {
419
				$linea = explode(" ", trim($line));
420
				switch($i)
421
				{
422
					case 1:
423
						if ($linea[1] == "none")	/* don't show default anti-lockout rule */
424
							unset($cursp);
425
						else
426
							$cursp['dir'] = $linea[0];
427
						break;
428
					case 2:
429
						$upperspec = explode("/", $linea[0]);
430
						$cursp['proto'] = $upperspec[0];
431
						list($cursp['src'], $cursp['dst']) = explode("-", $upperspec[2]);
432
						$cursp['reqid'] =  substr($upperspec[3], strpos($upperspec[3], "#")+1);
433
						break;
434
				}
435
			}
436
			$i++;
437
		}
438
		if (is_array($cursp) && count($cursp))
439
			$spd[] = $cursp;
440
		pclose($fd);
441
	}
442
443
	return $spd;
444
}
445
446
/*
447
 * Return dump of SAD table
448
 */
449
function ipsec_dump_sad()
450
{
451
	$fd = @popen("/usr/local/sbin/setkey -D", "r");
452
	$sad = array();
453
	if ($fd) {
454
		while (!feof($fd)) {
455
			$line = chop(fgets($fd));
456
			if (!$line)
457
				continue;
458
			if ($line == "No SAD entries.")
459
				break;
460
			if ($line[0] != "\t")
461
			{
462
				if (is_array($cursa))
463
					$sad[] = $cursa;
464
				$cursa = array();
465
				list($cursa['src'],$cursa['dst']) = explode(" ", $line);
466
				$i = 0;
467
			}
468
			else
469
			{
470
				$linea = explode(" ", trim($line));
471
				switch ($i) {
472
					case 1:
473
						$cursa['proto'] = $linea[0];
474
						$cursa['spi'] = substr($linea[2], strpos($linea[2], "x")+1, -1);
475
						$reqid = substr($linea[3], strpos($linea[3], "=")+1);
476
						$cursa['reqid'] = substr($reqid, 0, strcspn($reqid,"("));
477
						break;
478
					case 2:
479
						$cursa['ealgo'] = $linea[1];
480
						break;
481
					case 3:
482
						$cursa['aalgo'] = $linea[1];
483
						break;
484 f451ea09 jim-p
					case 8:
485
						$sadata = explode("(", $linea[1]);
486
						$cursa['data'] = $sadata[0] . " B";
487
						break;
488 a93e56c5 Matthew Grooms
				}
489
			}
490
			$i++;
491
		}
492
		if (is_array($cursa) && count($cursa))
493
			$sad[] = $cursa;
494
		pclose($fd);
495
	}
496
497
	return $sad;
498
}
499
500 958420c5 jim-p
function ipsec_mobilekey_sort() {
501
	global $config;
502
503
	function mobilekeycmp($a, $b) {
504
		return strcmp($a['ident'][0], $b['ident'][0]);
505
	}
506
507
	usort($config['ipsec']['mobilekey'], "mobilekeycmp");
508
}
509
510 8f5c3d8d Pierre POMES
function ipsec_get_number_of_phase2($ikeid) {
511
	global $config;
512
    	$a_phase2 = $config['ipsec']['phase2'];
513
514
	$nbph2=0;
515
516
    	if (is_array($a_phase2) && count($a_phase2)) {
517
        	foreach ($a_phase2 as $ph2tmp) {
518
            		if ($ph2tmp['ikeid'] == $ikeid) {
519
				$nbph2++;
520
			}
521
		}
522
	}
523
524
	return $nbph2;
525
}
526
527
?>