Project

General

Profile

Download (12.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?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

    
32
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/setkey
33
	pfSense_MODULE:	ipsec
34

    
35
*/
36

    
37
/* 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
	'xauth_rsa_server' => array( 'name' => 'Mutual RSA + Xauth', 'mobile' => true ),
80
	'xauth_psk_server' => array( 'name' => 'Mutual PSK + Xauth', 'mobile' => true ),
81
	'rsasig' => array( 'name' => 'Mutual RSA', 'mobile' => false ),
82
	'pre_shared_key' => array( 'name' => 'Mutual PSK', 'mobile' => false ) );
83

    
84
$p2_modes = array(
85
	'tunnel' => 'Tunnel IPv4',
86
	'tunnel6' => 'Tunnel IPv6',
87
	'transport' => 'Transport');
88

    
89
$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
/*
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
/*
123
 * Return phase1 local address
124
 */
125
function ipsec_get_phase1_src(& $ph1ent) {
126

    
127
	if ($ph1ent['interface']) {
128
		if (!is_ipaddr($ph1ent['interface'])) {
129
			$if = $ph1ent['interface'];
130
			if($ph1ent['protocol'] == "inet6") {
131
				$interfaceip = get_interface_ipv6($if);
132
			} else {
133
				$interfaceip = get_interface_ip($if);
134
			}
135
		} else {
136
			$interfaceip=$ph1ent['interface'];
137
		}
138
	} else {
139
		$if = "wan";
140
		if($ph1ent['protocol'] == "inet6") {
141
			$interfaceip = get_interface_ipv6($if);
142
		} else {
143
			$interfaceip = get_interface_ip($if);
144
		}
145
	}
146

    
147
	return $interfaceip;
148
}
149

    
150
/*
151
 * Return phase1 local address
152
 */
153
function ipsec_get_phase1_dst(& $ph1ent) {
154
	global $g;
155

    
156
	$rg = $ph1ent['remote-gateway'];
157
	if (!is_ipaddr($rg)) {
158
		if(! $g['booting'])
159
			return resolve_retry($rg);
160
	}
161
	if(!is_ipaddr($rg))
162
		return false;
163

    
164
	return $rg;
165
}
166

    
167
/*
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
			if ($addrbits) {
177
				if($idinfo['mode'] == "tunnel6") {
178
					return $idinfo['address']."/128";
179
				} else {
180
					return $idinfo['address']."/32";
181
				}
182
			} else {
183
				return $idinfo['address'];
184
			}
185
		case "network":
186
			return $idinfo['address']."/".$idinfo['netbits'];
187
		case "none":
188
		case "mobile":
189
			return "0.0.0.0/0";
190
		default:
191
			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
}
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
			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
				return $idinfo['address'];
222
			}
223
		case "none":
224
		case "network":
225
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
226
		case "mobile":
227
			return "0.0.0.0/0";
228
		default:
229
			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
}
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
	case "mobile":
255
		return "Mobile Client";
256
	case "none":
257
		return "None";
258
        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
	$rmt_ip = resolve_retry(ipsec_get_phase1_dst($ph1ent));
304

    
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
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
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
?>
(26-26/61)