Project

General

Profile

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