Project

General

Profile

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