Project

General

Profile

Download (11.2 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 = $ph1ent['interface'];
128
	else
129
		$if = "wan";
130

    
131
	$interfaceip = get_interface_ip($if);
132

    
133
	return $interfaceip;
134
}
135

    
136
/*
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
	if(!is_ipaddr($rg))
146
		return false;
147

    
148
	return $rg;
149
}
150

    
151
/*
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
		case "none":
167
		case "mobile":
168
			return "0.0.0.0/0";
169
		default:
170
			$address = get_interface_ip($idinfo['type']);
171
			$netbits = get_interface_subnet($idinfo['type']);
172
			$address = gen_subnet($address,$netbits);
173
			return $address."/".$netbits;
174
    }
175
}
176

    
177
/*
178
 * Return phase2 idinfo in address/netmask format
179
 */
180
function ipsec_idinfo_to_subnet(& $idinfo,$addrbits = false) {
181
	global $config;
182

    
183
	switch ($idinfo['type'])
184
	{
185
		case "address":
186
			if ($addrbits)
187
				return $idinfo['address']."/255.255.255.255";
188
			else
189
				return $idinfo['address'];
190
		case "none":
191
		case "network":
192
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
193
		case "mobile":
194
			return "0.0.0.0/0";
195
		default:
196
			$address = get_interface_ip($idinfo['type']);
197
			$netbits = get_interface_subnet($idinfo['type']);
198
			$address = gen_subnet($address,$netbits);
199
			$netbits = gen_subnet_mask($netbits);
200
			return $address."/".netbits;
201
    }
202
}
203

    
204
/*
205
 *  Return phase2 idinfo in text format
206
 */
207
function ipsec_idinfo_to_text(& $idinfo) {
208

    
209
    switch ($idinfo['type'])
210
    {
211
        case "address":
212
            return $idinfo['address'];
213
        case "network":
214
            return $idinfo['address']."/".$idinfo['netbits'];
215
	case "mobile":
216
		return "Mobile Client";
217
	case "none":
218
		return "None";
219
        default:
220
            return strtoupper($idinfo['type']);
221
    }
222
}
223

    
224
/*
225
 * Return phase1 association for phase2
226
 */
227
function ipsec_lookup_phase1(& $ph2ent,& $ph1ent)
228
{
229
    global $config;
230
    $a_phase1 = $config['ipsec']['phase1'];
231

    
232
    if (is_array($a_phase1) && count($a_phase1)) {
233
        foreach ($a_phase1 as $ph1tmp) {
234
            if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) {
235
                $ph1ent = $ph1tmp;
236
                return $ph1ent;
237
            }
238
        }
239
    }
240

    
241
    return false;
242
}
243

    
244
/*
245
 * Check phase1 communications status
246
 */
247
function ipsec_phase1_status(& $ph1ent) {
248

    
249
	$loc_ip = get_ipsec_tunnel_src($ph1ent);
250
	$rmt_ip = $ph1ent['remote-gateway'];
251

    
252
	if(ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
253
		return true;
254

    
255
	return false;
256
}
257

    
258
/*
259
 * Check phase2 communications status
260
 */
261
function ipsec_phase2_status(& $spd,& $sad,& $ph1ent,& $ph2ent) {
262

    
263
	$loc_ip = ipsec_get_phase1_src($ph1ent);
264
	$rmt_ip = gethostbyname(ipsec_get_phase1_dst($ph1ent));
265

    
266
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true);
267
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true);
268

    
269
	/* check for established SA in both directions */
270
	if( ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id) &&
271
		ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$loc_id))
272
		return true;
273

    
274
	return false;
275
}
276

    
277
/*
278
 * Return ISAKMP SA details
279
 */
280
function ipsec_lookup_isakmp_sa($in_srcip,$in_dstip) {
281
	/* TODO : use racconctl to lookup iskamp SA */
282
	return NULL;
283
}
284

    
285
/*
286
 * Return IPsec SA details
287
 */
288
function ipsec_lookup_ipsec_sa(& $spd,& $sad,$dir,$in_srcip,$in_dstip,$in_srcid,$in_dstid) {
289

    
290
	/* match the phase1/2 to an SP */
291

    
292
	foreach($spd as $sp) {
293

    
294
		/* match direction */
295

    
296
		if($dir != $sp['dir'])
297
			continue;
298

    
299
		/* match IPs */
300

    
301
		if($in_srcip != $sp['src'])
302
			continue;
303
		if($in_dstip != $sp['dst'])
304
			continue;
305

    
306
		/* add netbits for address IDs */
307

    
308
		$sp_srcid = $sp['srcid'];
309
		$sp_dstid = $sp['dstid'];
310

    
311
		if (!strstr($sp_srcid,"/"))
312
			$sp_srcid .= '/32';
313
		if (!strstr($sp_dstid,"/"))
314
			$sp_dstid .= '/32';
315

    
316
		/* match IDs */
317

    
318
		if($in_srcid != $sp_srcid)
319
			continue;
320
		if($in_dstid != $sp_dstid)
321
			continue;
322

    
323
		/* match the SP to a unique SA by reqid */
324

    
325
		foreach($sad as $sa) {
326

    
327
			/* match REQIDs */
328

    
329
			if($sa[reqid] != $sp[reqid])
330
				continue;
331

    
332
			/* sanitize for NAT-T ports */
333

    
334
			$sa_srcip = $sa['src'];
335
			$sa_dstip = $sa['dst'];
336

    
337
			if (strstr($sa_srcip,"["))
338
				$sa_srcip = substr($sa_srcip,0,strcspn($sa_srcip,"["));
339
			if (strstr($sa_dstip,"["))
340
				$sa_dstip = substr($sa_dstip,0,strcspn($sa_dstip,"["));
341

    
342
			/* match IPs */
343

    
344
			if($in_srcip != $sa_srcip)
345
				continue;
346
			if($in_dstip != $sa_dstip)
347
				continue;
348

    
349
			return $sa;
350
		}
351
	}
352

    
353
	return NULL;
354
}
355

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

    
403
	return $spd;
404
}
405

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

    
453
	return $sad;
454
}
455

    
456
function ipsec_mobilekey_sort() {
457
	global $config;
458

    
459
	function mobilekeycmp($a, $b) {
460
		return strcmp($a['ident'][0], $b['ident'][0]);
461
	}
462

    
463
	usort($config['ipsec']['mobilekey'], "mobilekeycmp");
464
}
465

    
466
?>
(23-23/54)