Project

General

Profile

Download (11.7 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

    
148
	$rg = $ph1ent['remote-gateway'];
149
	if (!is_ipaddr($rg)) {
150
		if(! $g['booting'])
151
			return resolve_retry($rg);
152
	}
153
	if(!is_ipaddr($rg))
154
		return false;
155

    
156
	return $rg;
157
}
158

    
159
/*
160
 * Return phase2 idinfo in cidr format
161
 */
162
function ipsec_idinfo_to_cidr(& $idinfo,$addrbits = false) {
163
	global $config;
164

    
165
	switch ($idinfo['type'])
166
	{
167
		case "address":
168
			if ($addrbits)
169
				return $idinfo['address']."/32";
170
			else
171
				return $idinfo['address'];
172
		case "network":
173
			return $idinfo['address']."/".$idinfo['netbits'];
174
		case "none":
175
		case "mobile":
176
			return "0.0.0.0/0";
177
		default:
178
			$address = get_interface_ip($idinfo['type']);
179
			$netbits = get_interface_subnet($idinfo['type']);
180
			$address = gen_subnet($address,$netbits);
181
			return $address."/".$netbits;
182
    }
183
}
184

    
185
/*
186
 * Return phase2 idinfo in address/netmask format
187
 */
188
function ipsec_idinfo_to_subnet(& $idinfo,$addrbits = false) {
189
	global $config;
190

    
191
	switch ($idinfo['type'])
192
	{
193
		case "address":
194
			if ($addrbits)
195
				return $idinfo['address']."/255.255.255.255";
196
			else
197
				return $idinfo['address'];
198
		case "none":
199
		case "network":
200
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
201
		case "mobile":
202
			return "0.0.0.0/0";
203
		default:
204
			$address = get_interface_ip($idinfo['type']);
205
			$netbits = get_interface_subnet($idinfo['type']);
206
			$address = gen_subnet($address,$netbits);
207
			$netbits = gen_subnet_mask($netbits);
208
			return $address."/".netbits;
209
    }
210
}
211

    
212
/*
213
 *  Return phase2 idinfo in text format
214
 */
215
function ipsec_idinfo_to_text(& $idinfo) {
216

    
217
    switch ($idinfo['type'])
218
    {
219
        case "address":
220
            return $idinfo['address'];
221
        case "network":
222
            return $idinfo['address']."/".$idinfo['netbits'];
223
	case "mobile":
224
		return "Mobile Client";
225
	case "none":
226
		return "None";
227
        default:
228
            return strtoupper($idinfo['type']);
229
    }
230
}
231

    
232
/*
233
 * Return phase1 association for phase2
234
 */
235
function ipsec_lookup_phase1(& $ph2ent,& $ph1ent)
236
{
237
    global $config;
238
    $a_phase1 = $config['ipsec']['phase1'];
239

    
240
    if (is_array($a_phase1) && count($a_phase1)) {
241
        foreach ($a_phase1 as $ph1tmp) {
242
            if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) {
243
                $ph1ent = $ph1tmp;
244
                return $ph1ent;
245
            }
246
        }
247
    }
248

    
249
    return false;
250
}
251

    
252
/*
253
 * Check phase1 communications status
254
 */
255
function ipsec_phase1_status(& $ph1ent) {
256

    
257
	$loc_ip = get_ipsec_tunnel_src($ph1ent);
258
	$rmt_ip = $ph1ent['remote-gateway'];
259

    
260
	if(ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
261
		return true;
262

    
263
	return false;
264
}
265

    
266
/*
267
 * Check phase2 communications status
268
 */
269
function ipsec_phase2_status(& $spd,& $sad,& $ph1ent,& $ph2ent) {
270

    
271
	$loc_ip = ipsec_get_phase1_src($ph1ent);
272
	$rmt_ip = gethostbyname(ipsec_get_phase1_dst($ph1ent));
273

    
274
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true);
275
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true);
276

    
277
	/* check for established SA in both directions */
278
	if( ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id) &&
279
		ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$loc_id))
280
		return true;
281

    
282
	return false;
283
}
284

    
285
/*
286
 * Return ISAKMP SA details
287
 */
288
function ipsec_lookup_isakmp_sa($in_srcip,$in_dstip) {
289
	/* TODO : use racconctl to lookup iskamp SA */
290
	return NULL;
291
}
292

    
293
/*
294
 * Return IPsec SA details
295
 */
296
function ipsec_lookup_ipsec_sa(& $spd,& $sad,$dir,$in_srcip,$in_dstip,$in_srcid,$in_dstid) {
297

    
298
	/* match the phase1/2 to an SP */
299

    
300
	foreach($spd as $sp) {
301

    
302
		/* match direction */
303

    
304
		if($dir != $sp['dir'])
305
			continue;
306

    
307
		/* match IPs */
308

    
309
		if($in_srcip != $sp['src'])
310
			continue;
311
		if($in_dstip != $sp['dst'])
312
			continue;
313

    
314
		/* add netbits for address IDs */
315

    
316
		$sp_srcid = $sp['srcid'];
317
		$sp_dstid = $sp['dstid'];
318

    
319
		if (!strstr($sp_srcid,"/"))
320
			$sp_srcid .= '/32';
321
		if (!strstr($sp_dstid,"/"))
322
			$sp_dstid .= '/32';
323

    
324
		/* match IDs */
325

    
326
		if($in_srcid != $sp_srcid)
327
			continue;
328
		if($in_dstid != $sp_dstid)
329
			continue;
330

    
331
		/* match the SP to a unique SA by reqid */
332

    
333
		foreach($sad as $sa) {
334

    
335
			/* match REQIDs */
336

    
337
			if($sa[reqid] != $sp[reqid])
338
				continue;
339

    
340
			/* sanitize for NAT-T ports */
341

    
342
			$sa_srcip = $sa['src'];
343
			$sa_dstip = $sa['dst'];
344

    
345
			if (strstr($sa_srcip,"["))
346
				$sa_srcip = substr($sa_srcip,0,strcspn($sa_srcip,"["));
347
			if (strstr($sa_dstip,"["))
348
				$sa_dstip = substr($sa_dstip,0,strcspn($sa_dstip,"["));
349

    
350
			/* match IPs */
351

    
352
			if($in_srcip != $sa_srcip)
353
				continue;
354
			if($in_dstip != $sa_dstip)
355
				continue;
356

    
357
			return $sa;
358
		}
359
	}
360

    
361
	return NULL;
362
}
363

    
364
/*
365
 * Return dump of SPD table
366
 */
367
function ipsec_dump_spd()
368
{
369
	$fd = @popen("/usr/local/sbin/setkey -DP", "r");
370
	$spd = array();
371
	if ($fd) {
372
		while (!feof($fd)) {
373
			$line = chop(fgets($fd));
374
			if (!$line)
375
				continue;
376
			if ($line == "No SPD entries.")
377
				break;
378
			if ($line[0] != "\t") {
379
				if (is_array($cursp))
380
					$spd[] = $cursp;
381
				$cursp = array();
382
				$linea = explode(" ", $line);
383
				$cursp['srcid'] = substr($linea[0], 0, strpos($linea[0], "["));
384
				$cursp['dstid'] = substr($linea[1], 0, strpos($linea[1], "["));
385
				$i = 0;
386
			} else if (is_array($cursp)) {
387
				$linea = explode(" ", trim($line));
388
				switch($i)
389
				{
390
					case 1:
391
						if ($linea[1] == "none")	/* don't show default anti-lockout rule */
392
							unset($cursp);
393
						else
394
							$cursp['dir'] = $linea[0];
395
						break;
396
					case 2:
397
						$upperspec = explode("/", $linea[0]);
398
						$cursp['proto'] = $upperspec[0];
399
						list($cursp['src'], $cursp['dst']) = explode("-", $upperspec[2]);
400
						$cursp['reqid'] =  substr($upperspec[3], strpos($upperspec[3], "#")+1);
401
						break;
402
				}
403
			}
404
			$i++;
405
		}
406
		if (is_array($cursp) && count($cursp))
407
			$spd[] = $cursp;
408
		pclose($fd);
409
	}
410

    
411
	return $spd;
412
}
413

    
414
/*
415
 * Return dump of SAD table
416
 */
417
function ipsec_dump_sad()
418
{
419
	$fd = @popen("/usr/local/sbin/setkey -D", "r");
420
	$sad = array();
421
	if ($fd) {
422
		while (!feof($fd)) {
423
			$line = chop(fgets($fd));
424
			if (!$line)
425
				continue;
426
			if ($line == "No SAD entries.")
427
				break;
428
			if ($line[0] != "\t")
429
			{
430
				if (is_array($cursa))
431
					$sad[] = $cursa;
432
				$cursa = array();
433
				list($cursa['src'],$cursa['dst']) = explode(" ", $line);
434
				$i = 0;
435
			}
436
			else
437
			{
438
				$linea = explode(" ", trim($line));
439
				switch ($i) {
440
					case 1:
441
						$cursa['proto'] = $linea[0];
442
						$cursa['spi'] = substr($linea[2], strpos($linea[2], "x")+1, -1);
443
						$reqid = substr($linea[3], strpos($linea[3], "=")+1);
444
						$cursa['reqid'] = substr($reqid, 0, strcspn($reqid,"("));
445
						break;
446
					case 2:
447
						$cursa['ealgo'] = $linea[1];
448
						break;
449
					case 3:
450
						$cursa['aalgo'] = $linea[1];
451
						break;
452
				}
453
			}
454
			$i++;
455
		}
456
		if (is_array($cursa) && count($cursa))
457
			$sad[] = $cursa;
458
		pclose($fd);
459
	}
460

    
461
	return $sad;
462
}
463

    
464
function ipsec_mobilekey_sort() {
465
	global $config;
466

    
467
	function mobilekeycmp($a, $b) {
468
		return strcmp($a['ident'][0], $b['ident'][0]);
469
	}
470

    
471
	usort($config['ipsec']['mobilekey'], "mobilekeycmp");
472
}
473

    
474
function ipsec_get_number_of_phase2($ikeid) {
475
	global $config;
476
    	$a_phase2 = $config['ipsec']['phase2'];
477

    
478
	$nbph2=0;
479

    
480
    	if (is_array($a_phase2) && count($a_phase2)) {
481
        	foreach ($a_phase2 as $ph2tmp) {
482
            		if ($ph2tmp['ikeid'] == $ikeid) {
483
				$nbph2++;
484
			}
485
		}
486
	}
487

    
488
	return $nbph2;
489
}
490

    
491
?>
(26-26/61)