Project

General

Profile

Download (10.9 KB) Statistics
| Branch: | Tag: | Revision:
1 a93e56c5 Matthew Grooms
<?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
33 3462a529 Matthew Grooms
/* IPsec defines */
34
$my_identifier_list = array(
35
	'myaddress' => array( 'desc' => 'My IP address', 'mobile' => true ),
36
	'address' => array( 'desc' => 'IP address', 'mobile' => true ),
37
	'fqdn' => array( 'desc' => 'Distinguished name', 'mobile' => true ),
38
	'user_fqdn' => array( 'desc' => 'User distinguished name', 'mobile' => true ),
39
	'asn1dn' => array( 'desc' => 'ASN.1 distinguished Name', 'mobile' => true ),
40
	'keyid tag' => array( 'desc' => 'KeyID tag', 'mobile' => true ),
41
	'dyn_dns' => array( 'desc' => 'Dynamic DNS', 'mobile' => true ));
42
43
$peer_identifier_list = array(
44
	'peeraddress' => array( 'desc' => 'Peer IP address', 'mobile' => false ),
45
	'address' => array( 'desc' => 'IP address', 'mobile' => false ),
46
	'fqdn' => array( 'desc' => 'Distinguished name', 'mobile' => true ),
47
	'user_fqdn' => array( 'desc' => 'User distinguished name', 'mobile' => true ),
48
	'asn1dn' => array( 'desc' => 'ASN.1 distinguished Name', 'mobile' => true ),
49
	'keyid tag' => array( 'desc' =>'KeyID tag', 'mobile' => true ));
50
51
$p1_ealgos = array(
52
	'aes' => array( 'name' => 'AES', 'keysel' => array( 'lo' => 128, 'hi' => 256, 'step' => 64 ) ),
53
	'blowfish' => array( 'name' => 'Blowfish', 'keysel' => array( 'lo' => 128, 'hi' => 256, 'step' => 8 ) ),
54
	'3des' => array( 'name' => '3DES' ),
55
	'cast128' => array( 'name' => 'CAST128' ),
56
	'des' => array( 'name' => 'DES' ));
57
58
$p2_ealgos = array(
59
	'aes' => array( 'name' => 'AES', 'keysel' => array( 'lo' => 128, 'hi' => 256, 'step' => 64 ) ),
60
	'blowfish' => array( 'name' => 'Blowfish', 'keysel' => array( 'lo' => 128, 'hi' => 256, 'step' => 8 ) ),
61
	'3des' => array( 'name' => '3DES' ),
62
	'cast128' => array( 'name' => 'CAST128' ),
63
	'des' => array( 'name' => 'DES' ));
64
65
$p1_halgos = array(
66
	'sha1' => 'SHA1',
67
	'md5' => 'MD5');
68
69
$p2_halgos = array(
70
	'hmac_sha1' => 'SHA1',
71
	'hmac_md5' => 'MD5');
72
73
$p1_authentication_methods = array(
74
	'hybrid_rsa_server' => array( 'name' => 'Hybrid RSA + Xauth', 'mobile' => true ),
75
	'xauth_rsa_server' => array( 'name' => 'Mutual RSA + Xauth', 'mobile' => true ),
76
	'xauth_psk_server' => array( 'name' => 'Mutual PSK + Xauth', 'mobile' => true ),
77
	'rsasig' => array( 'name' => 'Mutual RSA', 'mobile' => false ),
78
	'pre_shared_key' => array( 'name' => 'Mutual PSK', 'mobile' => false ) );
79
80 4b96b367 mgrooms
$p2_modes = array(
81
	'tunnel' => 'Tunnel',
82
	'transport' => 'Transport');
83
84 3462a529 Matthew Grooms
$p2_protos = array(
85
	'esp' => 'ESP',
86
	'ah' => 'AH');
87
88
$p2_pfskeygroups = array(
89
	'0' => 'off',
90
	'1' => '1',
91
	'2' => '2',
92
	'5' => '5');
93
94 d799787e Matthew Grooms
/*
95
 * ikeid management functions
96
 */
97
98
function ipsec_ikeid_used($ikeid) {
99
	global $config;
100
101
	foreach ($config['ipsec']['phase1'] as $ph1ent)
102
		if( $ikeid == $ph1ent['ikeid'] )
103
			return true;
104
105
	return false;
106
}
107
108
function ipsec_ikeid_next() {
109
110
	$ikeid = 1;
111
	while(ipsec_ikeid_used($ikeid))
112
		$ikeid++;
113
114
	return $ikeid;
115
}
116
117 a93e56c5 Matthew Grooms
/*
118
 * Return phase1 local address
119
 */
120
function ipsec_get_phase1_src(& $ph1ent) {
121
122
	if ($ph1ent['interface'])
123
		$if = $ph1ent['interface'];
124
	else
125
		$if = "WAN";
126
127
	$realinterface = convert_friendly_interface_to_real_interface_name($if);
128
	$interfaceip = find_interface_ip($realinterface);
129
130
	return $interfaceip;
131
}
132
133 3462a529 Matthew Grooms
/*
134
 * Return phase1 local address
135
 */
136
function ipsec_get_phase1_dst(& $ph1ent) {
137
138
	$rg = $ph1ent['remote-gateway'];
139
	if (!is_ipaddr($rg))
140
		return resolve_retry($rg);
141
142 0af7398a Matthew Grooms
	if(!is_ipaddr($rg))
143
		return false;
144
145 3462a529 Matthew Grooms
	return $rg;
146
}
147
148 a93e56c5 Matthew Grooms
/*
149
 * Return phase2 idinfo in cidr format
150
 */
151
function ipsec_idinfo_to_cidr(& $idinfo,$addrbits = false) {
152
	global $config;
153
154
	switch ($idinfo['type'])
155
	{
156
		case "address":
157
			if ($addrbits)
158
				return $idinfo['address']."/32";
159
			else
160
				return $idinfo['address'];
161
		case "network":
162
			return $idinfo['address']."/".$idinfo['netbits'];
163 3462a529 Matthew Grooms
		case "mobile":
164
			return "0.0.0.0/0";
165 a55e9c70 Ermal Lu?i
		default:
166
			$address = get_interface_ip($idinfo['type']);
167
			$netbits = get_interface_subnet($idinfo['type']);
168 a93e56c5 Matthew Grooms
			$address = gen_subnet($address,$netbits);
169
			return $address."/".$netbits;
170
    }
171
}
172
173
/*
174
 * Return phase2 idinfo in address/netmask format
175
 */
176
function ipsec_idinfo_to_subnet(& $idinfo,$addrbits = false) {
177
	global $config;
178
179
	switch ($idinfo['type'])
180
	{
181
		case "address":
182
			if ($addrbits)
183
				return $idinfo['address']."/255.255.255.255";
184
			else
185
				return $idinfo['address'];
186
		case "network":
187
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
188 3462a529 Matthew Grooms
		case "mobile":
189
			return "0.0.0.0/0";
190 a93e56c5 Matthew Grooms
        default:
191 a55e9c70 Ermal Lu?i
			$address = get_interface_ip($idinfo['type']);
192
			$netbits = get_interface_subnet($idinfo['type']);
193 a93e56c5 Matthew Grooms
			$address = gen_subnet($address,$netbits);
194
			$netbits = gen_subnet_mask($netbits);
195
			return $address."/".netbits;
196
    }
197
}
198
199
/*
200
 *  Return phase2 idinfo in text format
201
 */
202
function ipsec_idinfo_to_text(& $idinfo) {
203
204
    switch ($idinfo['type'])
205
    {
206
        case "address":
207
            return $idinfo['address'];
208
        case "network":
209
            return $idinfo['address']."/".$idinfo['netbits'];
210 3462a529 Matthew Grooms
		case "mobile":
211
			return "Mobile Client";
212 a93e56c5 Matthew Grooms
        default:
213
            return strtoupper($idinfo['type']);
214
    }
215
}
216
217
/*
218
 * Return phase1 association for phase2
219
 */
220
function ipsec_lookup_phase1(& $ph2ent,& $ph1ent)
221
{
222
    global $config;
223
    $a_phase1 = $config['ipsec']['phase1'];
224
225
    if (is_array($a_phase1) && count($a_phase1)) {
226
        foreach ($a_phase1 as $ph1tmp) {
227
            if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) {
228
                $ph1ent = $ph1tmp;
229
                return $ph1ent;
230
            }
231
        }
232
    }
233
234
    return false;
235
}
236
237
/*
238
 * Check phase1 communications status
239
 */
240
function ipsec_phase1_status(& $ph1ent) {
241
242
	$loc_ip = get_ipsec_tunnel_src($ph1ent);
243
	$rmt_ip = $ph1ent['remote-gateway'];
244
245
	if(ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
246
		return true;
247
248
	return false;
249
}
250
251
/*
252
 * Check phase2 communications status
253
 */
254
function ipsec_phase2_status(& $spd,& $sad,& $ph1ent,& $ph2ent) {
255
256
	$loc_ip = ipsec_get_phase1_src($ph1ent);
257 30bc15cf Bill Marquette
	$rmt_ip = gethostbyname(ipsec_get_phase1_dst($ph1ent));
258 a93e56c5 Matthew Grooms
259
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true);
260
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true);
261
262
	/* check for established SA in both directions */
263
	if( ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id) &&
264
		ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$loc_id))
265
		return true;
266
267
	return false;
268
}
269
270
/*
271
 * Return ISAKMP SA details
272
 */
273
function ipsec_lookup_isakmp_sa($in_srcip,$in_dstip) {
274
	/* TODO : use racconctl to lookup iskamp SA */
275
	return NULL;
276
}
277
278
/*
279
 * Return IPsec SA details
280
 */
281
function ipsec_lookup_ipsec_sa(& $spd,& $sad,$dir,$in_srcip,$in_dstip,$in_srcid,$in_dstid) {
282
283
	/* match the phase1/2 to an SP */
284
285
	foreach($spd as $sp) {
286
287
		/* match direction */
288
289
		if($dir != $sp['dir'])
290
			continue;
291
292
		/* match IPs */
293
294
		if($in_srcip != $sp['src'])
295
			continue;
296
		if($in_dstip != $sp['dst'])
297
			continue;
298
299
		/* add netbits for address IDs */
300
301
		$sp_srcid = $sp['srcid'];
302
		$sp_dstid = $sp['dstid'];
303
304
		if (!strstr($sp_srcid,"/"))
305
			$sp_srcid .= '/32';
306
		if (!strstr($sp_dstid,"/"))
307
			$sp_dstid .= '/32';
308
309
		/* match IDs */
310
311
		if($in_srcid != $sp_srcid)
312
			continue;
313
		if($in_dstid != $sp_dstid)
314
			continue;
315
316
		/* match the SP to a unique SA by reqid */
317
318
		foreach($sad as $sa) {
319
320
			/* match REQIDs */
321
322
			if($sa[reqid] != $sp[reqid])
323
				continue;
324
325
			/* sanitize for NAT-T ports */
326
327
			$sa_srcip = $sa['src'];
328
			$sa_dstip = $sa['dst'];
329
330
			if (strstr($sa_srcip,"["))
331
				$sa_srcip = substr($sa_srcip,0,strcspn($sa_srcip,"["));
332
			if (strstr($sa_dstip,"["))
333
				$sa_dstip = substr($sa_dstip,0,strcspn($sa_dstip,"["));
334
335
			/* match IPs */
336
337
			if($in_srcip != $sa_srcip)
338
				continue;
339
			if($in_dstip != $sa_dstip)
340
				continue;
341
342
			return $sa;
343
		}
344
	}
345
346
	return NULL;
347
}
348
349
/*
350
 * Return dump of SPD table
351
 */
352
function ipsec_dump_spd()
353
{
354
	$fd = @popen("/usr/local/sbin/setkey -DP", "r");
355
	$spd = array();
356
	if ($fd) {
357
		while (!feof($fd)) {
358
			$line = chop(fgets($fd));
359
			if (!$line)
360
				continue;
361
			if ($line == "No SPD entries.")
362
				break;
363
			if ($line[0] != "\t") {
364
				if (is_array($cursp))
365
					$spd[] = $cursp;
366
				$cursp = array();
367
				$linea = explode(" ", $line);
368
				$cursp['srcid'] = substr($linea[0], 0, strpos($linea[0], "["));
369
				$cursp['dstid'] = substr($linea[1], 0, strpos($linea[1], "["));
370
				$i = 0;
371
			} else if (is_array($cursp)) {
372
				$linea = explode(" ", trim($line));
373
				switch($i)
374
				{
375
					case 1:
376
						if ($linea[1] == "none")	/* don't show default anti-lockout rule */
377
							unset($cursp);
378
						else
379
							$cursp['dir'] = $linea[0];
380
						break;
381
					case 2:
382
						$upperspec = explode("/", $linea[0]);
383
						$cursp['proto'] = $upperspec[0];
384
						list($cursp['src'], $cursp['dst']) = explode("-", $upperspec[2]);
385
						$cursp['reqid'] =  substr($upperspec[3], strpos($upperspec[3], "#")+1);
386
						break;
387
				}
388
			}
389
			$i++;
390
		}
391
		if (is_array($cursp) && count($cursp))
392
			$spd[] = $cursp;
393
		pclose($fd);
394
	}
395
396
	return $spd;
397
}
398
399
/*
400
 * Return dump of SAD table
401
 */
402
function ipsec_dump_sad()
403
{
404
	$fd = @popen("/usr/local/sbin/setkey -D", "r");
405
	$sad = array();
406
	if ($fd) {
407
		while (!feof($fd)) {
408
			$line = chop(fgets($fd));
409
			if (!$line)
410
				continue;
411
			if ($line == "No SAD entries.")
412
				break;
413
			if ($line[0] != "\t")
414
			{
415
				if (is_array($cursa))
416
					$sad[] = $cursa;
417
				$cursa = array();
418
				list($cursa['src'],$cursa['dst']) = explode(" ", $line);
419
				$i = 0;
420
			}
421
			else
422
			{
423
				$linea = explode(" ", trim($line));
424
				switch ($i) {
425
					case 1:
426
						$cursa['proto'] = $linea[0];
427
						$cursa['spi'] = substr($linea[2], strpos($linea[2], "x")+1, -1);
428
						$reqid = substr($linea[3], strpos($linea[3], "=")+1);
429
						$cursa['reqid'] = substr($reqid, 0, strcspn($reqid,"("));
430
						break;
431
					case 2:
432
						$cursa['ealgo'] = $linea[1];
433
						break;
434
					case 3:
435
						$cursa['aalgo'] = $linea[1];
436
						break;
437
				}
438
			}
439
			$i++;
440
		}
441
		if (is_array($cursa) && count($cursa))
442
			$sad[] = $cursa;
443
		pclose($fd);
444
	}
445
446
	return $sad;
447
}
448
449
?>