Project

General

Profile

Download (11.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' => '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',
86
	'transport' => 'Transport');
87

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

    
126
	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
		$if = "wan";
136
		$interfaceip = get_interface_ip($if);
137
	}
138

    
139
	return $interfaceip;
140
}
141

    
142
/*
143
 * Return phase1 local address
144
 */
145
function ipsec_get_phase1_dst(& $ph1ent) {
146
	global $g;
147
	if (!$ph1ent['remote-gateway'])
148
		return false;
149
	$rg = $ph1ent['remote-gateway'];
150
	if (!is_ipaddr($rg)) {
151
		if(! $g['booting'])
152
			return resolve_retry($rg);
153
	}
154
	if(!is_ipaddr($rg))
155
		return false;
156

    
157
	return $rg;
158
}
159

    
160
/*
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
		case "none":
176
		case "mobile":
177
			return "0.0.0.0/0";
178
		default:
179
			$address = get_interface_ip($idinfo['type']);
180
			$netbits = get_interface_subnet($idinfo['type']);
181
			$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
		case "none":
200
		case "network":
201
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
202
		case "mobile":
203
			return "0.0.0.0/0";
204
		default:
205
			$address = get_interface_ip($idinfo['type']);
206
			$netbits = get_interface_subnet($idinfo['type']);
207
			$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
	case "mobile":
225
		return "Mobile Client";
226
	case "none":
227
		return "None";
228
        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
	$rmt_ip = gethostbyname(ipsec_get_phase1_dst($ph1ent));
274

    
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
					case 8:
454
						$sadata = explode("(", $linea[1]);
455
						$cursa['data'] = $sadata[0] . " B";
456
						break;
457
				}
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
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
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
?>
(26-26/62)