Project

General

Profile

Download (12.8 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' => 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

    
47
$peer_identifier_list = array(
48
	'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

    
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
	if (!$ph1ent['remote-gateway'])
156
		return false;
157
	$rg = $ph1ent['remote-gateway'];
158
	if (!is_ipaddr($rg)) {
159
		if(! $g['booting'])
160
			return resolve_retry($rg);
161
	}
162
	if(!is_ipaddr($rg))
163
		return false;
164

    
165
	return $rg;
166
}
167

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

    
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
					case 8:
485
						$sadata = explode("(", $linea[1]);
486
						$cursa['data'] = $sadata[0] . " B";
487
						break;
488
				}
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
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
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
?>
(26-26/61)