Project

General

Profile

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

    
147
	$rg = $ph1ent['remote-gateway'];
148
	if (!is_ipaddr($rg))
149
		return resolve_retry($rg);
150

    
151
	if(!is_ipaddr($rg))
152
		return false;
153

    
154
	return $rg;
155
}
156

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

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

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

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

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

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

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

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

    
247
    return false;
248
}
249

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

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

    
258
	if(ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
259
		return true;
260

    
261
	return false;
262
}
263

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

    
269
	$loc_ip = ipsec_get_phase1_src($ph1ent);
270
	$rmt_ip = gethostbyname(ipsec_get_phase1_dst($ph1ent));
271

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

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

    
280
	return false;
281
}
282

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

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

    
296
	/* match the phase1/2 to an SP */
297

    
298
	foreach($spd as $sp) {
299

    
300
		/* match direction */
301

    
302
		if($dir != $sp['dir'])
303
			continue;
304

    
305
		/* match IPs */
306

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

    
312
		/* add netbits for address IDs */
313

    
314
		$sp_srcid = $sp['srcid'];
315
		$sp_dstid = $sp['dstid'];
316

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

    
322
		/* match IDs */
323

    
324
		if($in_srcid != $sp_srcid)
325
			continue;
326
		if($in_dstid != $sp_dstid)
327
			continue;
328

    
329
		/* match the SP to a unique SA by reqid */
330

    
331
		foreach($sad as $sa) {
332

    
333
			/* match REQIDs */
334

    
335
			if($sa[reqid] != $sp[reqid])
336
				continue;
337

    
338
			/* sanitize for NAT-T ports */
339

    
340
			$sa_srcip = $sa['src'];
341
			$sa_dstip = $sa['dst'];
342

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

    
348
			/* match IPs */
349

    
350
			if($in_srcip != $sa_srcip)
351
				continue;
352
			if($in_dstip != $sa_dstip)
353
				continue;
354

    
355
			return $sa;
356
		}
357
	}
358

    
359
	return NULL;
360
}
361

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

    
409
	return $spd;
410
}
411

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

    
459
	return $sad;
460
}
461

    
462
function ipsec_mobilekey_sort() {
463
	global $config;
464

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

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

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

    
476
	$nbph2=0;
477

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

    
486
	return $nbph2;
487
}
488

    
489
?>
(26-26/61)