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

    
33
/* 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
$p2_protos = array(
81
	'esp' => 'ESP',
82
	'ah' => 'AH');
83

    
84
$p2_pfskeygroups = array(
85
	'0' => 'off',
86
	'1' => '1',
87
	'2' => '2',
88
	'5' => '5');
89

    
90
/*
91
 * ikeid management functions
92
 */
93

    
94
function ipsec_ikeid_used($ikeid) {
95
	global $config;
96

    
97
	foreach ($config['ipsec']['phase1'] as $ph1ent)
98
		if( $ikeid == $ph1ent['ikeid'] )
99
			return true;
100

    
101
	return false;
102
}
103

    
104
function ipsec_ikeid_next() {
105

    
106
	$ikeid = 1;
107
	while(ipsec_ikeid_used($ikeid))
108
		$ikeid++;
109

    
110
	return $ikeid;
111
}
112

    
113
/*
114
 * Return phase1 local address
115
 */
116
function ipsec_get_phase1_src(& $ph1ent) {
117

    
118
	if ($ph1ent['interface'])
119
		$if = $ph1ent['interface'];
120
	else
121
		$if = "WAN";
122

    
123
	$realinterface = convert_friendly_interface_to_real_interface_name($if);
124
	$interfaceip = find_interface_ip($realinterface);
125

    
126
	return $interfaceip;
127
}
128

    
129
/*
130
 * Return phase1 local address
131
 */
132
function ipsec_get_phase1_dst(& $ph1ent) {
133

    
134
	$rg = $ph1ent['remote-gateway'];
135
	if (!is_ipaddr($rg))
136
		return resolve_retry($rg);
137

    
138
	if(!is_ipaddr($rg))
139
		return false;
140

    
141
	return $rg;
142
}
143

    
144
/*
145
 * Return phase2 idinfo in cidr format
146
 */
147
function ipsec_idinfo_to_cidr(& $idinfo,$addrbits = false) {
148
	global $config;
149

    
150
	switch ($idinfo['type'])
151
	{
152
		case "address":
153
			if ($addrbits)
154
				return $idinfo['address']."/32";
155
			else
156
				return $idinfo['address'];
157
		case "network":
158
			return $idinfo['address']."/".$idinfo['netbits'];
159
		case "mobile":
160
			return "0.0.0.0/0";
161
        default:
162
			$address = $config['interfaces']['lan']['ipaddr'];
163
			$netbits = $config['interfaces'][$idinfo['type']]['subnet'];
164
			$address = gen_subnet($address,$netbits);
165
			return $address."/".$netbits;
166
    }
167
}
168

    
169
/*
170
 * Return phase2 idinfo in address/netmask format
171
 */
172
function ipsec_idinfo_to_subnet(& $idinfo,$addrbits = false) {
173
	global $config;
174

    
175
	switch ($idinfo['type'])
176
	{
177
		case "address":
178
			if ($addrbits)
179
				return $idinfo['address']."/255.255.255.255";
180
			else
181
				return $idinfo['address'];
182
		case "network":
183
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
184
		case "mobile":
185
			return "0.0.0.0/0";
186
        default:
187
			$address = $config['interfaces']['lan']['ipaddr'];
188
			$netbits = $config['interfaces'][$idinfo['type']]['subnet'];
189
			$address = gen_subnet($address,$netbits);
190
			$netbits = gen_subnet_mask($netbits);
191
			return $address."/".netbits;
192
    }
193
}
194

    
195
/*
196
 *  Return phase2 idinfo in text format
197
 */
198
function ipsec_idinfo_to_text(& $idinfo) {
199

    
200
    switch ($idinfo['type'])
201
    {
202
        case "address":
203
            return $idinfo['address'];
204
        case "network":
205
            return $idinfo['address']."/".$idinfo['netbits'];
206
		case "mobile":
207
			return "Mobile Client";
208
        default:
209
            return strtoupper($idinfo['type']);
210
    }
211
}
212

    
213
/*
214
 * Return phase1 association for phase2
215
 */
216
function ipsec_lookup_phase1(& $ph2ent,& $ph1ent)
217
{
218
    global $config;
219
    $a_phase1 = $config['ipsec']['phase1'];
220

    
221
    if (is_array($a_phase1) && count($a_phase1)) {
222
        foreach ($a_phase1 as $ph1tmp) {
223
            if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) {
224
                $ph1ent = $ph1tmp;
225
                return $ph1ent;
226
            }
227
        }
228
    }
229

    
230
    return false;
231
}
232

    
233
/*
234
 * Check phase1 communications status
235
 */
236
function ipsec_phase1_status(& $ph1ent) {
237

    
238
	$loc_ip = get_ipsec_tunnel_src($ph1ent);
239
	$rmt_ip = $ph1ent['remote-gateway'];
240

    
241
	if(ipsec_lookup_ipsakmp_sa($loc_ip,$rmt_ip))
242
		return true;
243

    
244
	return false;
245
}
246

    
247
/*
248
 * Check phase2 communications status
249
 */
250
function ipsec_phase2_status(& $spd,& $sad,& $ph1ent,& $ph2ent) {
251

    
252
	$loc_ip = ipsec_get_phase1_src($ph1ent);
253
	$rmt_ip = $ph1ent['remote-gateway'];
254

    
255
	$loc_id = ipsec_idinfo_to_cidr($ph2ent['localid'],true);
256
	$rmt_id = ipsec_idinfo_to_cidr($ph2ent['remoteid'],true);
257

    
258
	/* check for established SA in both directions */
259
	if( ipsec_lookup_ipsec_sa($spd,$sad,"out",$loc_ip,$rmt_ip,$loc_id,$rmt_id) &&
260
		ipsec_lookup_ipsec_sa($spd,$sad,"in",$rmt_ip,$loc_ip,$rmt_id,$loc_id))
261
		return true;
262

    
263
	return false;
264
}
265

    
266
/*
267
 * Return ISAKMP SA details
268
 */
269
function ipsec_lookup_isakmp_sa($in_srcip,$in_dstip) {
270
	/* TODO : use racconctl to lookup iskamp SA */
271
	return NULL;
272
}
273

    
274
/*
275
 * Return IPsec SA details
276
 */
277
function ipsec_lookup_ipsec_sa(& $spd,& $sad,$dir,$in_srcip,$in_dstip,$in_srcid,$in_dstid) {
278

    
279
	/* match the phase1/2 to an SP */
280

    
281
	foreach($spd as $sp) {
282

    
283
		/* match direction */
284

    
285
		if($dir != $sp['dir'])
286
			continue;
287

    
288
		/* match IPs */
289

    
290
		if($in_srcip != $sp['src'])
291
			continue;
292
		if($in_dstip != $sp['dst'])
293
			continue;
294

    
295
		/* add netbits for address IDs */
296

    
297
		$sp_srcid = $sp['srcid'];
298
		$sp_dstid = $sp['dstid'];
299

    
300
		if (!strstr($sp_srcid,"/"))
301
			$sp_srcid .= '/32';
302
		if (!strstr($sp_dstid,"/"))
303
			$sp_dstid .= '/32';
304

    
305
		/* match IDs */
306

    
307
		if($in_srcid != $sp_srcid)
308
			continue;
309
		if($in_dstid != $sp_dstid)
310
			continue;
311

    
312
		/* match the SP to a unique SA by reqid */
313

    
314
		foreach($sad as $sa) {
315

    
316
			/* match REQIDs */
317

    
318
			if($sa[reqid] != $sp[reqid])
319
				continue;
320

    
321
			/* sanitize for NAT-T ports */
322

    
323
			$sa_srcip = $sa['src'];
324
			$sa_dstip = $sa['dst'];
325

    
326
			if (strstr($sa_srcip,"["))
327
				$sa_srcip = substr($sa_srcip,0,strcspn($sa_srcip,"["));
328
			if (strstr($sa_dstip,"["))
329
				$sa_dstip = substr($sa_dstip,0,strcspn($sa_dstip,"["));
330

    
331
			/* match IPs */
332

    
333
			if($in_srcip != $sa_srcip)
334
				continue;
335
			if($in_dstip != $sa_dstip)
336
				continue;
337

    
338
			return $sa;
339
		}
340
	}
341

    
342
	return NULL;
343
}
344

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

    
392
	return $spd;
393
}
394

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

    
442
	return $sad;
443
}
444

    
445
?>
(16-16/37)