Project

General

Profile

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