Project

General

Profile

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

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

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

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

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

    
220
/*
221
 * Return phase1 association for phase2
222
 */
223
function ipsec_lookup_phase1(& $ph2ent,& $ph1ent)
224
{
225
    global $config;
226
    $a_phase1 = $config['ipsec']['phase1'];
227

    
228
    if (is_array($a_phase1) && count($a_phase1)) {
229
        foreach ($a_phase1 as $ph1tmp) {
230
            if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) {
231
                $ph1ent = $ph1tmp;
232
                return $ph1ent;
233
            }
234
        }
235
    }
236

    
237
    return false;
238
}
239

    
240
/*
241
 * Check phase1 communications status
242
 */
243
function ipsec_phase1_status(& $ph1ent) {
244

    
245
	$loc_ip = get_ipsec_tunnel_src($ph1ent);
246
	$rmt_ip = $ph1ent['remote-gateway'];
247

    
248
	if(ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
249
		return true;
250

    
251
	return false;
252
}
253

    
254
/*
255
 * Check phase2 communications status
256
 */
257
function ipsec_phase2_status(& $spd,& $sad,& $ph1ent,& $ph2ent) {
258

    
259
	$loc_ip = ipsec_get_phase1_src($ph1ent);
260
	$rmt_ip = gethostbyname(ipsec_get_phase1_dst($ph1ent));
261

    
262
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true);
263
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true);
264

    
265
	/* check for established SA in both directions */
266
	if( ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id) &&
267
		ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$loc_id))
268
		return true;
269

    
270
	return false;
271
}
272

    
273
/*
274
 * Return ISAKMP SA details
275
 */
276
function ipsec_lookup_isakmp_sa($in_srcip,$in_dstip) {
277
	/* TODO : use racconctl to lookup iskamp SA */
278
	return NULL;
279
}
280

    
281
/*
282
 * Return IPsec SA details
283
 */
284
function ipsec_lookup_ipsec_sa(& $spd,& $sad,$dir,$in_srcip,$in_dstip,$in_srcid,$in_dstid) {
285

    
286
	/* match the phase1/2 to an SP */
287

    
288
	foreach($spd as $sp) {
289

    
290
		/* match direction */
291

    
292
		if($dir != $sp['dir'])
293
			continue;
294

    
295
		/* match IPs */
296

    
297
		if($in_srcip != $sp['src'])
298
			continue;
299
		if($in_dstip != $sp['dst'])
300
			continue;
301

    
302
		/* add netbits for address IDs */
303

    
304
		$sp_srcid = $sp['srcid'];
305
		$sp_dstid = $sp['dstid'];
306

    
307
		if (!strstr($sp_srcid,"/"))
308
			$sp_srcid .= '/32';
309
		if (!strstr($sp_dstid,"/"))
310
			$sp_dstid .= '/32';
311

    
312
		/* match IDs */
313

    
314
		if($in_srcid != $sp_srcid)
315
			continue;
316
		if($in_dstid != $sp_dstid)
317
			continue;
318

    
319
		/* match the SP to a unique SA by reqid */
320

    
321
		foreach($sad as $sa) {
322

    
323
			/* match REQIDs */
324

    
325
			if($sa[reqid] != $sp[reqid])
326
				continue;
327

    
328
			/* sanitize for NAT-T ports */
329

    
330
			$sa_srcip = $sa['src'];
331
			$sa_dstip = $sa['dst'];
332

    
333
			if (strstr($sa_srcip,"["))
334
				$sa_srcip = substr($sa_srcip,0,strcspn($sa_srcip,"["));
335
			if (strstr($sa_dstip,"["))
336
				$sa_dstip = substr($sa_dstip,0,strcspn($sa_dstip,"["));
337

    
338
			/* match IPs */
339

    
340
			if($in_srcip != $sa_srcip)
341
				continue;
342
			if($in_dstip != $sa_dstip)
343
				continue;
344

    
345
			return $sa;
346
		}
347
	}
348

    
349
	return NULL;
350
}
351

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

    
399
	return $spd;
400
}
401

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

    
449
	return $sad;
450
}
451

    
452
?>
(22-22/50)