Project

General

Profile

Download (105 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * ipsec.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2008 Shrew Soft Inc.
7
 * Copyright (c) 2007-2013 BSD Perimeter
8
 * Copyright (c) 2013-2016 Electric Sheep Fencing
9
 * Copyright (c) 2014-2021 Rubicon Communications, LLC (Netgate)
10
 * All rights reserved.
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 * http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24

    
25
require_once("filter.inc");
26
require_once("auth.inc");
27
require_once("certs.inc");
28
require_once("interfaces.inc");
29

    
30
/* IPsec defines */
31

    
32
global $ipsec_conid_prefix;
33
$ipsec_conid_prefix = 'con';
34

    
35
global $ipsec_conid_separator;
36
$ipsec_conid_separator = '_';
37

    
38
/* Leave plenty of room for dynamically allocated reqids */
39
global $ipsec_reqid_base;
40
$ipsec_reqid_base = 5000;
41

    
42
global $ipsec_loglevels;
43
$ipsec_loglevels = array(
44
	"dmn" => gettext("Daemon"),
45
	"mgr" => gettext("SA Manager"),
46
	"ike" => gettext("IKE SA"),
47
	"chd" => gettext("IKE Child SA"),
48
	"job" => gettext("Job Processing"),
49
	"cfg" => gettext("Configuration backend"),
50
	"knl" => gettext("Kernel Interface"),
51
	"net" => gettext("Networking"),
52
	"asn" => gettext("ASN encoding"),
53
	"enc" => gettext("Message encoding"),
54
	"imc" => gettext("Integrity checker"),
55
	"imv" => gettext("Integrity Verifier"),
56
	"pts" => gettext("Platform Trust Service"),
57
	"tls" => gettext("TLS handler"),
58
	"esp" => gettext("IPsec traffic"),
59
	"lib" => gettext("StrongSwan Lib")
60
);
61

    
62
global $ipsec_log_sevs;
63
$ipsec_log_sevs = array(
64
	'-1' => gettext('Silent'),
65
	'0' => gettext('Audit'),
66
	'1' => gettext('Control'),
67
	'2' => gettext('Diag'),
68
	'3' => gettext('Raw'),
69
	'4' => gettext('Highest')
70
);
71

    
72
global $ipsec_log_cats;
73
$ipsec_log_cats = array(
74
	"dmn" => gettext("Daemon"),
75
	"mgr" => gettext("SA Manager"),
76
	"ike" => gettext("IKE SA"),
77
	"chd" => gettext("IKE Child SA"),
78
	"job" => gettext("Job Processing"),
79
	"cfg" => gettext("Configuration backend"),
80
	"knl" => gettext("Kernel Interface"),
81
	"net" => gettext("Networking"),
82
	"asn" => gettext("ASN encoding"),
83
	"enc" => gettext("Message encoding"),
84
	"imc" => gettext("Integrity checker"),
85
	"imv" => gettext("Integrity Verifier"),
86
	"pts" => gettext("Platform Trust Service"),
87
	"tls" => gettext("TLS handler"),
88
	"esp" => gettext("IPsec traffic"),
89
	"lib" => gettext("StrongSwan Lib")
90
);
91

    
92
/* For PSK tab, different order based on likely user preference */
93
global $ipsec_identifier_list;
94
$ipsec_identifier_list = array(
95
	'none' => array('desc' => 'Automatic based on content', 'mobile' => true),
96
	'email' => array('desc' => gettext('E-mail address'), 'mobile' => true),
97
	'fqdn' => array('desc' => gettext('Fully qualified domain name'), 'mobile' => true),
98
	'userfqdn' => array('desc' => gettext('User fully qualified domain name'), 'mobile' => true),
99
	'keyid' => array('desc' => gettext('KeyID tag'), 'mobile' => true),
100
	'asn1dn' => array('desc' => gettext('ASN.1 distinguished Name'), 'mobile' => true),
101
);
102

    
103
global $my_identifier_list;
104
$my_identifier_list = array(
105
	'myaddress' => array('desc' => gettext('My IP address'), 'mobile' => true),
106
	'address' => array('desc' => gettext('IP address'), 'mobile' => true),
107
	'fqdn' => array('desc' => gettext('Fully qualified domain name'), 'mobile' => true),
108
	'user_fqdn' => array('desc' => gettext('User fully qualified domain name / E-mail'), 'mobile' => true),
109
	'asn1dn' => array('desc' => gettext('ASN.1 distinguished Name'), 'mobile' => true),
110
	'keyid tag' => array('desc' => gettext('KeyID tag'), 'mobile' => true),
111
	'dyn_dns' => array('desc' => gettext('Dynamic DNS'), 'mobile' => true),
112
	'auto' => array('desc' => gettext('Automatic based on content'), 'mobile' => true),
113
);
114

    
115
global $peer_identifier_list;
116
$peer_identifier_list = array(
117
	'any' => array('desc' => gettext('Any'), 'mobile' => true),
118
	'peeraddress' => array('desc' => gettext('Peer IP address'), 'mobile' => false),
119
	'address' => array('desc' => gettext('IP address'), 'mobile' => false),
120
	'fqdn' => array('desc' => gettext('Fully qualified domain name'), 'mobile' => true),
121
	'user_fqdn' => array('desc' => gettext('User fully qualified domain name / E-mail'), 'mobile' => true),
122
	'asn1dn' => array('desc' => gettext('ASN.1 distinguished Name'), 'mobile' => true),
123
	'keyid tag' => array('desc' =>gettext('KeyID tag'), 'mobile' => true),
124
	'auto' => array('desc' => gettext('Automatic based on content'), 'mobile' => true),
125
);
126

    
127
global $ipsec_idhandling;
128
$ipsec_idhandling = array(
129
	'replace' => 'Yes (Replace)', 'no' => 'No', 'never' => 'Never', 'keep' => 'Keep'
130
);
131

    
132
global $p1_ealgos;
133
$p1_ealgos = array(
134
	'aes' => array('name' => 'AES', 'keysel' => array('lo' => 128, 'hi' => 256, 'step' => 64)),
135
	'aes128gcm' => array('name' => 'AES128-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)),
136
	'aes192gcm' => array('name' => 'AES192-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)),
137
	'aes256gcm' => array('name' => 'AES256-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)),
138
	'blowfish' => array('name' => 'Blowfish', 'keysel' => array('lo' => 128, 'hi' => 256, 'step' => 64)),
139
	'3des' => array('name' => '3DES'),
140
	'cast128' => array('name' => 'CAST128')
141
);
142

    
143
global $p2_ealgos;
144
$p2_ealgos = array(
145
	'aes' => array('name' => 'AES', 'keysel' => array('lo' => 128, 'hi' => 256, 'step' => 64)),
146
	'aes128gcm' => array('name' => 'AES128-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)),
147
	'aes192gcm' => array('name' => 'AES192-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)),
148
	'aes256gcm' => array('name' => 'AES256-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)),
149
	'blowfish' => array('name' => 'Blowfish', 'keysel' => array('lo' => 128, 'hi' => 256, 'step' => 64)),
150
	'3des' => array('name' => '3DES'),
151
	'cast128' => array('name' => 'CAST128')
152
);
153

    
154
global $p1_halgos;
155
$p1_halgos = array(
156
	'md5' => 'MD5',
157
	'sha1' => 'SHA1',
158
	'sha256' => 'SHA256',
159
	'sha384' => 'SHA384',
160
	'sha512' => 'SHA512',
161
	'aesxcbc' => 'AES-XCBC'
162
);
163

    
164
global $p1_dhgroups;
165
$p1_dhgroups = array(
166
	1  => '1 (768 bit)',
167
	2  => '2 (1024 bit)',
168
	5  => '5 (1536 bit)',
169
	14 => '14 (2048 bit)',
170
	15 => '15 (3072 bit)',
171
	16 => '16 (4096 bit)',
172
	17 => '17 (6144 bit)',
173
	18 => '18 (8192 bit)',
174
	19 => '19 (nist ecp256)',
175
	20 => '20 (nist ecp384)',
176
	21 => '21 (nist ecp521)',
177
	22 => '22 (1024(sub 160) bit)',
178
	23 => '23 (2048(sub 224) bit)',
179
	24 => '24 (2048(sub 256) bit)',
180
	25 => '25 (nist ecp192)',
181
	26 => '26 (nist ecp224)',
182
	27 => '27 (brainpool ecp224)',
183
	28 => '28 (brainpool ecp256)',
184
	29 => '29 (brainpool ecp384)',
185
	30 => '30 (brainpool ecp512)',
186
	31 => '31 (Elliptic Curve 25519, 256 bit)',
187
	32 => '32 (Elliptic Curve 25519, 448 bit)',
188
);
189

    
190
global $p2_halgos;
191
$p2_halgos = array(
192
	'hmac_md5' => 'MD5',
193
	'hmac_sha1' => 'SHA1',
194
	'hmac_sha256' => 'SHA256',
195
	'hmac_sha384' => 'SHA384',
196
	'hmac_sha512' => 'SHA512',
197
	'aesxcbc' => 'AES-XCBC'
198
);
199

    
200
global $p1_authentication_methods;
201
$p1_authentication_methods = array(
202
	'hybrid_cert_server' => array('name' => gettext('Hybrid Certificate + Xauth'), 'mobile' => true),
203
	'xauth_cert_server' => array('name' => gettext('Mutual Certificate + Xauth'), 'mobile' => true),
204
	'xauth_psk_server' => array('name' => gettext('Mutual PSK + Xauth'), 'mobile' => true),
205
	'eap-tls' => array('name' => gettext('EAP-TLS'), 'mobile' => true),
206
	'eap-radius' => array('name' => gettext('EAP-RADIUS'), 'mobile' => true),
207
	'eap-mschapv2' => array('name' => gettext('EAP-MSChapv2'), 'mobile' => true),
208
	'cert' => array('name' => gettext('Mutual Certificate'), 'mobile' => false),
209
	'pkcs11' => array('name' => gettext('Mutual Certificate (PKCS#11)'), 'mobile' => false),
210
	'pre_shared_key' => array('name' => gettext('Mutual PSK'), 'mobile' => false)
211
);
212

    
213
global $ipsec_preshared_key_type;
214
$ipsec_preshared_key_type = array(
215
	'PSK' => 'PSK',
216
	'EAP' => 'EAP'
217
);
218

    
219
global $ipsec_startactions;
220
$ipsec_startactions = array(
221
	'' => gettext('Default'),
222
	'none' => gettext('None (Responder Only)'),
223
	'start' => gettext('Initiate at start (VTI or Tunnel Mode)'),
224
	'trap' => gettext('Initiate on demand (Tunnel mode only)'),
225
);
226

    
227
global $ipsec_closeactions;
228
$ipsec_closeactions = array(
229
	'' => gettext('Default'),
230
	'none' => gettext('Close connection and clear SA'),
231
	'start' => gettext('Restart/Reconnect'),
232
	'trap' => gettext('Close connection and reconnect on demand'),
233
);
234

    
235
global $p2_modes;
236
$p2_modes = array(
237
	'tunnel' => gettext('Tunnel IPv4'),
238
	'tunnel6' => gettext('Tunnel IPv6'),
239
	'transport' => gettext('Transport'),
240
	'vti' => gettext('Routed (VTI)')
241
);
242

    
243
global $p2_protos;
244
$p2_protos = array(
245
	'esp' => 'ESP',
246
	'ah' => 'AH'
247
);
248

    
249
global $p2_pfskeygroups;
250
$p2_pfskeygroups = array(
251
	0 => gettext('off'),
252
	1  => gettext('1 (768 bit)'),
253
	2  => gettext('2 (1024 bit)'),
254
	5  => gettext('5 (1536 bit)'),
255
	14 => gettext('14 (2048 bit)'),
256
	15 => gettext('15 (3072 bit)'),
257
	16 => gettext('16 (4096 bit)'),
258
	17 => gettext('17 (6144 bit)'),
259
	18 => gettext('18 (8192 bit)'),
260
	19 => gettext('19 (nist ecp256)'),
261
	20 => gettext('20 (nist ecp384)'),
262
	21 => gettext('21 (nist ecp521)'),
263
	22 => gettext('22 (1024(sub 160) bit)'),
264
	23 => gettext('23 (2048(sub 224) bit)'),
265
	24 => gettext('24 (2048(sub 256) bit)'),
266
	25 => gettext('25 (nist ecp192)'),
267
	26 => gettext('26 (nist ecp224)'),
268
	27 => gettext('27 (brainpool ecp224)'),
269
	28 => gettext('28 (brainpool ecp256)'),
270
	29 => gettext('29 (brainpool ecp384)'),
271
	30 => gettext('30 (brainpool ecp512)'),
272
	31 => gettext('31 (Elliptic Curve 25519, 256 bit)'),
273
	32 => gettext('32 (Elliptic Curve 25519, 448 bit)'),
274
);
275

    
276
function ipsec_enabled() {
277
	global $config;
278

    
279
	if (!isset($config['ipsec']) || !is_array($config['ipsec'])) {
280
		return false;
281
	}
282

    
283
	/* Check if we have at least one phase 1 entry. */
284
	if (!isset($config['ipsec']['phase1']) ||
285
	    !is_array($config['ipsec']['phase1']) ||
286
	    empty($config['ipsec']['phase1'])) {
287
		return false;
288
	}
289
	/* Check if at least one phase 1 entry is enabled. */
290
	foreach ($config['ipsec']['phase1'] as $phase1) {
291
		if (!isset($phase1['disabled'])) {
292
			return true;
293
		}
294
	}
295

    
296
	return false;
297
}
298

    
299
/*
300
 * ikeid management functions
301
 */
302

    
303
function ipsec_ikeid_used($ikeid) {
304
	global $config;
305
	init_config_arr(array('ipsec', 'phase1'));
306
	foreach ($config['ipsec']['phase1'] as $ph1ent) {
307
		if ($ikeid == $ph1ent['ikeid']) {
308
			return true;
309
		}
310
	}
311
	return false;
312
}
313

    
314
function ipsec_ikeid_next() {
315
	global $config;
316
	$ikeid = 1;
317
	init_config_arr(array('ipsec', 'phase1'));
318
	$ipsecikeid = lock('ipsecikeids', LOCK_EX);
319
	$ikeids = array();
320

    
321
	foreach ($config['ipsec']['phase1'] as $ph1) {
322
		$keyids[$ph1['ikeid']] = $ph1['ikeid'];
323
	}
324

    
325
	for ($i = 1; $i < 16000; $i++) {
326
		if (!isset($keyids[$i])) {
327
			$keyid = $i;
328
			break;
329
		}
330
	}
331

    
332
	unlock($ipsecreqid);
333
	return $keyid;
334
}
335

    
336
function ipsec_new_reqid() {
337
	global $config;
338
	init_config_arr(array('ipsec', 'phase2'));
339
	$ipsecreqid = lock('ipsecreqids', LOCK_EX);
340
	$keyids = array();
341
	$keyid = 1;
342

    
343
	foreach ($config['ipsec']['phase2'] as $ph2) {
344
		$keyids[$ph2['reqid']] = $ph2['reqid'];
345
	}
346

    
347
	for ($i = 1; $i < 16000; $i++) {
348
		if (!isset($keyids[$i])) {
349
			$keyid = $i;
350
			break;
351
		}
352
	}
353

    
354
	unlock($ipsecreqid);
355
	return $keyid;
356
}
357

    
358
function ipsec_get_phase1($ikeid) {
359
	global $config;
360
	init_config_arr(array('ipsec', 'phase1'));
361

    
362
	foreach ($config['ipsec']['phase1'] as $p1) {
363
		if ($p1['ikeid'] == $ikeid) {
364
			return $p1;
365
		}
366
	}
367
}
368

    
369
function ipsec_get_p1_descr($ikeid) {
370
	global $config;
371
	init_config_arr(array('ipsec', 'phase1'));
372
	$p1 = ipsec_get_phase1($ikeid);
373
	return (!empty($p1['descr'])) ? $p1['descr'] : '';
374
}
375

    
376
/*
377
 * Return phase1 by reference for a given phase2
378
 */
379
function ipsec_lookup_phase1($p2, &$p1ref) {
380
	global $config;
381
	init_config_arr(array('ipsec', 'phase1'));
382

    
383
	foreach ($config['ipsec']['phase1'] as & $p1) {
384
		if ($p1['ikeid'] == $p2['ikeid']) {
385
			$p1ref = $p1;
386
			return true;
387
		}
388
	}
389

    
390
	return false;
391
}
392

    
393
function ipsec_get_phase2($reqid) {
394
	global $config;
395
	init_config_arr(array('ipsec', 'phase2'));
396

    
397
	foreach ($config['ipsec']['phase2'] as $p2) {
398
		if ($p2['reqid'] == $reqid) {
399
			return $p2;
400
		}
401
	}
402
}
403

    
404
function ipsec_get_phase2_by_ikeid($ikeid) {
405
	global $config;
406
	init_config_arr(array('ipsec', 'phase2'));
407
	$results = array();
408
	foreach ($config['ipsec']['phase2'] as $p2) {
409
		if ($p2['ikeid'] == $ikeid) {
410
			$results[] = $p2;
411
		}
412
	}
413
	return $results;
414
}
415

    
416
function ipsec_get_number_of_phase2($ikeid) {
417
	global $config;
418
	return count(ipsec_get_phase2_by_ikeid($ikeid));
419
}
420

    
421
/* Return a nested hash with P1 indexed by ikeid and P2 indexed by reqid */
422
function ipsec_map_config_by_id() {
423
	global $config;
424
	init_config_arr(array('ipsec', 'phase1'));
425
	init_config_arr(array('ipsec', 'phase2'));
426
	$map = array();
427

    
428
	foreach ($config['ipsec']['phase1'] as $p1) {
429
		$map[$p1['ikeid']] = array();
430
		$map[$p1['ikeid']]['p1'] = $p1;
431
	}
432
	foreach ($config['ipsec']['phase2'] as $p2) {
433
		if (!is_array($map[$p2['ikeid']]['p2'])) {
434
			$map[$p2['ikeid']]['p2'] = array();
435
		}
436
		$map[$p2['ikeid']]['p2'][$p2['reqid']] = $p2;
437
	}
438

    
439
	return $map;
440
}
441

    
442
function ipsec_conid($p1 = null, $p2 = null) {
443
	global $ipsec_conid_prefix, $ipsec_conid_separator;
444

    
445
	$conid = NULL;
446
	$have_p1 = (!empty($p1) && is_array($p1));
447
	$have_p2 = (!empty($p2) && is_array($p2));
448

    
449
	if (!$have_p1 && $have_p2) {
450
		$p1 = ipsec_get_phase1($p2['ikeid']);
451
	}
452

    
453
	if (isset($p1['mobile'])) {
454
		return "{$ipsec_conid_prefix}-mobile";
455
	}
456

    
457
	$conid = "{$ipsec_conid_prefix}{$p1['ikeid']}";
458

    
459
	if ($have_p2) {
460
		/* IKEv1 or IKEv2+Split Connections use separate P2 entries
461
		 * so add reqid to ID */
462
		if (($p1['iketype'] != 'ikev2') || isset($p1['splitconn'])) {
463
			$conid .= "{$ipsec_conid_separator}{$p2['reqid']}";
464
		}
465
	}
466

    
467
	return $conid;
468
}
469

    
470
function ipsec_id_by_conid($conid) {
471
	global $config, $ipsec_conid_prefix, $ipsec_conid_separator;
472

    
473
	if ($conid == "{$ipsec_conid_prefix}-mobile") {
474
		init_config_arr(array('ipsec', 'phase1'));
475
		foreach ($config['ipsec']['phase1'] as $p1) {
476
			if (isset($p1['mobile'])) {
477
				$conid = "{$ipsec_conid_prefix}{$p1['ikeid']}";
478
			}
479
		}
480
	}
481

    
482
	if (substr($conid, 0, strlen($ipsec_conid_prefix)) == $ipsec_conid_prefix) {
483
		$conid = substr($conid, strlen($ipsec_conid_prefix));
484
	}
485
	$parts = explode($ipsec_conid_separator, $conid);
486
	if (count($parts) == 1) {
487
		$parts[1] = NULL;
488
	}
489
	return array($parts[0], $parts[1]);
490
}
491

    
492
/*
493
 * Return phase1 local address
494
 */
495
function ipsec_get_phase1_src(& $ph1ent, $gateways_status = false) {
496
	global $interface_ip_arr_cache;
497
	global $interface_sn_arr_cache;
498
	if ($ph1ent['interface']) {
499
		/* get_interface_ip() already calls get_failover_interface so no need for special handling */
500
		$if = $ph1ent['interface'];
501
	} else {
502
		$if = "wan";
503
	}
504

    
505
	/* Fetch gateway status if not passed */
506
	if (!is_array($gateways_status)) {
507
		$gateways_status = return_gateways_status(true);
508
	}
509

    
510
	$ifips = array();
511
	if (in_array($ph1ent['protocol'], array('inet', 'both'))) {
512
		$ip4 = get_interface_ip($if, $gateways_status);
513
		if (!empty($ip4)) {
514
			$ifips[] = $ip4;
515
		}
516
	}
517

    
518
	if (in_array($ph1ent['protocol'], array('inet6', 'both'))) {
519
		/* get correct interface name for 6RD/6to4 interfaces
520
		 * see https://redmine.pfsense.org/issues/11643 */
521
		if (is_stf_interface($ph1ent['interface'])) {
522
			$ip6 = get_interface_ipv6($ph1ent['interface'], false, false, $gateways_status);
523
		} else {
524
			$ip6 = get_interface_ipv6($if, false, false, $gateways_status);
525
		}
526
		if (!empty($ip6)) {
527
			$ifips[] = $ip6;
528
		}
529
	}
530

    
531
	return implode(',', $ifips);
532
}
533

    
534
/*
535
 * Return phase1 local address
536
 */
537
function ipsec_get_phase1_dst(& $ph1ent) {
538
	global $g;
539

    
540
	if (empty($ph1ent['remote-gateway'])) {
541
		return false;
542
	}
543
	$rg = $ph1ent['remote-gateway'];
544
	if (!is_ipaddr($rg)) {
545
		if (!platform_booting()) {
546
			return resolve_retry($rg, $ph1ent['protocol']);
547
		}
548
	}
549
	if (!is_ipaddr($rg)) {
550
		return false;
551
	}
552

    
553
	return $rg;
554
}
555

    
556
/*
557
 * Return phase2 idinfo in cidr format
558
 */
559
function ipsec_idinfo_to_cidr(& $idinfo, $addrbits = false, $mode = "") {
560
	global $config;
561

    
562
	switch ($idinfo['type']) {
563
		case "address":
564
			if ($addrbits) {
565
				if ($mode == "tunnel6") {
566
					return $idinfo['address']."/128";
567
				} elseif (($mode == "vti") && is_ipaddrv4($idinfo['address'])) {
568
					return $idinfo['address']."/30";
569
				} elseif (($mode == "vti") && is_ipaddrv6($idinfo['address'])) {
570
					return $idinfo['address']."/64";
571
				} else {
572
					return $idinfo['address']."/32";
573
				}
574
			} else {
575
				return $idinfo['address'];
576
			}
577
			break; /* NOTREACHED */
578
		case "network":
579
			return "{$idinfo['address']}/{$idinfo['netbits']}";
580
			break; /* NOTREACHED */
581
		case "none":
582
		case "mobile":
583
			return '0.0.0.0/0';
584
			break; /* NOTREACHED */
585
		default:
586
			if (empty($mode) && !empty($idinfo['mode'])) {
587
				$mode = $idinfo['mode'];
588
			}
589

    
590
			if ($mode == "tunnel6") {
591
				$address = get_interface_ipv6($idinfo['type']);
592
				$netbits = get_interface_subnetv6($idinfo['type']);
593
				$address = gen_subnetv6($address, $netbits);
594
				return "{$address}/{$netbits}";
595
			} else {
596
				$address = get_interface_ip($idinfo['type']);
597
				$netbits = get_interface_subnet($idinfo['type']);
598
				$address = gen_subnet($address, $netbits);
599
				return "{$address}/{$netbits}";
600
			}
601
			break; /* NOTREACHED */
602
	}
603
}
604

    
605
/*
606
 * Return phase2 idinfo in address/netmask format
607
 */
608
function ipsec_idinfo_to_subnet(& $idinfo, $addrbits = false) {
609
	global $config;
610

    
611
	switch ($idinfo['type']) {
612
		case "address":
613
			if ($addrbits) {
614
				if ($idinfo['mode'] == "tunnel6") {
615
					return $idinfo['address']."/128";
616
				} else {
617
					return $idinfo['address']."/255.255.255.255";
618
				}
619
			} else {
620
				return $idinfo['address'];
621
			}
622
			break; /* NOTREACHED */
623
		case "none":
624
		case "network":
625
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
626
			break; /* NOTREACHED */
627
		case "mobile":
628
			return "0.0.0.0/0";
629
			break; /* NOTREACHED */
630
		default:
631
			if ($idinfo['mode'] == "tunnel6") {
632
				$address = get_interface_ipv6($idinfo['type']);
633
				$netbits = get_interface_subnetv6($idinfo['type']);
634
				$address = gen_subnetv6($address, $netbits);
635
				return $address."/".$netbits;
636
			} else {
637
				$address = get_interface_ip($idinfo['type']);
638
				$netbits = get_interface_subnet($idinfo['type']);
639
				$address = gen_subnet($address, $netbits);
640
				return $address."/".$netbits;
641
			}
642
			break; /* NOTREACHED */
643
	}
644
}
645

    
646
/*
647
 *  Return phase2 idinfo in text format
648
 */
649
function ipsec_idinfo_to_text(& $idinfo) {
650
	global $config;
651

    
652
	switch ($idinfo['type']) {
653
		case "address":
654
			return $idinfo['address'];
655
			break; /* NOTREACHED */
656
		case "network":
657
			return $idinfo['address']."/".$idinfo['netbits'];
658
			break; /* NOTREACHED */
659
		case "mobile":
660
			return gettext("Mobile Client");
661
			break; /* NOTREACHED */
662
		case "none":
663
			return gettext("None");
664
			break; /* NOTREACHED */
665
		default:
666
			if (!empty($config['interfaces'][$idinfo['type']])) {
667
				return convert_friendly_interface_to_friendly_descr($idinfo['type']);
668
			} else {
669
				return strtoupper($idinfo['type']);
670
			}
671
			break; /* NOTREACHED */
672
	}
673
}
674

    
675
/*
676
 * Wrapper to call pfSense_ipsec_list_sa() when IPsec is enabled
677
 */
678
function ipsec_list_sa() {
679
	if (ipsec_enabled()) {
680
		return pfSense_ipsec_list_sa();
681
	}
682
	return array();
683
}
684

    
685
function ipsec_status() {
686
	$cmap = ipsec_map_config_by_id();
687
	$status = ipsec_list_sa();
688

    
689
	/*
690
	Start:
691
	$cmap[$ikeid]['p1'] => Phase 1 config
692
	$cmap[$ikeid]['p2'] => Array of P2 configs indexed by reqid
693

    
694
	End:
695
	$cmap[$ikeid]['p1']['status'] => Array of IKE SA entries and child SAs for this P1
696
	$cmap[$ikeid]['p1']['connected'] => true/false if the P1 is connected
697
	$cmap[$ikeid]['p2'][$reqid]['connected'] => true/false if the P2 is connected
698
		If non-split IKEv2 then mark all as 'true'
699
	*/
700

    
701
	if (!is_array($status)) {
702
		$status = array();
703
	}
704

    
705
	$cmap['connected'] = array(
706
		/* List of connected P1 conid entries (excluding mobile) */
707
		'p1' => array(),
708
		/* List of connected P2 conid entries (excluding mobile) */
709
		'p2' => array(),
710
	);
711
	$cmap['disconnected'] = array(
712
		/* List of disconnected P1 conid entries (excluding mobile) */
713
		'p1' => array(),
714
		/* List of disconnected P2 conid entries (excluding mobile) */
715
		'p2' => array(),
716
	);
717

    
718
	/* First level of array has IKE SA content */
719
	foreach ($status as $ikesa) {
720
		/* If the con-id isn't there it must not be a proper entry */
721
		if (!isset($ikesa['con-id'])) {
722
			continue;
723
		}
724

    
725
		/* Mark this IKE ID as connected and drop the status info underneath the P1 config */
726
		list($ikeid, $reqid) = ipsec_id_by_conid($ikesa['con-id']);
727
		if (!array_key_exists($ikeid, $cmap)) {
728
			// Doesn't match known tunnel
729
			$cmap['connected']['p1'][$ikesa['con-id']] = $ikesa['con-id'];
730
		} else {
731
			/* Track this IKE ID in the connection list */
732
			$cmap['connected']['p1'][$ikeid] = $ikesa['con-id'];
733
			/* Copy the status data. Use an array since it could be connected multiple times. */
734
			if (!is_array($cmap[$ikeid]['status'])) {
735
				$cmap[$ikeid]['status'] = array();
736
			}
737
			$cmap[$ikeid]['status'][] = $ikesa;
738
			$cmap[$ikeid]['p1']['connected'] = true;
739
		}
740

    
741
		/* If there are child SAs under this IKE entry, process them */
742
		if (array_key_exists('child-sas', $ikesa) &&
743
		    is_array($ikesa['child-sas'])) {
744
			foreach ($ikesa['child-sas'] as $childid => $childsa) {
745
				/* Mark this reqid as connected */
746
				list($childikeid, $childreqid) = ipsec_id_by_conid($childsa['name']);
747

    
748
				if ($childreqid != null) {
749
					if (!isset($cmap[$childikeid]['p2'][$childreqid]['mobile'])) {
750
						$cmap['connected']['p2'][$childreqid] = $childsa['name'];
751
						$cmap[$childikeid]['p2'][$childreqid]['connected'] = true;
752
					}
753
				} else {
754
					/* If this is IKEv2 w/o Split, mark all reqids for the P1 as connected */
755
					if ($cmap[$childikeid]['p1']['iketype'] == 'ikev2' &&
756
					    !isset($cmap[$childikeid]['p1']['splitconn'])) {
757
						foreach ($cmap[$ikeid]['p2'] as $p2) {
758
							if (isset($p2['mobile'])) {
759
								continue;
760
							}
761
							$cmap['connected']['p2'][$p2['reqid']] = $childsa['name'];
762
							$cmap[$childikeid]['p2'][$p2['reqid']]['connected'] = true;
763
						}
764
					}
765
				}
766
			}
767
		}
768
	}
769
	if (is_array($cmap)) {
770
		foreach ($cmap as $k => $tunnel) {
771
			if (in_array($k, array('connected', 'disconnected')) ||
772
			    (!array_key_exists('p1', $tunnel) ||
773
			    isset($tunnel['p1']['disabled'])) ||
774
			    isset($tunnel['p1']['mobile'])) {
775
				continue;
776
			}
777
			if (!array_key_exists($tunnel['p1']['ikeid'], $cmap['connected']['p1'])) {
778
				$cmap['disconnected']['p1'][$tunnel['p1']['ikeid']] = ipsec_conid($tunnel['p1']);
779
			}
780
			foreach ($tunnel['p2'] as $p2) {
781
				if (isset($p2['mobile'])) {
782
					continue;
783
				}
784
				if (!array_key_exists($p2['reqid'], $cmap['connected']['p2'])) {
785
					/* This P2 is not connected */
786
					$cmap['disconnected']['p2'][$p2['reqid']] = ipsec_conid($p1['p1'], $p2);
787
				}
788
			}
789
		}
790
	}
791
	return $cmap;
792
}
793

    
794
function ipsec_status_button($req = 'post', $act = 'connect', $type = 'ike', $conid, $uniqueid = null, $full = true) {
795
	$btn = '<a href="';
796
	if ($act == 'disconnect') {
797
		$buttonclass = ($type == 'ike') ? 'danger' : 'warning';
798
		$title = gettext("Disconnect");
799
		$icon = 'trash';
800
	} else {
801
		$buttonclass = 'success';
802
		$title = gettext("Connect");
803
		$icon = 'sign-in';
804
	}
805
	if ($full) {
806
		$linkclass = 'btn btn-xs btn-' . htmlspecialchars($buttonclass);
807
	} else {
808
		$linkclass = '';
809
	}
810

    
811
	$title .= " ";
812
	switch ($type) {
813
		case 'all':
814
			$title .= gettext("P1 and P2s");
815
			break;
816
		case 'child':
817
			$title .= gettext("P2");
818
			break;
819
		case 'ike':
820
		default:
821
			$title .= gettext("P1");
822
			break;
823
	}
824

    
825
	if ($req == 'post') {
826
		$btn .= 'status_ipsec.php?act=' . htmlspecialchars($act) .
827
			'&amp;type=' . htmlspecialchars($type) .
828
			'&amp;conid=' . htmlspecialchars($conid);
829
		if (!empty($uniqueid)) {
830
			$btn .= '&amp;uniqueid=' . htmlspecialchars($uniqueid);
831
		}
832
	} else {
833
		$btn .= '#" id="' . 'ipsecstatus-' .
834
			htmlspecialchars($act) . '-' .
835
			htmlspecialchars($type) . '-' .
836
			htmlspecialchars($conid);
837
		if (!empty($uniqueid)) {
838
			$btn .= '-' . htmlspecialchars($uniqueid);
839
		}
840
	}
841

    
842
	$btn .= '"';
843
	if ($full) {
844
		$btn .= ' class="' . htmlspecialchars($linkclass) . '"';
845
	}
846
	$btn .= ' title="' . htmlspecialchars($title) . '"';
847

    
848
	if ($req == 'post') {
849
		$btn .= ' usepost';
850
	}
851
	$btn .= '>';
852
	$btn .= '<i class="fa fa-' . htmlspecialchars($icon);
853
	if ($req == 'post') {
854
		$btn .= ' icon-embed-btn';
855
	}
856
	$btn .= '"></i>';
857

    
858
	if ($full) {
859
		$btn .= ' ' . htmlspecialchars($title);
860
	}
861
	$btn .= '</a>';
862
	return $btn;
863
}
864

    
865
function ipsec_initiate($type = 'ike', $p1 = null, $p2 = null) {
866
	return ipsec_initiate_by_conid($type, ipsec_conid($p1, $p2));
867
}
868

    
869
function ipsec_initiate_by_conid($type = 'ike', $conid) {
870
	if (empty($conid)) {
871
		return false;
872
	}
873
	switch ($type) {
874
		case 'all':
875
			/* Find relevant P2s and initaite all of them */
876
			$cmap = ipsec_map_config_by_id();
877
			list($ikeid, $reqid) = ipsec_id_by_conid($conid);
878
			if (array_key_exists($ikeid, $cmap)) {
879
				if ($cmap[$ikeid]['p1']['iketype'] == 'ikev2' && !isset($cmap[$ikeid]['p1']['splitconn'])) {
880
					/* IKEv2 without split only has one P2, named the same as the the P1 */
881
					ipsec_initiate_by_conid('child', ipsec_conid($cmap[$ikeid]['p1']));
882
				} else {
883
					foreach ($cmap[$ikeid]['p2'] as $p2) {
884
						ipsec_initiate_by_conid('child', ipsec_conid($cmap[$ikeid]['p1'], $p2));
885
					}
886
				}
887
			}
888
			break;
889
		case 'ike':
890
		case 'child':
891
			mwexec_bg("/usr/local/sbin/swanctl --initiate " . escapeshellarg('--' . $type) . " " . escapeshellarg($conid));
892
			break;
893
		default:
894
			/* Do nothing. Invalid type. */
895
			break;
896
	}
897
	return true;
898
}
899

    
900
function ipsec_terminate($type = 'ike', $p1 = null, $p2 = null, $uniqueid = null) {
901
	return ipsec_terminate_by_conid($type, ipsec_conid($p1, $p2), $uniqueid);
902
}
903

    
904
function ipsec_terminate_by_conid($type = 'ike', $conid = null, $uniqueid = null) {
905
	/* Must have one or the other */
906
	if (($conid === null) && ($uniqueid === null)) {
907
		return false;
908
	}
909
	$selector = '--';
910
	$selector .= ($type == 'ike') ? 'ike' : 'child';
911

    
912
	if (ctype_digit($uniqueid) && ($uniqueid > 0)) {
913
		$selector .= '-id';
914
		$term = $uniqueid;
915
	} else {
916
		$term = $conid;
917
	}
918
	mwexec_bg("/usr/local/sbin/swanctl --terminate " . escapeshellarg($selector) . " " . escapeshellarg($term));
919
	return true;
920
}
921

    
922
/*
923
 * Return dump of SPD table
924
 */
925
function ipsec_dump_spd() {
926
	$fd = @popen("/sbin/setkey -DP", "r");
927
	$spd = array();
928
	if ($fd) {
929
		while (!feof($fd)) {
930
			$line = chop(fgets($fd));
931
			if (!$line) {
932
				continue;
933
			}
934
			if ($line == "No SPD entries.") {
935
				break;
936
			}
937
			if ($line[0] != "\t") {
938
				if (is_array($cursp)) {
939
					$spd[] = $cursp;
940
				}
941
				$cursp = array();
942
				$linea = explode(" ", $line);
943
				$cursp['srcid'] = substr($linea[0], 0, strpos($linea[0], "["));
944
				$cursp['dstid'] = substr($linea[1], 0, strpos($linea[1], "["));
945
				$i = 0;
946
			} else if (is_array($cursp)) {
947
				$line = trim($line, "\t\r\n ");
948
				$linea = explode(" ", $line);
949
				switch ($i) {
950
					case 1:
951
						if ($linea[1] == "none")	/* don't show default anti-lockout rule */ {
952
							unset($cursp);
953
						} else {
954
							$cursp['dir'] = $linea[0];
955
						}
956
						break;
957
					case 2:
958
						$upperspec = explode("/", $linea[0]);
959
						$cursp['proto'] = $upperspec[0];
960
						list($cursp['src'], $cursp['dst']) = explode("-", $upperspec[2]);
961
						$cursp['reqid'] = substr($upperspec[3], strpos($upperspec[3], "#")+1);
962
						break;
963
				}
964
			}
965
			$i++;
966
		}
967
		if (is_array($cursp) && count($cursp)) {
968
			$spd[] = $cursp;
969
		}
970
		pclose($fd);
971
	}
972

    
973
	return $spd;
974
}
975

    
976
/*
977
 * Return dump of SAD table
978
 */
979
function ipsec_dump_sad() {
980
	$fd = @popen("/sbin/setkey -D", "r");
981
	$sad = array();
982
	if ($fd) {
983
		while (!feof($fd)) {
984
			$line = chop(fgets($fd));
985
			if (!$line || $line[0] == " ") {
986
				continue;
987
			}
988
			if ($line == "No SAD entries.") {
989
				break;
990
			}
991
			if ($line[0] != "\t") {
992
				if (is_array($cursa)) {
993
					$sad[] = $cursa;
994
				}
995
				$cursa = array();
996
				list($cursa['src'], $cursa['dst']) = explode(" ", $line);
997
			} else {
998
				$line = trim($line, "\t\n\r ");
999
				$linea = explode(" ", $line);
1000
				foreach ($linea as $idx => $linee) {
1001
					if ($linee == 'esp' || $linee == 'ah' || $linee[0] == '#') {
1002
						$cursa['proto'] = $linee;
1003
					} else if (substr($linee, 0, 3) == 'spi') {
1004
						$cursa['spi'] = substr($linee, strpos($linee, 'x') + 1, -1);
1005
					} else if (substr($linee, 0, 5) == 'reqid') {
1006
						$cursa['reqid'] = substr($linee, strpos($linee, 'x') + 1, -1);
1007
					} else if (substr($linee, 0, 2) == 'E:') {
1008
						$cursa['ealgo'] = $linea[$idx + 1];
1009
						break;
1010
					} else if (substr($linee, 0, 2) == 'A:') {
1011
						$cursa['aalgo'] = $linea[$idx + 1];
1012
						break;
1013
					} else if (substr($linee, 0, 8) == 'current:') {
1014
						$cursa['data'] = substr($linea[$idx + 1], 0, strpos($linea[$idx + 1], 'bytes') - 1) . ' B';
1015
						break;
1016
					}
1017
				}
1018
			}
1019
		}
1020
		if (is_array($cursa) && count($cursa)) {
1021
			$sad[] = $cursa;
1022
		}
1023
		pclose($fd);
1024
	}
1025

    
1026
	return $sad;
1027
}
1028

    
1029
/*
1030
 * Return dump of mobile user list
1031
 */
1032
function ipsec_dump_mobile() {
1033
	global $g, $config;
1034

    
1035
	if(!isset($config['ipsec']['client']['enable'])) {
1036
		return array();
1037
	}
1038

    
1039
	/* Fetch the pool contents and leases from swanctl. */
1040
	$_gb = exec("/usr/local/sbin/swanctl --list-pools --leases 2>/dev/null", $output, $rc);
1041

    
1042
	if ($rc != 0) {
1043
		log_error(gettext("Unable to find IPsec daemon leases file. Could not display mobile user stats!"));
1044
		return array();
1045
	}
1046

    
1047
	$response = array(
1048
		'pool' => array(),
1049
	);
1050

    
1051
	/* swanctl --list-pools --leases example output, field names can be confirmed by
1052
	 * looking at --raw or --pretty output. */
1053
	/* mobile-pool          10.7.200.0                          0 / 1 / 254 */
1054
	$pool_regex='/^(?P<name>\S+)\s+(?P<base>\S+)\s+(?P<online>\d+) \/ (?P<offline>\d+) \/ (?P<size>\d+)/';
1055
	/*   10.7.200.1                     online   'jimp' */
1056
	$lease_regex='/\s*(?P<host>[\d\.]+)\s+(?P<status>online|offline)\s+\'(?P<id>.*)\'/';
1057
	foreach ($output as $line) {
1058
		if (preg_match($pool_regex, $line, $matches)) {
1059
			$id++;
1060
			$response['pool'][$id] = array(
1061
			    'name'   => $matches['name'],
1062
			    'base'   => $matches['base'],
1063
			    'online' => $matches['online'],
1064
			    'offline'  => $matches['offline'],
1065
			    'size'   => $matches['size'],
1066
			);
1067
		} elseif (preg_match($lease_regex, $line, $matches)) {
1068
			$response['pool'][$id]['lease'][] = array(
1069
			    'host'   => $matches['host'],
1070
			    'status' => $matches['status'],
1071
			    'id'     => $matches['id']
1072
			);
1073
		}
1074
	}
1075

    
1076
	unset($_gb, $output, $rc, $pool_regex);
1077

    
1078
	return $response;
1079
}
1080

    
1081
function ipsec_mobilekey_sort() {
1082
	global $config;
1083

    
1084
	function mobilekeycmp($a, $b) {
1085
		return strcmp($a['ident'][0], $b['ident'][0]);
1086
	}
1087

    
1088
	usort($config['ipsec']['mobilekey'], "mobilekeycmp");
1089
}
1090

    
1091
function ipsec_fixup_ip($ipaddr) {
1092
	if (is_ipaddrv6($ipaddr) || is_subnetv6($ipaddr)) {
1093
		return text_to_compressed_ip6($ipaddr);
1094
	} else {
1095
		return $ipaddr;
1096
	}
1097
}
1098

    
1099
function ipsec_find_id(& $ph1ent, $side = "local", $rgmap = array(), $gateways_status = false) {
1100
	if ($side == "local") {
1101
		$id_type = $ph1ent['myid_type'];
1102
		$id_data = $ph1ent['myid_data'];
1103

    
1104
		$addr = ipsec_get_phase1_src($ph1ent, $gateways_status);
1105
		if (!$addr) {
1106
			return array();
1107
		}
1108
		/* When automatically guessing, use the first address. */
1109
		$addr = explode(',', $addr);
1110
		$addr = $addr[0];
1111
	} elseif ($side == "peer") {
1112
		$id_type = $ph1ent['peerid_type'];
1113
		$id_data = $ph1ent['peerid_data'];
1114

    
1115
		if (isset($ph1ent['mobile'])) {
1116
			$addr = "%any";
1117
		} else {
1118
			$addr = $ph1ent['remote-gateway'];
1119
		}
1120
	} else {
1121
		return array();
1122
	}
1123

    
1124
	$thisid_type = $id_type;
1125
	switch ($thisid_type) {
1126
		case 'myaddress':
1127
			$thisid_type = 'address';
1128
			$thisid_data = $addr;
1129
			break;
1130
		case 'dyn_dns':
1131
			$thisid_type = 'dns';
1132
			$thisid_data = $id_data;
1133
			break;
1134
		case 'peeraddress':
1135
			$thisid_type = 'address';
1136
			$thisid_data = $rgmap[$ph1ent['remote-gateway']];
1137
			break;
1138
		case 'keyid tag':
1139
			$thisid_type = 'keyid';
1140
			$thisid_data = $id_data;
1141
			break;
1142
		case 'user_fqdn':
1143
			$thisid_type = 'userfqdn';
1144
			$thisid_data = $id_data;
1145
			break;
1146
		case 'any':
1147
			$thisid_data = '%any';
1148
			break;
1149
		case 'address':
1150
		case 'fqdn':
1151
		case 'asn1dn':
1152
		case 'auto':
1153
			$thisid_data = $id_data;
1154
			break;
1155
		default:
1156
			break;
1157
	}
1158
	return array($thisid_type, $thisid_data);
1159
}
1160

    
1161
/*
1162
 * Fixup ID type/data to include prefix when necessary, add quotes, etc.
1163
 */
1164
function ipsec_fixup_id($type, $data) {
1165
	/* List of types to pass through as-is without changes or adding prefix */
1166
	$auto_types = array('address', 'auto');
1167
	/* List of types which need the resulting string double quoted. */
1168
	$quote_types = array('keyid', 'asn1dn', 'auto');
1169

    
1170
	/* If the first character of asn1dn type data is not #, then rely on
1171
	 * automatic parsing https://redmine.pfsense.org/issues/4792 */
1172
	if (($type == 'asn1dn') && !empty($data) && ($data[0] != '#')) {
1173
		$auto_types[] = 'asn1dn';
1174
	}
1175

    
1176
	if ($type == 'any') {
1177
		$idstring = "%any";
1178
	} elseif (!in_array($type, $auto_types)) {
1179
		$idstring = "{$type}:{$data}";
1180
	} else {
1181
		$idstring = $data;
1182
	}
1183

    
1184
	if (in_array($type, $quote_types)) {
1185
		$idstring = "\"{$idstring}\"";
1186
	}
1187

    
1188
	return $idstring;
1189
}
1190

    
1191
function ipsec_fixup_network($network) {
1192
	if (substr($network, -3) == '|/0') {
1193
		$result = substr($network, 0, -3);
1194
	} else {
1195
		$tmp = explode('|', $network);
1196
		if (isset($tmp[1])) {
1197
			$result = $tmp[1];
1198
		} else {
1199
			$result = $tmp[0];
1200
		}
1201
		unset($tmp);
1202
	}
1203

    
1204
	return $result;
1205
}
1206

    
1207
function ipsec_get_loglevels() {
1208
	global $config, $ipsec_log_cats;
1209
	$def_loglevel = '1';
1210

    
1211
	$levels = array();
1212

    
1213
	foreach (array_keys($ipsec_log_cats) as $cat) {
1214
		if (isset($config['ipsec']['logging'][$cat])) {
1215
			$levels[$cat] = $config['ipsec']['logging'][$cat];
1216
		} elseif (in_array($cat, array('ike', 'chd', 'cfg'))) {
1217
			$levels[$cat] = "2";
1218
		} else {
1219
			$levels[$cat] = $def_loglevel;
1220
		}
1221
	}
1222
	return $levels;
1223
}
1224

    
1225
function ipsec_vti($p1, $returnaddresses = false, $skipdisabled = true) {
1226
	global $config;
1227
	$is_vti = false;
1228
	$vtisubnet_spec = array();
1229

    
1230
	foreach(ipsec_get_phase2_by_ikeid($p1['ikeid']) as $p2) {
1231
		if ($p2['mode'] == 'vti') {
1232
			if ($returnaddresses) {
1233
				$vtisubnet_spec[] = array(
1234
					'left' => ipsec_idinfo_to_cidr($p2['localid'], true, $p2['mode']),
1235
					'right' => ipsec_idinfo_to_cidr($p2['remoteid'], false, $p2['mode']),
1236
					'descr' => $p2['descr'],
1237
					'reqid' => $p2['reqid'],
1238
				);
1239
			}
1240
			if ($skipdisabled && isset($p2['disabled'])) {
1241
				continue;
1242
			} else {
1243
				$is_vti = true;
1244
			}
1245
		}
1246
	}
1247
	return ($returnaddresses) ? $vtisubnet_spec : $is_vti;
1248
}
1249

    
1250
/* Called when IPsec is reloaded through rc.newipsecdns and similar, gives
1251
 * packages an opportunity to execute custom code when IPsec reloads.
1252
 */
1253
function ipsec_reload_package_hook() {
1254
	global $g, $config;
1255

    
1256
	init_config_arr(array('installedpackages', 'package'));
1257
	foreach ($config['installedpackages']['package'] as $package) {
1258
		if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) {
1259
			continue;
1260
		}
1261

    
1262
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $package['configurationfile'], 'packagegui');
1263
		if (!empty($pkg_config['include_file']) &&
1264
		    file_exists($pkg_config['include_file'])) {
1265
			require_once($pkg_config['include_file']);
1266
		}
1267
		if (empty($pkg_config['ipsec_reload_function'])) {
1268
			continue;
1269
		}
1270
		$pkg_ipsec_reload = $pkg_config['ipsec_reload_function'];
1271
		if (!function_exists($pkg_ipsec_reload)) {
1272
			continue;
1273
		}
1274
		$pkg_ipsec_reload();
1275
	}
1276
}
1277

    
1278
/****f* certs/ipsec_convert_to_modp
1279
 * NAME
1280
 *   ipsec_convert_to_modp - Take a DH Group number value and return the
1281
 *                           associated name
1282
 * INPUTS
1283
 *   $index: DH group index number to look up
1284
 * RESULT
1285
 *   Returns the Diffie Hellman Group keyword associated with the group number.
1286
 ******/
1287
function ipsec_convert_to_modp($index) {
1288
	$modpmap = array(
1289
		'1' => 'modp768',
1290
		'2' => 'modp1024',
1291
		'5' => 'modp1536',
1292
		'14' => 'modp2048',
1293
		'15' => 'modp3072',
1294
		'16' => 'modp4096',
1295
		'17' => 'modp6144',
1296
		'18' => 'modp8192',
1297
		'19' => 'ecp256',
1298
		'20' => 'ecp384',
1299
		'21' => 'ecp521',
1300
		'22' => 'modp1024s160',
1301
		'23' => 'modp2048s224',
1302
		'24' => 'modp2048s256',
1303
		'25' => 'ecp192',
1304
		'26' => 'ecp224',
1305
		'27' => 'ecp224bp',
1306
		'28' => 'ecp256bp',
1307
		'29' => 'ecp384bp',
1308
		'30' => 'ecp512bp',
1309
		'31' => 'curve25519',
1310
		'32' => 'curve448',
1311
	);
1312

    
1313
	return $modpmap[$index];
1314
}
1315

    
1316
/*
1317
 * Forcefully restart IPsec
1318
 * This is required for when dynamic interfaces reload
1319
 * For all other occasions the normal ipsec_configure()
1320
 * will gracefully reload the settings without restarting
1321
 */
1322
function ipsec_force_reload($interface = "") {
1323
	global $g, $config;
1324

    
1325
	if (!ipsec_enabled()) {
1326
		return;
1327
	}
1328

    
1329
	$ipseccfg = $config['ipsec'];
1330

    
1331
	if (!empty($interface) && is_array($ipseccfg['phase1'])) {
1332
		$found = false;
1333
		foreach ($ipseccfg['phase1'] as $ipsec) {
1334
			if (!isset($ipsec['disabled']) && ($ipsec['interface'] == $interface)) {
1335
				$found = true;
1336
				break;
1337
			}
1338
		}
1339
		if (!$found) {
1340
			log_error(sprintf(gettext("Ignoring IPsec reload since there are no tunnels on interface %s"), $interface));
1341
			return;
1342
		}
1343
	}
1344

    
1345
	/* If we get this far then we need to take action. */
1346
	log_error(gettext("Forcefully reloading IPsec"));
1347
	ipsec_configure();
1348
}
1349

    
1350
/****f* ipsec/ipsec_strongswan_confgen
1351
 * NAME
1352
 *   ipsec_strongswan_confgen - Generate strongswan.conf style formatted output
1353
 *                              From a multi-dimensional array.
1354
 * INPUTS
1355
 *   $confarr: An array of key=value pairs or key=array entries for subsections.
1356
 *   $indent : A string of tabs used when indenting lines.
1357
 * RESULT
1358
 *   Returns a string of key=value pairs with proper indenting, and with named
1359
 *   subsections enclosed in braces.
1360
 * NOTES
1361
 *   Values starting with a "#" (comments) or "include " will be printed
1362
 *   directly without their associated key name.
1363
 ******/
1364
function ipsec_strongswan_confgen($confarr, $indent = "") {
1365
	$confstr = "";
1366
	/* Only process the contents if it's an array. */
1367
	if (is_array($confarr)) {
1368
		foreach ($confarr as $key => $value) {
1369
			if (is_array($value)) {
1370
				/* If the array is empty, do not print anything */
1371
				if (empty($value)) {
1372
					continue;
1373
				}
1374
				/* If this is an array, setup the subsection name
1375
				 * and structure, then call this function
1376
				 * recursively to walk the lower array. */
1377
				$confstr .= "{$indent}{$key} {\n";
1378
				$confstr .= ipsec_strongswan_confgen($value, $indent . "\t");
1379
				$confstr .= "{$indent}}\n";
1380
			} else {
1381
				$confstr .= "{$indent}";
1382
				if ((substr($value, 0 , 1) != '#') &&
1383
				    (substr($value, 0 , 8) != 'include ')) {
1384
					/* Not a comment or include, print the key */
1385
					$confstr .= "{$key} = ";
1386
				}
1387
				$confstr .= "{$value}\n";
1388
			}
1389
		}
1390
	}
1391
	return $confstr;
1392
}
1393

    
1394
global $g, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs;
1395
$ipsec_swanctl_basedir = "{$g['varetc_path']}/ipsec";
1396
$ipsec_swanctl_dirs = array(
1397
	'conf'     => "{$ipsec_swanctl_basedir}/conf.d",
1398
	'certpath' => "{$ipsec_swanctl_basedir}/x509",
1399
	'capath'   => "{$ipsec_swanctl_basedir}/x509ca",
1400
	'aapath'   => "{$ipsec_swanctl_basedir}/x509aa",
1401
	'ocsppath' => "{$ipsec_swanctl_basedir}/x509ocsp",
1402
	'crlpath'  => "{$ipsec_swanctl_basedir}/x509crl",
1403
	'acpath'   => "{$ipsec_swanctl_basedir}/x509ac",
1404
	'rsakeys'  => "{$ipsec_swanctl_basedir}/rsa",
1405
	'eckeys'   => "{$ipsec_swanctl_basedir}/ecdsa",
1406
	'keypath'  => "{$ipsec_swanctl_basedir}/private",
1407
	'pubkpath' => "{$ipsec_swanctl_basedir}/pubkey",
1408
	'blisspath' => "{$ipsec_swanctl_basedir}/bliss",
1409
	'pkcs8path' => "{$ipsec_swanctl_basedir}/pkcs8",
1410
	'pkcs12path' => "{$ipsec_swanctl_basedir}/pkcs12",
1411
);
1412

    
1413
/****f* ipsec/ipsec_create_dirs
1414
 * NAME
1415
 *   ipsec_create_dirs - Create default set of strongSwan configuration directories.
1416
 * INPUTS
1417
 *   None
1418
 * RESULT
1419
 *   Cleans up and creates strongSwan configuration directories and links.
1420
 ******/
1421
function ipsec_create_dirs() {
1422
	global $config, $g, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs;
1423

    
1424
	/* Cleanup base paths to ensure old files are not left behind (#5238) */
1425
	if (!empty($ipsec_swanctl_basedir)) {
1426
		@rmdir_recursive($ipsec_swanctl_basedir, false);
1427
	}
1428
	/* Create directory structure */
1429
	array_map('safe_mkdir', array_values($ipsec_swanctl_dirs));
1430

    
1431
	/* Create and link strongSwan config */
1432
	$ssdpath = "{$g['varetc_path']}/ipsec/strongswan.d";
1433
	if (!file_exists($ssdpath) || !is_link($ssdpath)) {
1434
		if (is_dir($ssdpath) && !is_link($ssdpath)) {
1435
			@rmdir_recursive($ssdpath, false);
1436
		} else {
1437
			@unlink($ssdpath);
1438
		}
1439
		@symlink("/usr/local/etc/strongswan.d",
1440
		    $ssdpath);
1441
	}
1442
	if (!file_exists("/usr/local/etc/strongswan.conf") ||
1443
	    !is_link("/usr/local/etc/strongswan.conf")) {
1444
		@unlink("/usr/local/etc/strongswan.conf");
1445
		@symlink("{$g['varetc_path']}/ipsec/strongswan.conf",
1446
		    "/usr/local/etc/strongswan.conf");
1447
	}
1448
}
1449

    
1450
/****f* ipsec/ipsec_setup_gwifs
1451
 * NAME
1452
 *   ipsec_setup_gwifs - Setup IPsec-related interfaces and gateways
1453
 * INPUTS
1454
 *   None
1455
 * RESULT
1456
 *   Sets up VTI interfaces and gateways, populates ipsecpinghosts
1457
 ******/
1458
function ipsec_setup_gwifs($gateways_status = false) {
1459
	global $g, $a_phase1, $a_phase2, $rgmap, $filterdns_list,
1460
		$aggressive_mode_psk, $mobile_ipsec_auth, $ifacesuse,
1461
		$ipsecpinghostsactive;
1462
	/* resolve all local, peer addresses and setup pings */
1463
	/* Fetch gateway status if not passed */
1464
	if (!is_array($gateways_status)) {
1465
		$gateways_status = return_gateways_status(true);
1466
	}
1467

    
1468
	unset($iflist);
1469
	if (is_array($a_phase1) && count($a_phase1)) {
1470
		$ipsecpinghosts = array();
1471
		/* step through each phase1 entry */
1472
		foreach ($a_phase1 as $ph1ent) {
1473
			if (isset($ph1ent['disabled'])) {
1474
				continue;
1475
			}
1476
			if (substr($ph1ent['interface'], 0, 4) == "_vip") {
1477
				$vpninterface = get_configured_vip_interface($ph1ent['interface']);
1478
				$ifacesuse[] = get_real_interface($vpninterface);
1479
			} else {
1480
				$vpninterface = get_failover_interface($ph1ent['interface'], 'all', $gateways_status);
1481
				if (substr($vpninterface, 0, 4) == "_vip") {
1482
					$vpninterface = get_configured_vip_interface($vpninterface);
1483
					$ifacesuse[] = get_real_interface($vpninterface);
1484
				} elseif (!empty($vpninterface)) {
1485
					$ifacesuse[] = $vpninterface;
1486
				}
1487
			}
1488
			if ($ph1ent['mode'] == "aggressive" && ($ph1ent['authentication_method'] == "pre_shared_key" || $ph1ent['authentication_method'] == "xauth_psk_server")) {
1489
				$aggressive_mode_psk = true;
1490
			}
1491

    
1492
			$ikeid = $ph1ent['ikeid'];
1493

    
1494
			$local_spec = ipsec_get_phase1_src($ph1ent, $gateways_status);
1495
			/* When automatically guessing, use the first address. */
1496
			$local_spec  = explode(',', $local_spec);
1497
			$local_spec  = $local_spec[0];
1498
			if (!is_ipaddr($local_spec)) {
1499
				log_error(sprintf(gettext("IPsec ERROR: Could not find phase 1 source for connection %s. Omitting from configuration file."), $ph1ent['descr']));
1500
				continue;
1501
			}
1502

    
1503
			if (isset($ph1ent['mobile'])) {
1504
				$mobile_ipsec_auth = $ph1ent['authentication_method'];
1505
				continue;
1506
			}
1507

    
1508
			/* see if this tunnel has a hostname for the remote-gateway. If so,
1509
			   try to resolve it now and add it to the list for filterdns */
1510
			$rg = $ph1ent['remote-gateway'];
1511
			if (!is_ipaddr($rg)) {
1512
				$filterdns_list[] = "{$rg}";
1513
				add_hostname_to_watch($rg);
1514
				if (!platform_booting()) {
1515
					$rg = resolve_retry($rg, $ph1ent['protocol']);
1516
				}
1517
				if (!is_ipaddr($rg)) {
1518
					continue;
1519
				}
1520
			}
1521
			if (!isset($ph1ent['gw_duplicates']) && array_search($rg, $rgmap)) {
1522
				log_error(sprintf(gettext("The remote gateway %s already exists on another phase 1 entry"), $rg));
1523
				continue;
1524
			}
1525
			$rgmap[$ph1ent['remote-gateway']] = $rg;
1526

    
1527
			$is_vti = false;
1528
			if (is_array($a_phase2)) {
1529
				/* step through each phase2 entry */
1530
				foreach ($a_phase2 as $ph2ent) {
1531
					if ($ph2ent['ikeid'] != $ph1ent['ikeid']) {
1532
						continue;
1533
					}
1534
					if ($ph2ent['mode'] == 'vti') {
1535
						$is_vti = true;
1536
					}
1537
					if (isset($ph2ent['disabled']) || ($ikeid != $ph2ent['ikeid'])) {
1538
						continue;
1539
					}
1540

    
1541
					/* add an ipsec pinghosts entry */
1542
					if ($ph2ent['pinghost']) {
1543
						if (!is_array($iflist)) {
1544
							$iflist = get_configured_interface_list();
1545
						}
1546
						$srcip = null;
1547
						$local_subnet = ipsec_idinfo_to_cidr($ph2ent['localid'], true, $ph2ent['mode']);
1548
						if (is_ipaddrv6($ph2ent['pinghost'])) {
1549
							foreach ($iflist as $ifent => $ifname) {
1550
								$interface_ip = get_interface_ipv6($ifent);
1551
								if (!is_ipaddrv6($interface_ip)) {
1552
									continue;
1553
								}
1554
								if (ip_in_subnet($interface_ip, $local_subnet)) {
1555
									$srcip = $interface_ip;
1556
									break;
1557
								}
1558
							}
1559
						} else {
1560
							foreach ($iflist as $ifent => $ifname) {
1561
								$interface_ip = get_interface_ip($ifent, $gateways_status);
1562
								if (!is_ipaddrv4($interface_ip)) {
1563
									continue;
1564
								}
1565
								if ($local_subnet == "0.0.0.0/0" || ip_in_subnet($interface_ip, $local_subnet)) {
1566
									$srcip = $interface_ip;
1567
									break;
1568
								}
1569
							}
1570
						}
1571
						/* if no valid src IP was found in configured interfaces, try the vips */
1572
						if (is_null($srcip)) {
1573
							$viplist = get_configured_vip_list();
1574
							foreach ($viplist as $vip => $address) {
1575
								if (ip_in_subnet($address, $local_subnet)) {
1576
									$srcip = $address;
1577
									break;
1578
								}
1579
							}
1580
						}
1581
						$dstip = $ph2ent['pinghost'];
1582
						$family = "inet" . (is_ipaddrv6($dstip)) ? "6" : "";
1583
						if (is_ipaddr($srcip)) {
1584
							$ipsecpinghosts[] = "{$srcip}|{$dstip}|3|||||{$family}|\n";
1585
							$ipsecpinghostsactive = true;
1586
						}
1587
					}
1588
				}
1589
			}
1590
			if ($is_vti) {
1591
				interface_ipsec_vti_configure($ph1ent, $gateways_status);
1592
			}
1593
		}
1594
		@file_put_contents("{$g['vardb_path']}/ipsecpinghosts", $ipsecpinghosts);
1595
		unset($ipsecpinghosts);
1596
	}
1597
	unset($iflist);
1598
}
1599

    
1600
/****f* ipsec/ipsec_logging_config
1601
 * NAME
1602
 *   ipsec_logging_config - Generate an array containing strongSwan logging
1603
 *                          values.
1604
 * INPUTS
1605
 *   None
1606
 * RESULT
1607
 *   Returns an array which can be merged into the strongswan daemon logging
1608
 *   values so that each logging category is represented, even those not listed
1609
 *   in config.xml.
1610
 ******/
1611
function ipsec_logging_config() {
1612
	global $config, $ipsec_log_cats, $ipsec_log_sevs;
1613
	$logcfg = array();
1614
	$log_levels = ipsec_get_loglevels();
1615
	foreach (array_keys($ipsec_log_cats) as $cat) {
1616
		if (is_numeric($log_levels[$cat]) &&
1617
		    in_array(intval($log_levels[$cat]), array_keys($ipsec_log_sevs), true)) {
1618
			$logcfg[$cat] = $log_levels[$cat];
1619
		}
1620
	}
1621
	return $logcfg;
1622
}
1623

    
1624
/****f* ipsec/ipsec_setup_strongswan
1625
 * NAME
1626
 *   ipsec_setup_strongswan - Generate the strongswan.conf configuration file.
1627
 * INPUTS
1628
 *   None
1629
 * RESULT
1630
 *   Determines strongswan.conf values and crafts the configuration file in the
1631
 *   appropriate format.
1632
 * NOTES
1633
 *   Called from ipsec_configure()
1634
 ******/
1635
function ipsec_setup_strongswan() {
1636
	global $config, $g, $mobile_ipsec_auth, $a_client, $a_phase2, $aggressive_mode_psk, $ifacesuse;
1637

    
1638
	/* strongswan.conf array */
1639
	$ssconf = array();
1640
	$ssconf[] = "# Automatically generated config file - DO NOT MODIFY. Changes will be overwritten.";
1641
	$ssconf['starter'] = array();
1642
	$ssconf['starter']['load_warning'] = "no";
1643

    
1644
	$ssconf['charon'] = array();
1645
	$ssconf['charon'][] = '# number of worker threads in charon';
1646
	$ssconf['charon']['threads'] = '16';
1647
	$ssconf['charon']['ikesa_table_size'] = '32';
1648
	$ssconf['charon']['ikesa_table_segments'] = '4';
1649
	$ssconf['charon']['init_limit_half_open'] = '1000';
1650
	$ssconf['charon']['install_routes'] = 'no';
1651
	$ssconf['charon']['load_modular'] = 'yes';
1652
	$ssconf['charon']['ignore_acquire_ts'] = 'yes';
1653

    
1654
	if ($aggressive_mode_psk) {
1655
		$stronconf = '';
1656
		if (file_exists("{$g['varetc_path']}/ipsec/strongswan.conf")) {
1657
			$stronconf = file_get_contents("{$g['varetc_path']}/ipsec/strongswan.conf");
1658
		}
1659
		log_error("WARNING: Setting i_dont_care_about_security_and_use_aggressive_mode_psk option because a phase 1 is configured using aggressive mode with pre-shared keys. This is not a secure configuration.");
1660
		$restart = (!empty($stronconf) && strpos($stronconf, 'i_dont_care_about_security_and_use_aggressive_mode_psk') === FALSE);
1661
		unset($stronconf);
1662
		$ssconf['charon']['i_dont_care_about_security_and_use_aggressive_mode_psk'] = "yes";
1663
	}
1664

    
1665
	if (isset($config['ipsec']['acceptunencryptedmainmode'])) {
1666
		$ssconf['charon']['accept_unencrypted_mainmode_messages'] = "yes";
1667
	}
1668

    
1669
	if (isset($config['ipsec']['maxexchange'])) {
1670
		$ssconf['charon']['max_ikev1_exchanges'] = $config['ipsec']['maxexchange'];
1671
	}
1672

    
1673
	$unity_enabled = isset($config['ipsec']['unityplugin']) ? 'yes' : 'no';
1674
	$ssconf['charon']['cisco_unity'] = $unity_enabled;
1675

    
1676
	if (isset($config['ipsec']['enableinterfacesuse']) && !empty($ifacesuse)) {
1677
		$ssconf['charon']['interfaces_use'] = implode(',', array_unique($ifacesuse));
1678
	}
1679

    
1680
	if (isset($config['ipsec']['makebeforebreak'])) {
1681
		$ssconf['charon']['make_before_break'] = "yes";
1682
	}
1683

    
1684
	if (isset($config['ipsec']['port'])) {
1685
		$ssconf['charon']['port'] = $config['ipsec']['port'];
1686
	}
1687

    
1688
	if (isset($config['ipsec']['port_nat_t'])) {
1689
		$ssconf['charon']['port_nat_t'] = $config['ipsec']['port_nat_t'];
1690
	}
1691

    
1692
	$ssconf['charon']['syslog'] = array();
1693
	$ssconf['charon']['syslog']['identifier'] = 'charon';
1694
	$ssconf['charon']['syslog'][] = "# log everything under daemon since it ends up in the same place regardless with our syslog.conf";
1695
	$ssconf['charon']['syslog']['daemon'] = array();
1696
	$ssconf['charon']['syslog']['daemon']['ike_name'] = 'yes';
1697
	$ssconf['charon']['syslog']['daemon'] = array_merge($ssconf['charon']['syslog']['daemon'], ipsec_logging_config());
1698
	$ssconf['charon']['syslog'][] = '# disable logging under auth so logs aren\'t duplicated';
1699
	$ssconf['charon']['syslog']['auth'] = array();
1700
	$ssconf['charon']['syslog']['auth']['default'] = -1;
1701

    
1702
	$ssconf['charon']['plugins'] = array();
1703
	$ssconf['charon']['plugins'][] = "# Load defaults";
1704
	$ssconf['charon']['plugins'][] = "include {$g['varetc_path']}/ipsec/strongswan.d/charon/*.conf";
1705
	$ssconf['charon']['plugins']['unity'] = array('load' => $unity_enabled);
1706
	$ssconf['charon']['plugins']['curve25519'] = array('load' => "yes");
1707

    
1708
	if (isset($config['ipsec']['pkcs11support'])) {
1709
		$ssconf['charon']['plugins']['pkcs11'] = array();
1710
		$ssconf['charon']['plugins']['pkcs11']['load'] = "yes";
1711
		$ssconf['charon']['plugins']['pkcs11']['modules'] = array();
1712
		$ssconf['charon']['plugins']['pkcs11']['modules']['opensc'] = array();
1713
		$ssconf['charon']['plugins']['pkcs11']['modules']['opensc']['load_certs'] = "yes";
1714
		$ssconf['charon']['plugins']['pkcs11']['modules']['opensc']['path'] = "/usr/local/lib/opensc-pkcs11.so";
1715
	}
1716

    
1717
	/* Find RADIUS servers designated for Mobile IPsec user auth */
1718
	$radius_servers = array();
1719
	foreach (explode(',', $config['ipsec']['client']['user_source']) as $user_source) {
1720
		$auth_server = auth_get_authserver($user_source);
1721
		$nice_user_source = strtolower(preg_replace('/[\s\.]+/', '_', $user_source));
1722
		if ($auth_server && ($auth_server['type'] === 'radius')) {
1723
			$radius_servers[$nice_user_source] = array();
1724
			$radius_servers[$nice_user_source]['address'] = $auth_server['host'];
1725
			$radius_servers[$nice_user_source]['secret'] = "\"{$auth_server['radius_secret']}\"";
1726
			$radius_servers[$nice_user_source]['auth_port'] = empty($auth_server['radius_auth_port']) ? 1812 : $auth_server['radius_auth_port'];;
1727
			$radius_servers[$nice_user_source]['acct_port'] = empty($auth_server['radius_acct_port']) ? 1813 : $auth_server['radius_acct_port'];
1728
		}
1729
	}
1730

    
1731
	/* Generate an eap-radius config section if appropriate */
1732
	if (count($radius_servers) && ($mobile_ipsec_auth === "eap-radius")) {
1733
		$ssconf['charon']['plugins']['eap-radius'] = array();
1734
		$ssconf['charon']['plugins']['eap-radius']['load'] = "2";
1735
		$ssconf['charon']['plugins']['eap-radius']['class_group'] = "yes";
1736
		$ssconf['charon']['plugins']['eap-radius']['eap_start'] = "no";
1737
		/* Activate RADIUS accounting only if it was selected on the IPsec Mobile Clients tab */
1738
		if ($auth_server && isset($auth_server['radius_acct_port']) &&
1739
		    (is_array($a_client) && ($a_client['radiusaccounting'] == "enabled"))) {
1740
			$ssconf['charon']['plugins']['eap-radius']['accounting'] = "yes";
1741
			$ssconf['charon']['plugins']['eap-radius']['accounting_close_on_timeout'] = "no";
1742
		}
1743
		/* advanced parameters, see https://redmine.pfsense.org/issues/11211 */
1744
		if (isset($a_client['radius_retransmit_base'])) {
1745
			$ssconf['charon']['plugins']['eap-radius']['retransmit_base'] = $a_client['radius_retransmit_base'];
1746
		}
1747
		if (isset($a_client['radius_retransmit_timeout'])) {
1748
			$ssconf['charon']['plugins']['eap-radius']['retransmit_timeout'] = $a_client['radius_retransmit_timeout'];
1749
		}
1750
		if (isset($a_client['radius_retransmit_tries'])) {
1751
			$ssconf['charon']['plugins']['eap-radius']['retransmit_tries'] = $a_client['radius_retransmit_tries'];
1752
		}
1753
		if (isset($a_client['radius_sockets'])) {
1754
			$ssconf['charon']['plugins']['eap-radius']['sockets'] = $a_client['radius_sockets'];
1755
		}
1756
		$ssconf['charon']['plugins']['eap-radius']['servers'] = $radius_servers;
1757
	}
1758

    
1759
	if (is_array($a_client) && isset($a_client['enable'])) {
1760
		if ($a_client['user_source'] != "none") {
1761
			$ssconf['charon']['plugins']['xauth-generic'] = array();
1762
			$ssconf['charon']['plugins']['xauth-generic']['script'] = "/etc/inc/ipsec.auth-user.php";
1763
			$authcfgs = array();
1764
			foreach (explode(",", $a_client['user_source']) as $authcfg) {
1765
				$authcfgs[] = ($authcfg == "system") ? "Local Database" : $authcfg;
1766
			}
1767
			$ssconf['charon']['plugins']['xauth-generic']['authcfg'] = implode(",", $authcfgs);
1768
		}
1769
	}
1770

    
1771
	@file_put_contents("{$g['varetc_path']}/ipsec/strongswan.conf", ipsec_strongswan_confgen($ssconf));
1772
}
1773

    
1774
/****f* ipsec/ipsec_setup_pools
1775
 * NAME
1776
 *   ipsec_setup_pools - Generate primary mobile pool configuration for swanctl
1777
  * INPUTS
1778
 *   None
1779
 * RESULT
1780
 *   Adds configured mobile pool settings to $scconf
1781
 * NOTES
1782
 *   These values were formerly in strongswan.conf but may now be configured in
1783
 *   pools, making future expansion to support multiple pools possible.
1784
 ******/
1785
function ipsec_setup_pools() {
1786
	global $config, $g, $mobile_ipsec_auth, $a_client, $a_phase2, $scconf;
1787
	if (!is_array($a_client) || !isset($a_client['enable'])) {
1788
		return;
1789
	}
1790
	if (($mobile_ipsec_auth == "eap-radius") && empty($a_client['pool_address']) &&
1791
	    empty($a_client['pool_address_v6'])) {
1792
		return;
1793
	}
1794

    
1795
	/* Keep the comment in the array to ensure the entry makes it into the
1796
	 * config in all cases.
1797
	 * See https://redmine.pfsense.org/issues/11891 */
1798
	$scconf['mobile-pool'] = array("# Mobile pool settings template");
1799
	$scconf['pools'] = array();
1800
	$pool_common =& $scconf['mobile-pool'];
1801

    
1802
	$rightdnsservers = array();
1803
	if (!empty($a_client['dns_server1'])) {
1804
		$rightdnsservers[] = $a_client['dns_server1'];
1805
	}
1806
	if (!empty($a_client['dns_server2'])) {
1807
		$rightdnsservers[] = $a_client['dns_server2'];
1808
	}
1809
	if (!empty($a_client['dns_server3'])) {
1810
		$rightdnsservers[] = $a_client['dns_server3'];
1811
	}
1812
	if (!empty($a_client['dns_server4'])) {
1813
		$rightdnsservers[] = $a_client['dns_server4'];
1814
	}
1815
	if (count($rightdnsservers)) {
1816
		$pool_common['dns'] = implode(',', $rightdnsservers);
1817
	}
1818

    
1819
	$cfgservers = array();
1820
	if (!empty($a_client['wins_server1'])) {
1821
		$cfgservers[] = $a_client['wins_server1'];
1822
	}
1823
	if (!empty($a_client['wins_server2'])) {
1824
		$cfgservers[] = $a_client['wins_server2'];
1825
	}
1826
	if (!empty($cfgservers)) {
1827
		$pool_common['nbns'] = implode(",", $cfgservers);
1828
	}
1829
	unset($cfgservers);
1830

    
1831
	$net_list4 = array();
1832
	$net_list6 = array();
1833
	if (isset($a_client['net_list']) && is_array($a_phase2)) {
1834
		foreach ($a_phase2 as $ph2ent) {
1835
			if (isset($ph2ent['disabled']) ||
1836
			    !isset($ph2ent['mobile'])) {
1837
				continue;
1838
			}
1839
			$nlent = ipsec_idinfo_to_cidr($ph2ent['localid'], true, $ph2ent['mode']);
1840
			if (is_subnetv4($nlent)) {
1841
				$net_list4[] = $nlent;
1842
			} elseif (is_subnetv6($nlent)) {
1843
				$net_list6[] = $nlent;
1844
			}
1845
			unset($nlent);
1846
		}
1847
	}
1848
	if (!empty($a_client['dns_domain'])) {
1849
		$pool_common[] = "# Search domain and default domain";
1850
		$pool_common['28674'] = "\"{$a_client['dns_domain']}\"";
1851
		if (empty($a_client['dns_split'])) {
1852
			$pool_common['28675'] = "\"{$a_client['dns_domain']}\"";
1853
		}
1854
	}
1855

    
1856
	if (!empty($a_client['dns_split'])) {
1857
		$pool_common['28675'] = "\"{$a_client['dns_split']}\"";
1858
	}
1859

    
1860
	if (!empty($a_client['login_banner'])) {
1861
		$pool_common['28672'] = "\"{$a_client['login_banner']}\"";
1862
	}
1863

    
1864
	if (isset($a_client['save_passwd'])) {
1865
		$pool_common['28673'] = "1";
1866
	}
1867

    
1868
	if ($a_client['pfs_group']) {
1869
		$pool_common['28679'] = "\"{$a_client['pfs_group']}\"";
1870
	}
1871

    
1872
	if (!empty($a_client['pool_address'])) {
1873
		$scconf['pools']['mobile-pool-v4 : mobile-pool'] = array();
1874
		$v4pool =& $scconf['pools']['mobile-pool-v4 : mobile-pool'];
1875
		$v4pool['addrs'] = "{$a_client['pool_address']}/{$a_client['pool_netbits']}";
1876
		if (!empty($net_list4)) {
1877
			$v4pool['subnet'] = implode(",", $net_list4);
1878
			$v4pool['split_include'] = implode(",", $net_list4);
1879
			unset($net_list4);
1880
		}
1881
	}
1882
	if (!empty($a_client['pool_address_v6'])) {
1883
		$scconf['pools']['mobile-pool-v6 : mobile-pool'] = array();
1884
		$v6pool =& $scconf['pools']['mobile-pool-v6 : mobile-pool'];
1885
		$v6pool['addrs'] = "{$a_client['pool_address_v6']}/{$a_client['pool_netbits_v6']}";
1886
		if (!empty($net_list6)) {
1887
			$v6pool['subnet'] = implode(",", $net_list6);
1888
			$v6pool['split_include'] = implode(",", $net_list6);
1889
			unset($net_list6);
1890
		}
1891
	}
1892

    
1893
	return;
1894
}
1895

    
1896
/****f* ipsec/ipsec_setup_userpools
1897
 * NAME
1898
 *   ipsec_setup_userpools - Generate per-user custom pool settings for swanctl
1899
  * INPUTS
1900
 *   None
1901
 * RESULT
1902
 *   Adds configured per-user pool settings to $scconf using the primary mobile
1903
 *   pool as a base configuration.
1904
 * NOTES
1905
 *   Given this new flexible format, it is now possible to override any valid
1906
 *   pool setting, so future expansion of per-user settings is possible.
1907
 ******/
1908
function ipsec_setup_userpools() {
1909
	global $config, $scconf;
1910
	$a_mobilekey = $config['ipsec']['mobilekey'];
1911

    
1912
	/* Do not waste time if we do not have all the necessary information. */
1913
	if (!is_array($a_mobilekey) ||
1914
	    empty($a_mobilekey) ||
1915
	    !is_array($scconf['connections']) ||
1916
	    !is_array($scconf['con-mobile-defaults']) ||
1917
	    !is_array($scconf['pools']) ||
1918
	    !is_array($scconf['mobile-pool'])) {
1919
		return;
1920
	}
1921

    
1922
	$suffix = 1;
1923
	foreach ($a_mobilekey as $mkent) {
1924
		if (($mkent['type'] != "EAP") ||
1925
		    !isset($mkent['ident_type']) ||
1926
		    !isset($mkent['pool_address']) ||
1927
		    !isset($mkent['pool_netbits']) ||
1928
		    (strlen($mkent['pool_address']) < 1) ||
1929
		    !is_ipaddr($mkent['pool_address'])) {
1930
			continue;
1931
		}
1932
		/* The name of just this pool */
1933
		$upbase = "mobile-userpool-{$suffix}";
1934
		/* The full connection name including a reference to the primary
1935
		 * mobile connection */
1936
		$upconn = "con-{$upbase} : con-mobile-defaults";
1937
		/* The full pool name including a reference to the primary
1938
		 * mobile pool */
1939
		$upname = "{$upbase} : mobile-pool";
1940

    
1941
		$scconf['connections'][$upconn] = array();
1942
		$scconf['pools'][$upname] = array();
1943

    
1944
		$clientid = (in_array($mkent['ident_type'], array('auto', 'none'))) ? "\"{$mkent['ident']}\"" : "{$mkent['ident_type']}:{$mkent['ident']}";
1945
		$clienteapid = ($ph1ent['authentication_method'] == "eap-mschapv2") ? $clientid : '%any';
1946

    
1947
		/* Craft a cloned connection with the ID information to match */
1948
		$scconf['connections'][$upconn]['remote'] = array();
1949
		$scconf['connections'][$upconn]['remote']['id'] = $clientid;
1950
		$scconf['connections'][$upconn]['remote']['eap_id'] = $clienteapid;
1951
		$scconf['connections'][$upconn]['pools'] = $upbase;
1952

    
1953
		/* Craft a cloned pool with pool settings to override for this user */
1954
		$scconf['pools'][$upname]['addrs'] = "{$mkent['pool_address']}/{$mkent['pool_netbits']}";
1955
		if (isset($mkent['dns_address']) && strlen($mkent['dns_address']) > 0 && is_ipaddr($mkent['dns_address'])) {
1956
			$scconf['pools'][$upname]['dns'] = $mkent['dns_address'];
1957
		}
1958
		$suffix++;
1959
	}
1960
	return;
1961
}
1962

    
1963
/****f* ipsec/ipsec_setup_bypass
1964
 * NAME
1965
 *   ipsec_setup_bypass - Generate a bypass connection for LAN-LAN and custom rules traffic
1966
 * INPUTS
1967
 *   None
1968
 * RESULT
1969
 *   Sets up a bypass connection to prevent local traffic from being caught by
1970
 *   IPsec policies.
1971
 ******/
1972
function ipsec_setup_bypass($gateways_status) {
1973
	global $config, $scconf;
1974

    
1975
	$scconf['connections']['bypass'] = array();
1976
	/* Prevents the connection from being considered for remote peers */
1977
	$scconf['connections']['bypass']['remote_addrs'] = "127.0.0.1";
1978
	$scconf['connections']['bypass']['children'] = array();
1979

    
1980
	/* Locate the LAN IPv4 and IPv6 subnets */
1981
	$bypassnets = array();
1982
	if ($config['interfaces']['lan']) {
1983
		$lanip = get_interface_ip("lan", $gateways_status);
1984
		if (!empty($lanip) && is_ipaddrv4($lanip)) {
1985
			$lansn = get_interface_subnet("lan");
1986
			$lansa = gen_subnetv4($lanip, $lansn);
1987
			if (!empty($lansa) && !empty($lansn)) {
1988
				$bypassnets[] = "{$lansa}/{$lansn}";
1989
			}
1990
		}
1991
		$lanip6 = get_interface_ipv6("lan", $gateways_status);
1992
		if (!empty($lanip6) && is_ipaddrv6($lanip6)) {
1993
			$lansn6 = get_interface_subnetv6("lan");
1994
			$lansa6 = gen_subnetv6($lanip6, $lansn6);
1995
			if (!empty($lansa6) && !empty($lansn6)) {
1996
				$bypassnets[] = "{$lansa6}/{$lansn6}";
1997
			}
1998
		}
1999
	}
2000
	/* If we have viable targets, setup a bypass LAN connection */
2001
	if (!empty($bypassnets) && !isset($config['ipsec']['noshuntlaninterfaces'])) {
2002
		$bypass = implode(',', $bypassnets);
2003
		$scconf['connections']['bypass']['children']['bypasslan'] = array();
2004
		$scconf['connections']['bypass']['children']['bypasslan']['local_ts'] = $bypass;
2005
		$scconf['connections']['bypass']['children']['bypasslan']['remote_ts'] = $bypass;
2006
		$scconf['connections']['bypass']['children']['bypasslan']['mode'] = "pass";
2007
		$scconf['connections']['bypass']['children']['bypasslan']['start_action'] = "trap";
2008
	}
2009

    
2010
	/* Setup custom bypass rules */
2011
	if ((isset($config['ipsec']['ipsecbypass']) && $config['ipsec']['bypassrules']) &&
2012
	    is_array($config['ipsec']['bypassrules'])) {
2013
		foreach ($config['ipsec']['bypassrules']['rule'] as $id => $rule) {
2014
			$scconf['connections']['bypass']['children']["rule{$id}"] = array();
2015
			$scconf['connections']['bypass']['children']["rule{$id}"]["local_ts"] = $rule['source'] .
2016
			       	"/" . $rule['srcmask'];
2017
			$scconf['connections']['bypass']['children']["rule{$id}"]["remote_ts"] = $rule['destination'] .
2018
			       	"/" . $rule['dstmask'];
2019
			$scconf['connections']['bypass']['children']["rule{$id}"]["mode"] = "pass";
2020
			$scconf['connections']['bypass']['children']["rule{$id}"]["start_action"] = "trap";
2021
		}
2022
	}
2023

    
2024
	return;
2025
}
2026

    
2027
/****f* ipsec/ipsec_setup_routes
2028
 * NAME
2029
 *   ipsec_setup_routes - Setup OS routing table static routes for remote peers.
2030
 *                        This ensures that IPsec connections are routed out of
2031
 *                        the expected interface on egress.
2032
 * INPUTS
2033
 *   $interface : The interface upon which routes will be configured.
2034
 *   $family    : The address family ('inet' or 'inet6')
2035
 *   $sourcehost: The local source address used for initiating connections.
2036
 *   $duplicates: If the ipsec tunnel allows duplicates remote peers
2037
 * RESULT
2038
 *   Static routes in the OS routing table for IPsec peers
2039
 ******/
2040
function ipsec_setup_routes($interface, $family, $sourcehost, $duplicates) {
2041
	if (substr($interface, 0, 4) == "_vip") {
2042
		$vpninterface = get_configured_vip_interface($interface);
2043
		if (substr($vpninterface, 0, 4) == "_vip") {
2044
			// vips are nested if its a ipalias with a carp parent
2045
			$vpninterface = get_configured_vip_interface($vpninterface);
2046
		}
2047
		$ifacesuse = get_real_interface($vpninterface);
2048
	} else {
2049
		$ifacesuse = get_failover_interface($interface);
2050
		if (substr($ifacesuse, 0, 4) == "_vip") {
2051
			$vpninterface = get_configured_vip_interface($ifacesuse);
2052
			$ifacesuse = get_real_interface($vpninterface);
2053
		} else {
2054
			$vpninterface = convert_real_interface_to_friendly_interface_name($ifacesuse);
2055
		}
2056
	}
2057
	if ($family == 'inet' && !empty($ifacesuse) &&
2058
	    interface_has_gateway($vpninterface)) {
2059
		$gatewayip = get_interface_gateway($vpninterface);
2060
		$interfaceip = get_interface_ip($vpninterface);
2061
		$subnet_bits = get_interface_subnet($vpninterface);
2062
		$subnet_ip = gen_subnetv4($interfaceip, $subnet_bits);
2063
		/*
2064
		 * if the remote gateway is in the local subnet, then don't add
2065
		 * a route
2066
		 */
2067
		if (is_ipaddrv4($sourcehost) &&
2068
		    !ip_in_subnet($sourcehost, "{$subnet_ip}/{$subnet_bits}") &&
2069
		    is_ipaddrv4($gatewayip) && !$duplicates) {
2070
			route_add_or_change($sourcehost, $gatewayip);
2071
		}
2072
	} else if ($family == 'inet6' && !empty($ifacesuse) &&
2073
	    interface_has_gatewayv6($vpninterface)) {
2074
		$gatewayip = get_interface_gateway_v6($vpninterface);
2075
		$interfaceip = get_interface_ipv6($vpninterface);
2076
		$subnet_bits = get_interface_subnetv6($vpninterface);
2077
		$subnet_ip = gen_subnetv6($interfaceip, $subnet_bits);
2078
		/*
2079
		 * if the remote gateway is in the local subnet, then don't add
2080
		 * a route
2081
		 */
2082
		if (is_ipaddrv6($sourcehost) &&
2083
		    !ip_in_subnet($sourcehost, "{$subnet_ip}/{$subnet_bits}") &&
2084
		    is_ipaddrv6($gatewayip) && !$duplicates) {
2085
			route_add_or_change($sourcehost, $gatewayip);
2086
		}
2087
	}
2088
	return $ifacesuse;
2089
}
2090

    
2091
/****f* ipsec/ipsec_setup_authentication
2092
 * NAME
2093
 *   ipsec_setup_authentication - Generate an array with local/remote
2094
 *                                authentication information for a given IPsec
2095
 *                                Phase 1.
2096
 * INPUTS
2097
 *   $ph1ent: An IPsec Phase 1 configuration
2098
 *   $conn  : A swanctl connection array corresponding to the IPsec Phase 1.
2099
 * RESULT
2100
 *   Populates $conn with local and remote arrays containing authentication
2101
 *   details.
2102
 ******/
2103
function ipsec_setup_authentication(& $ph1ent, & $conn, $gateways_status = false) {
2104
	global $rgmap, $ipsec_swanctl_dirs, $config;
2105
	$local = array();
2106
	$remote = array();
2107
	$remote2 = array();
2108

    
2109
	/* Fetch gateway status if not passed */
2110
	if (!is_array($gateways_status)) {
2111
		$gateways_status = return_gateways_status(true);
2112
	}
2113

    
2114
	list($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local', array(), $gateways_status);
2115
	$localid = ipsec_fixup_id($myid_type, $myid_data);
2116
	if (!empty($localid)) {
2117
		$local['id'] = $localid;
2118
	}
2119

    
2120
	// Only specify peer ID if we are not dealing with mobile PSK
2121
	if (!(isset($ph1ent['mobile']) &&
2122
	    in_array($ph1ent['authentication_method'], array("pre_shared_key", "xauth_psk_server")))) {
2123
		list ($remoteid_type, $remoteid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap, $gateways_status);
2124
		$remoteid = ipsec_fixup_id($remoteid_type, $remoteid_data);
2125
	}
2126
	if (!empty($remoteid)) {
2127
		$remote['id'] = $remoteid;
2128
	}
2129

    
2130
	if (!empty($ph1ent['caref'])) {
2131
		$ca = lookup_ca($ph1ent['caref']);
2132
		if ($ca) {
2133
			/* Get hash value to use for filename */
2134
			$ca_details = openssl_x509_parse(base64_decode($ca['crt']));
2135
			$cafn = "{$ipsec_swanctl_dirs['capath']}/{$ca_details['hash']}.0";
2136
		}
2137
	}
2138

    
2139
	$authentication = "";
2140
	switch ($ph1ent['authentication_method']) {
2141
		case 'eap-mschapv2':
2142
			if (isset($ph1ent['mobile'])) {
2143
				$local['auth'] = "pubkey";
2144
				$remote['eap_id'] = "%any";
2145
				$remote['auth'] = "eap-mschapv2";
2146
			}
2147
			break;
2148
		case 'eap-tls':
2149
			if (isset($ph1ent['mobile'])) {
2150
				$local['auth'] = "pubkey";
2151
				$remote['eap_id'] = "%any";
2152
			} else {
2153
				$local['auth'] = "eap-tls";
2154
			}
2155
			$remote['auth'] = "eap-tls";
2156
			break;
2157
		case 'eap-radius':
2158
			if (isset($ph1ent['mobile'])) {
2159
				$local['auth'] = "pubkey";
2160
				$remote['eap_id'] = "%any";
2161
			} else {
2162
				$local['auth'] = "eap-radius";
2163
			}
2164
			if (($config['ipsec']['client']['group_source'] == 'enabled') &&
2165
			    !empty($config['ipsec']['client']['auth_groups'])) {
2166
				$remote['groups'] = $config['ipsec']['client']['auth_groups'];
2167
			}
2168
			$remote['auth'] = "eap-radius";
2169
			break;
2170
		case 'xauth_cert_server':
2171
			$local['auth'] = "pubkey";
2172
			$remote['auth'] = "pubkey";
2173
			$remote2['auth'] = "xauth-generic";
2174
			break;
2175
		case 'xauth_psk_server':
2176
			$local['auth'] = "psk";
2177
			$remote['auth'] = "psk";
2178
			$remote2['auth'] = "xauth-generic";
2179
			break;
2180
		case 'pre_shared_key':
2181
			$local['auth'] = "psk";
2182
			$remote['auth'] = "psk";
2183
			break;
2184
		case 'cert':
2185
		case 'pkcs11':
2186
			$local['auth'] = "pubkey";
2187
			$remote['auth'] = "pubkey";
2188
			break;
2189
		case 'hybrid_cert_server':
2190
			$local['auth'] = "pubkey";
2191
			$remote['auth'] = "xauth-generic";
2192
			break;
2193
	}
2194
	if (in_array($ph1ent['authentication_method'], array('eap-mschapv2', 'eap-tls', 'eap-radius', 'xauth_cert_server', 'cert', 'hybrid_cert_server')) &&
2195
	    !empty($ph1ent['certref'])) {
2196
		$local['cert'] = array('file' => "{$ipsec_swanctl_dirs['certpath']}/cert-{$ph1ent['ikeid']}.crt");
2197
		$conn['send_cert'] = "always";
2198
	}
2199
	if ($ph1ent['authentication_method'] == 'pkcs11') {
2200
		$local['cert'] = array('handle' => "{$ph1ent['pkcs11certref']}");
2201
		$conn['send_cert'] = "always";
2202
	}
2203
	if (in_array($ph1ent['authentication_method'], array('eap-tls', 'xauth_cert_server', 'cert', 'pkcs11')) &&
2204
	    isset($cafn)) {
2205
		$remote['cacerts'] = $cafn;
2206
		if (isset($config['ipsec']['strictcrlpolicy'])) {
2207
			$remote['revocation'] = "strict";
2208
		}
2209
	}
2210

    
2211
	$conn['local'] = $local;
2212
	/* If there is data for a second remote auth round, setup two remote
2213
	 * arrays with unique names, otherwise setup a single remote. */
2214
	if (empty($remote2)) {
2215
		$conn['remote'] = $remote;
2216
	} else {
2217
		$conn['remote-1'] = $remote;
2218
		$conn['remote-2'] = $remote2;
2219
	}
2220
}
2221

    
2222
/****f* ipsec/ipsec_setup_proposal_algo
2223
 * NAME
2224
 *   ipsec_setup_proposal_algo - Form a single proposal algorithm string
2225
 * INPUTS
2226
 *   $ealg_id: Encryption algorithm ID
2227
 *   $keylen : Key length for the encryption algorithm
2228
 *   $halgo  : Hash algorithm
2229
 *   $modp   : DH Group number
2230
 * RESULT
2231
 *   Returns a string using the available information to form a single proposal
2232
 *   algorithm definition.
2233
 * NOTES
2234
 *   Values left empty will not be added to the string. Some combinations may
2235
 *   require one or more parts to be omitted.
2236
 ******/
2237
function ipsec_setup_proposal_algo($ealg_id, $keylen, $halgo, $prfalgo, $modp) {
2238
	$palgo = "";
2239

    
2240
	/* Add the encryption algorithm (if present) */
2241
	if (!empty($ealg_id)) {
2242
		$palgo .= "{$ealg_id}";
2243
	}
2244

    
2245
	/* Add the key length (if present) */
2246
	if (!empty($keylen)) {
2247
		$palgo .= "{$keylen}";
2248
	}
2249

    
2250
	/* Add the hash algorithm (if present) */
2251
	if (!empty($halgo)) {
2252
		/* If there is some content in the propsal already, add a
2253
		 * separator */
2254
		if (!empty($palgo)) {
2255
			$palgo .= "-";
2256
		}
2257
		$halgo = str_replace('hmac_', '', $halgo);
2258
		$palgo .= "{$halgo}";
2259
	}
2260

    
2261
	if (!empty($prfalgo)) {
2262
		$palgo .= "-prf{$prfalgo}";
2263
	}
2264

    
2265
	/* Convert the DH group to its keyword and add (if present) */
2266
	$modp = ipsec_convert_to_modp($modp);
2267
	if (!empty($modp)) {
2268
		$palgo .= "-{$modp}";
2269
	}
2270

    
2271
	return $palgo;
2272
}
2273

    
2274
/****f* ipsec/ipsec_setup_proposal_entry
2275
 * NAME
2276
 *   ipsec_setup_proposal_entry - Generate a full proposal string for an IPsec
2277
 *                                Phase 2 configuration.
2278
 * INPUTS
2279
 *   $ph2ent  : An IPsec Phase 2 configuration
2280
 *   $algo_arr: An array in which all proposal algorithms are collected
2281
 *   $ealg_id : Encryption algorithm ID
2282
 *   $keylen  : Key length for the encryption algorithm
2283
 * RESULT
2284
 *   Returns a string containing all proposal elements, and merges the entries
2285
 *   to $algo_arr as well.
2286
 ******/
2287
function ipsec_setup_proposal_entry(& $ph2ent, & $algo_arr, $ealg_id, $keylen) {
2288
	global $config, $p2_ealgos;
2289
	$proposal = array();
2290

    
2291
	/* If multiple hash algorithms are present, loop through and add them all. */
2292
	if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option']) 
2293
	    && !strpos($ealg_id, "gcm")) {
2294
		foreach ($ph2ent['hash-algorithm-option'] as $halgo) {
2295
			$proposal[] = ipsec_setup_proposal_algo($ealg_id, $keylen, $halgo, false, $ph2ent['pfsgroup']);
2296
		}
2297
	} else {
2298
		$proposal[] = ipsec_setup_proposal_algo($ealg_id, $keylen, false, false, $ph2ent['pfsgroup']);
2299
	}
2300

    
2301
	/* Add to master list */
2302
	$algo_arr = array_merge($algo_arr, $proposal);
2303
	return implode(',', $proposal);
2304
}
2305

    
2306
function ipsec_get_ifname($p1, $reqid = 0) {
2307
	global $config;
2308
	$ifname = "ipsec";
2309
	if ($reqid > 0) {
2310
		$ifname .= "{$reqid}";
2311
	} else {
2312
		$p2s = ipsec_get_phase2_by_ikeid($p1['ikeid']);
2313
		if (!empty($p2s)) {
2314
			$ifname .= $p2s[0]['reqid'];
2315
		}
2316
	}
2317
	return $ifname;
2318
}
2319

    
2320
function ipsec_get_ifname_by_conf($p1 = array(), $p2 = array()) {
2321
	global $config;
2322
	$have_p1 = (!empty($p1) && is_array($p1));
2323
	$have_p2 = (!empty($p2) && is_array($p2));
2324
	$reqid = null;
2325

    
2326
	if (!$have_p1 && $have_p2) {
2327
		$p1 = ipsec_get_phase1($p2['ikeid']);
2328
	}
2329

    
2330
	if ($have_p2) {
2331
		$reqid = $p2['reqid'];
2332
	}
2333

    
2334
	return ipsec_get_ifname($p1, $reqid);
2335
}
2336

    
2337
/****f* ipsec/ipsec_setup_vtireq
2338
 * NAME
2339
 *   ipsec_setup_vtireq - Setup a VTI type IPsec request
2340
 * INPUTS
2341
 *   $child                : A swanctl child array
2342
 *   $ipsec_vti_cleanup_ifs: An array of VTI interface names for later cleanup
2343
 *   $p1                   : The IKE config under which this child exists
2344
 *   $reqid                : The reqid of the P2 if known, otherwise the first P2 reqid for this P1 is looked up.
2345
 * RESULT
2346
 *   Sets up VTI-specific values for a request.
2347
 ******/
2348
function ipsec_setup_vtireq(& $child, & $ipsec_vti_cleanup_ifs, $p1, $reqid = 0) {
2349
	global $ipsec_reqid_base;
2350
	if ($reqid > 0) {
2351
		$child['reqid'] = $ipsec_reqid_base + $reqid;
2352
	} else {
2353
		$p2s = ipsec_get_phase2_by_ikeid($p1['ikeid']);
2354
		if (!empty($p2s)) {
2355
			$reqid = $p2s[0]['reqid'];
2356
			$child['reqid'] = $ipsec_reqid_base + $reqid;
2357
		} else {
2358
			return false;
2359
		}
2360
	}
2361
	/* This interface will be a valid IPsec interface, so remove it from the cleanup list. */
2362
	$ipsec_vti_cleanup_ifs = array_diff($ipsec_vti_cleanup_ifs, array(ipsec_get_ifname($p1, $reqid)));
2363
	$child['local_ts'] .= ",0.0.0.0/0,::/0";
2364
	$child['remote_ts'] .= ",0.0.0.0/0,::/0";
2365
	return true;
2366
}
2367

    
2368
/****f* ipsec/ipsec_setup_tunnels
2369
 * NAME
2370
 *   ipsec_setup_tunnels - Configure all P1/P2 entries as swanctl connections
2371
 * INPUTS
2372
 *   None
2373
 * RESULT
2374
 *   Sets up a swanctl array for all connections in the configuration along with
2375
 *   their children, authentication, etc.
2376
 ******/
2377
function ipsec_setup_tunnels($gateways_status = false) {
2378
	global $aggressive_mode_psk, $a_client, $config,
2379
		$filterdns_list, $g, $ifacesuse, $ipsec_idhandling, $ipsec_log_cats,
2380
		$ipsec_log_sevs, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs,
2381
		$ipseccfg, $mobile_ipsec_auth, $natfilterrules, $p1_ealgos,
2382
		$p2_ealgos, $rgmap, $sa, $sn, $scconf, $conn, $tunnels,
2383
		$ipsec_vti_cleanup_ifs, $conn_defaults;
2384

    
2385
	/* Fetch gateway status if not passed */
2386
	if (!is_array($gateways_status)) {
2387
		$gateways_status = return_gateways_status(true);
2388
	}
2389

    
2390
	foreach ($tunnels as $ph1ent) {
2391
		/* Skip disabled entries */
2392
		if (isset($ph1ent['disabled'])) {
2393
			continue;
2394
		}
2395
		/* If the local source is invalid, skip this entry. */
2396
		$local_spec = ipsec_get_phase1_src($ph1ent, $gateways_status);
2397
		if (!$local_spec) {
2398
			continue;
2399
		}
2400

    
2401
		/* Determine the name of this connection, either con-mobile for
2402
		 * mobile clients, or a name based on the IKE ID otherwise. */
2403
		if (isset($ph1ent['mobile'])) {
2404
			$cname = "con-mobile";
2405
			/* Start with common default values */
2406
			$scconf["{$cname}-defaults"] = $conn_defaults;
2407
			/* Array reference to make things easier */
2408
			$conn =& $scconf["{$cname}-defaults"];
2409
			$scconf['connections']["{$cname} : {$cname}-defaults"] = array("# Stub to load con-mobile-defaults");
2410
		} else {
2411
			$cname = ipsec_conid($ph1ent);
2412
			/* Start with common default values */
2413
			$scconf['connections'][$cname] = $conn_defaults;
2414
			$descr = "# P1 (ikeid {$ph1ent['ikeid']})";
2415
			if (!empty($ph1ent['descr'])) {
2416
				$descr .= ": {$ph1ent['descr']}";
2417
			}
2418
			array_unshift($scconf['connections'][$cname], $descr);
2419
			/* Array reference to make things easier */
2420
			$conn =& $scconf['connections'][$cname];
2421
		}
2422

    
2423
		/* Common parameters for all children */
2424
		$child_params = array();
2425
		if (!empty($ph1ent['closeaction'])) {
2426
			$child_params['close_action'] = $ph1ent['closeaction'];
2427
		}
2428

    
2429
		$ikeid = $ph1ent['ikeid'];
2430

    
2431
		$carpbackup = false;
2432
		if ((substr($ph1ent['interface'], 0, 4) == "_vip") &&
2433
		    in_array(get_carp_bind_status($ph1ent['interface']), array('BACKUP', 'INIT'))) {
2434
			$carpbackup = true;
2435
		}
2436

    
2437
		/* "trap" adds policies to start a tunnel when interesting
2438
		 * traffic is observed by the host. */
2439
		$start_action = "trap";
2440
		if ($carpbackup) {
2441
			$start_action = "none";
2442
		}
2443

    
2444
		/* Set the IKE version appropriately (empty = IKEv1) */
2445
		switch ($ph1ent['iketype']) {
2446
			case 'auto':
2447
				$ikeversion = 0;
2448
				break;
2449
			case 'ikev2':
2450
				$ikeversion = 2;
2451
				break;
2452
			case 'ikev1':
2453
			default:
2454
				$ikeversion = 1;
2455
				break;
2456
		}
2457
		$conn['version'] = $ikeversion;
2458

    
2459
		/* For IKEv1 or auto, setup aggressive mode if configured */
2460
		if ($ikeversion != 2){
2461
			$conn['aggressive'] = ($ph1ent['mode'] == "aggressive") ? "yes" : "no";
2462
		}
2463

    
2464
		if (isset($ph1ent['mobile'])) {
2465
			/* Mobile tunnels allow 'any' as a peer */
2466
			$remote_spec = "0.0.0.0/0,::/0";
2467
			/* Mobile tunnels cannot start automatically */
2468
			$start_action = 'none';
2469
		} else {
2470
			$remote_spec = $ph1ent['remote-gateway'];
2471
			$sourcehost = (is_ipaddr($remote_spec)) ? $remote_spec : $rgmap[$remote_spec];
2472
			$ifacesuse = ipsec_setup_routes($ph1ent['interface'], $ph1ent['protocol'], $sourcehost, isset($ph1ent['gw_duplicates']));
2473
		}
2474

    
2475
		/* Setup IKE proposals */
2476
		if (is_array($ph1ent['encryption']['item'])) {
2477
			$ciphers = array();
2478
			foreach($ph1ent['encryption']['item'] as $p1enc) {
2479
				if (!is_array($p1enc['encryption-algorithm']) ||
2480
						empty($p1enc['encryption-algorithm']['name']) ||
2481
						empty($p1enc['hash-algorithm'])) {
2482
					continue;
2483
				}
2484
				if ($ph1ent['prfselect_enable'] != 'yes') {
2485
					$p1enc['prf-algorithm'] = false;
2486
				}
2487
				$ciphers[] = ipsec_setup_proposal_algo($p1enc['encryption-algorithm']['name'],
2488
									$p1enc['encryption-algorithm']['keylen'],
2489
									$p1enc['hash-algorithm'],
2490
									$p1enc['prf-algorithm'],
2491
									$p1enc['dhgroup']);
2492
			}
2493
			$conn['proposals'] = implode(",", $ciphers);
2494
		}
2495

    
2496
		/* Configure DPD values. The DPD action is a per-child parameter,
2497
		 * not per-connection like the delay and timeout. */
2498
		if ($ph1ent['dpd_delay'] && $ph1ent['dpd_maxfail']) {
2499
			if ($start_action == "trap") {
2500
				$child_params['dpd_action'] = "trap";
2501
			} else {
2502
				$child_params['dpd_action'] = "clear";
2503
			}
2504
			$conn['dpd_delay'] = "{$ph1ent['dpd_delay']}s";
2505
			$conn['dpd_timeout'] =  $ph1ent['dpd_delay'] * ($ph1ent['dpd_maxfail'] + 1) . "s";
2506

    
2507
			/* Adjust dpd_action if the close_action is explicitly set */
2508
			if (!empty($child_params['close_action'])) {
2509
				switch ($ph1ent['closeaction']) {
2510
					case 'trap':
2511
						$child_params['dpd_action'] = 'trap';
2512
						break;
2513
					case 'start':
2514
						$child_params['dpd_action'] = 'restart';
2515
						break;
2516
					case 'none':
2517
					default:
2518
						$child_params['dpd_action'] = 'clear';
2519
				}
2520
			}
2521
		} else {
2522
			$child_params['dpd_action'] = "clear";
2523
		}
2524

    
2525
		/* Rekey (IKEv2 or Auto only, not supported by IKEv1) */
2526
		if (($ikeversion == 0) || ($ikeversion == 2)) {
2527
			$conn['rekey_time'] = ipsec_get_rekey_time($ph1ent) . "s";
2528
		}
2529

    
2530
		/* Reauth (Any) */
2531
		$conn['reauth_time'] = ipsec_get_reauth_time($ph1ent) . "s";
2532

    
2533
		/* Over Time */
2534
		$conn['over_time'] = ipsec_get_over_time($ph1ent) . "s";
2535

    
2536
		/* Rand Time */
2537
		$conn['rand_time'] = ipsec_get_rand_time($ph1ent) . "s";
2538

    
2539
		/* NAT Traversal */
2540
		$conn['encap'] = ($ph1ent['nat_traversal'] == 'force') ? "yes" : "no";
2541

    
2542
		/* MOBIKE support */
2543
		$conn['mobike'] = ($ph1ent['mobike'] == 'on') ? "yes" : "no";
2544

    
2545
		/* TFC Padding */
2546
		if (isset($ph1ent['tfc_enable'])) {
2547
			$conn['tfc_padding'] = (isset($ph1ent['tfc_bytes']) && is_numericint($ph1ent['tfc_bytes'])) ? $ph1ent['tfc_bytes'] : "mtu";
2548
		}
2549

    
2550
		/* Custom Ports */
2551
		if (isset($ph1ent['ikeport'])) {
2552
			/* For a connection without encapsulation, do not set local_port */
2553
			$conn['remote_port'] = $ph1ent['ikeport'];
2554
		} elseif (isset($ph1ent['nattport'])) {
2555
			/* For an encapsulated connection,
2556
			 * set local_port to the server NAT-T port value or default (4500) */
2557
			$conn['local_port'] = isset($config['ipsec']['port_nat_t']) ? $config['ipsec']['port_nat_t'] : 4500;
2558
			$conn['remote_port'] = $ph1ent['nattport'];
2559
		}
2560

    
2561
		/* Arrays for P2s/children */
2562
		$children = array();
2563
		$remote_ts_spec = array();
2564
		$local_ts_spec = array();
2565
		$reqids = array();
2566
		$has_vti = false;
2567
		$ealgoAHsp2arr = array();
2568
		$ealgoESPsp2arr = array();
2569

    
2570
		foreach ($ph1ent['p2'] as $ph2ent) {
2571
			/* If this entry is disabled, or cannot be configured, skip. */
2572
			if (isset($ph2ent['disabled']) ||
2573
			    (isset($ph2ent['mobile']) && !isset($a_client['enable']))) {
2574
				continue;
2575
			}
2576
			$child = array();
2577
			$local_ts = "";
2578
			$remote_ts = "";
2579
			if (($ph2ent['mode'] == 'tunnel') or ($ph2ent['mode'] == 'tunnel6')) {
2580
				/* Normal tunnel child config */
2581
				$child['mode'] = "tunnel";
2582
				$child['policies'] = "yes";
2583

    
2584
				$localid_type = $ph2ent['localid']['type'];
2585
				$localsubnet_data = ipsec_idinfo_to_cidr($ph2ent['localid'], false, $ph2ent['mode']);
2586

    
2587
				/* Do not print localid in some cases, such as a pure-psk or psk/xauth single phase2 mobile tunnel */
2588
				if (($localid_type == "none" || $localid_type == "mobile") &&
2589
				    isset($ph1ent['mobile']) && (ipsec_get_number_of_phase2($ikeid) == 1)) {
2590
					$local_spec = '0.0.0.0/0,::/0';
2591
				} else {
2592
					if ($localid_type != "address") {
2593
						$localid_type = "subnet";
2594
					}
2595
					// Don't let an empty subnet into config, it can cause parse errors. Ticket #2201.
2596
					if (!is_ipaddr($localsubnet_data) && !is_subnet($localsubnet_data) && ($localsubnet_data != "0.0.0.0/0")) {
2597
						log_error("Invalid IPsec Phase 2 \"{$ph2ent['descr']}\" - {$ph2ent['localid']['type']} has no subnet.");
2598
						continue;
2599
					}
2600
					if (!empty($ph2ent['natlocalid'])) {
2601
						$natlocalsubnet_data = ipsec_idinfo_to_cidr($ph2ent['natlocalid'], false, $ph2ent['mode']);
2602
						if ($ph2ent['natlocalid']['type'] != "address") {
2603
							if (is_subnet($natlocalsubnet_data)) {
2604
								$localsubnet_data = "{$natlocalsubnet_data}|{$localsubnet_data}";
2605
							}
2606
						} else {
2607
							if (is_ipaddr($natlocalsubnet_data)) {
2608
								$localsubnet_data = "{$natlocalsubnet_data}|{$localsubnet_data}";
2609
							}
2610
						}
2611
						$natfilterrules = true;
2612
					}
2613
				}
2614

    
2615
				$local_ts = $localsubnet_data;
2616

    
2617
				if (!isset($ph2ent['mobile'])) {
2618
					$remote_ts = ipsec_idinfo_to_cidr($ph2ent['remoteid'], false, $ph2ent['mode']);
2619
				} else if (!empty($a_client['pool_address'])) {
2620
					$remote_ts = "{$a_client['pool_address']}/{$a_client['pool_netbits']}";
2621
				}
2622

    
2623
			} elseif ($ph2ent['mode'] == 'vti') {
2624
				/* VTI-specific child config */
2625
				$child['policies'] = "no";
2626
				/* VTI cannot use trap policies, so start automatically instead */
2627
				$start_action = 'start';
2628
				if ($carpbackup) {
2629
					$start_action = "none";
2630
				}
2631
				if ($child_params['dpd_action'] == "trap") {
2632
					$child_params['dpd_action'] = "restart";
2633
				}
2634
				$localid_type = $ph2ent['localid']['type'];
2635
				$localsubnet_data = ipsec_idinfo_to_cidr($ph2ent['localid'], false, $ph2ent['mode']);
2636
				$local_ts = $localsubnet_data;
2637
				$remote_ts = ipsec_idinfo_to_cidr($ph2ent['remoteid'], false, $ph2ent['mode']);
2638
				$has_vti = true;
2639
			} else {
2640
				/* Transport mode child config */
2641
				$child['mode'] = "transport";
2642
				$child['policies'] = "yes";
2643

    
2644
				if ((($ph1ent['authentication_method'] == "xauth_psk_server") ||
2645
				    ($ph1ent['authentication_method'] == "pre_shared_key")) &&
2646
				    isset($ph1ent['mobile'])) {
2647
					$local_spec = "0.0.0.0/0,::/0";
2648
				} else {
2649
					$local_ts = ipsec_get_phase1_src($ph1ent, $gateways_status);
2650
				}
2651

    
2652
				if (!isset($ph2ent['mobile'])) {
2653
					$remote_ts = $remote_spec;
2654
				}
2655
			}
2656

    
2657
			if (!empty($local_ts)) {
2658
				$local_ts_spec[] = $local_ts;
2659
			}
2660
			if (!empty($remote_ts)) {
2661
				$remote_ts_spec[] = $remote_ts;
2662
			}
2663

    
2664
			/* If a PFS group is configured on the Mobile Clients tab,
2665
			 * and this is a mobile P2, use it here. */
2666
			if (isset($a_client['pfs_group']) && isset($ph2ent['mobile'])) {
2667
				$ph2ent['pfsgroup'] = $a_client['pfs_group'];
2668
			}
2669

    
2670
			$reqids[] = $ph2ent['reqid'];
2671

    
2672
			if (!empty($ph2ent['lifetime'])) {
2673
				$child['life_time'] = ipsec_get_life_time($ph2ent) . "s";
2674
				/* Rekey at 90% of lifetime */
2675
				$child['rekey_time'] = ipsec_get_rekey_time($ph2ent) . "s";
2676
				/* Random rekey fuzz time */
2677
				$child['rand_time'] = ipsec_get_rand_time($ph2ent) . "s";
2678
			}
2679

    
2680
			/* Set Child SA Start Action based on user preference, except
2681
			 * for invalid combinations such as 'trap' on VTI */
2682
			if (!isset($ph2ent['mobile']) && !$carpbackup &&
2683
			    !(($ph2ent['mode'] == 'vti') && ($ph1ent['startaction'] == 'trap'))) {
2684
				$start_action = !empty($ph1ent['startaction']) ? $ph1ent['startaction'] : $start_action;
2685
			}
2686
			$child['start_action'] = $start_action;
2687

    
2688
			/* Setup child SA proposals */
2689
			$proposal = array();
2690
			$ph2ent_ealgos = array();
2691
			$ph2ent_ealgos_aead = array();
2692
			if ($ph2ent['protocol'] == 'esp') {
2693
				if (is_array($ph2ent['encryption-algorithm-option'])) {
2694
					foreach ($ph2ent['encryption-algorithm-option'] as $ealg) {
2695
						if (strpos($ealg['name'], "gcm")) {
2696
							/* put AEAD ciphers on top, 
2697
							*  see https://redmine.pfsense.org/issues/11078 */
2698
							$ph2ent_ealgos_aead[] = $ealg;
2699
						} else {
2700
							$ph2ent_ealgos[] = $ealg;
2701
						}
2702
					}
2703
					$ph2ent_ealgos = array_merge(array_reverse($ph2ent_ealgos_aead), $ph2ent_ealgos);
2704
					foreach ($ph2ent_ealgos as $ealg) {
2705
						$ealg_id = $ealg['name'];
2706
						$ealg_kl = $ealg['keylen'];
2707

    
2708
						if (!empty($ealg_kl) && $ealg_kl == "auto") {
2709
							if (empty($p2_ealgos) || !is_array($p2_ealgos)) {
2710
								require_once("ipsec.inc");
2711
							}
2712
							$key_hi = $p2_ealgos[$ealg_id]['keysel']['hi'];
2713
							$key_lo = $p2_ealgos[$ealg_id]['keysel']['lo'];
2714
							$key_step = $p2_ealgos[$ealg_id]['keysel']['step'];
2715
							/* XXX: in some cases where include ordering is suspect these variables
2716
							 * are somehow 0 and we enter this loop forever and timeout after 900
2717
							 * seconds wrecking bootup */
2718
							if ($key_hi != 0 and $key_lo != 0 and $key_step != 0) {
2719
								for ($keylen = $key_hi; $keylen >= $key_lo; $keylen -= $key_step) {
2720
									$proposal[] = ipsec_setup_proposal_entry($ph2ent, $ealgoESPsp2arr, $ealg_id, $keylen);
2721
								}
2722
							}
2723
						} else {
2724
							$proposal[] = ipsec_setup_proposal_entry($ph2ent, $ealgoESPsp2arr, $ealg_id, $ealg_kl);
2725
						}
2726
					}
2727
				}
2728
			} else if ($ph2ent['protocol'] == 'ah') {
2729
				$proposal[] = ipsec_setup_proposal_entry($ph2ent, $ealgoAHsp2arr, '', '');
2730
			}
2731

    
2732
			/* Not mobile, and IKEv1 or Split Connections active */
2733
			if (!isset($ph1ent['mobile']) && (($ikeversion == 1) || isset($ph1ent['splitconn']))) {
2734
				if (!empty($remote_ts)) {
2735
					/* Setup child sub-connections using unique names */
2736
					$subconname = ipsec_conid($ph1ent, $ph2ent);
2737
					$children[$subconname] = $child;
2738

    
2739
					$descr = "# P2 (reqid {$ph2ent['reqid']})";
2740
					if (!empty($ph2ent['descr'])) {
2741
						$descr .= ": {$ph2ent['descr']}";
2742
					}
2743
					array_unshift($children[$subconname], $descr);
2744
					$children[$subconname]['local_ts'] = $local_ts;
2745
					$children[$subconname]['remote_ts'] = $remote_ts;
2746
					if ($has_vti) {
2747
						ipsec_setup_vtireq($children[$subconname], $ipsec_vti_cleanup_ifs, $ph1ent, $ph2ent['reqid']);
2748
					}
2749
					if (!empty($ph2ent['protocol']) && !empty($proposal)) {
2750
						$children[$subconname][$ph2ent['protocol'] . '_proposals'] = implode(',', $proposal);
2751
					}
2752
				} else {
2753
					log_error(sprintf(gettext("No phase2 specifications for tunnel with ikeid = %s"), $ikeid));
2754
				}
2755
			} else {
2756
				/* TODO: Fix this to nicely merge all P2 params for single child case */
2757
				if (!is_array($children[$cname])) {
2758
					$children[$cname] = array();
2759
				}
2760
				$children[$cname] = array_merge($children[$cname], $child);
2761

    
2762
				$descr = "# P2 (reqid {$ph2ent['reqid']})";
2763
				if (!empty($ph2ent['descr'])) {
2764
					$descr .= ": {$ph2ent['descr']}";
2765
				}
2766
				array_unshift($children[$cname], $descr);
2767
			}
2768
		}
2769

    
2770
		$conn['local_addrs'] = $local_spec;
2771
		$conn['remote_addrs'] = $remote_spec;
2772

    
2773
		$pools = array();
2774
		if (isset($ph1ent['mobile'])) {
2775
			if (($ph1ent['authentication_method'] == 'eap-radius') && 
2776
			    empty($a_client['pool_address']) && empty($a_client['pool_address_v6'])) {
2777
				$pools[] = "radius";
2778
			} else {
2779
				if (!empty($a_client['pool_address'])) {
2780
					$pools[] = "mobile-pool-v4";
2781
				}
2782
				if (!empty($a_client['pool_address_v6'])) {
2783
					$pools[] = "mobile-pool-v6";
2784
				}
2785
				if (isset($a_client['radius_ip_priority_enable'])) {
2786
					$pools[] .= "radius";
2787
				}
2788
			}
2789
		}
2790
		if (!empty($pools)) {
2791
			$conn['pools'] = implode(', ', $pools);
2792
		}
2793

    
2794
		/* For IKEv2 without Split Connections, setup combined sets of
2795
		 * local/remote traffic selectors and propsals */
2796
		if (!(!isset($ph1ent['mobile']) && (($ikeversion == 1) || isset($ph1ent['splitconn'])))) {
2797
			if (!isset($ph1ent['mobile']) && !empty($remote_ts_spec)) {
2798
				$children[$cname]['remote_ts'] = implode(",", $remote_ts_spec);
2799
			}
2800
			if (!empty($local_ts_spec)) {
2801
				$children[$cname]['local_ts'] = implode(",", $local_ts_spec);
2802
			}
2803
			if ($has_vti) {
2804
				ipsec_setup_vtireq($children[$cname], $ipsec_vti_cleanup_ifs, $ph1ent);
2805
			}
2806
			if (!empty($ealgoAHsp2arr)) {
2807
				$children[$cname]['ah_proposals'] = implode(',', array_unique($ealgoAHsp2arr));
2808
			}
2809
			if (!empty($ealgoESPsp2arr)) {
2810
				$children[$cname]['esp_proposals'] = implode(',', array_unique($ealgoESPsp2arr));
2811
			}
2812
		}
2813

    
2814
		/* Setup connection authentication */
2815
		ipsec_setup_authentication($ph1ent, $conn, $gateways_status);
2816

    
2817
		/* Add children to this connection, including default child parameters */
2818
		if (count($children)) {
2819
			foreach($children as $name => $child) {
2820
				$conn['children'][$name] = array_merge($child, $child_params);
2821
			}
2822
		}
2823
		unset ($ph2ent);
2824
	}
2825

    
2826
	return;
2827
}
2828

    
2829
/****f* ipsec/ipsec_setup_secrets
2830
 * NAME
2831
 *   ipsec_setup_secrets - Setup swanctl authentication secrets
2832
 * INPUTS
2833
 *   None
2834
 * RESULT
2835
 *   Returns a swanctl array containing secrets (PSKs, certs, etc) and writes out
2836
 *   necessary CA, CRL, and certificate data.
2837
 ******/
2838
function ipsec_setup_secrets($gateways_status = false) {
2839
	global $config, $a_phase1, $ipsec_swanctl_dirs, $ipseccfg, $rgmap, $scconf;
2840

    
2841
	$suffix = 0;
2842

    
2843
	/* write out CRL files */
2844
	if (is_array($config['crl']) && count($config['crl'])) {
2845
		foreach ($config['crl'] as $crl) {
2846
			if (!isset($crl['text'])) {
2847
				log_error(sprintf(gettext("Warning: Missing CRL data for %s"), $crl['descr']));
2848
				continue;
2849
			}
2850
			$fpath = "{$ipsec_swanctl_dirs['crlpath']}/{$crl['refid']}.crl";
2851
			if (!@file_put_contents($fpath, base64_decode($crl['text']))) {
2852
				log_error(sprintf(gettext("Error: Cannot write IPsec CRL file for %s"), $crl['descr']));
2853
				continue;
2854
			}
2855
		}
2856
	}
2857

    
2858
	/* Fetch gateway status if not passed */
2859
	if (!is_array($gateways_status)) {
2860
		$gateways_status = return_gateways_status(true);
2861
	}
2862

    
2863
	$vpncas = array();
2864
	if (is_array($a_phase1) && count($a_phase1)) {
2865
		foreach ($a_phase1 as $ph1ent) {
2866
			if (isset($ph1ent['disabled'])) {
2867
				continue;
2868
			}
2869

    
2870
			if (isset($ph1ent['mobile'])) {
2871
				if ($ph1ent['authentication_method'] == 'pre_shared_key') {
2872
					$mobilepsk = true;
2873
				} elseif ($ph1ent['authentication_method'] == 'eap-mschapv2') {
2874
					$mobileeap = true;
2875
				}
2876
			}
2877

    
2878
			if (strstr($ph1ent['authentication_method'], 'cert') ||
2879
			    in_array($ph1ent['authentication_method'], array('eap-mschapv2', 'eap-tls', 'eap-radius'))) {
2880
				/* Write certificate and private key, point to private key */
2881
				$certline = '';
2882

    
2883
				$ikeid = $ph1ent['ikeid'];
2884
				$cert = lookup_cert($ph1ent['certref']);
2885

    
2886
				if (!$cert) {
2887
					log_error(sprintf(gettext("Error: Invalid phase1 certificate reference for %s"), $ph1ent['name']));
2888
					continue;
2889
				}
2890

    
2891
				/* add signing CA cert chain of server cert
2892
				 * to the list of CAs to write
2893
				 */
2894
				$cachain = ca_chain_array($cert);
2895
				if ($cachain && is_array($cachain)) {
2896
					foreach ($cachain as $cacrt) {
2897
						$vpncas[$cacrt['refid']] = $cacrt;
2898
					}
2899
				}
2900

    
2901
				@chmod($ipsec_swanctl_dirs['certpath'], 0600);
2902

    
2903
				$ph1keyfile = "{$ipsec_swanctl_dirs['keypath']}/cert-{$ikeid}.key";
2904
				if (!file_put_contents($ph1keyfile, base64_decode($cert['prv']))) {
2905
					log_error(sprintf(gettext("Error: Cannot write phase1 key file for %s"), $ph1ent['name']));
2906
					continue;
2907
				}
2908
				@chmod($ph1keyfile, 0600);
2909

    
2910
				$ph1certfile = "{$ipsec_swanctl_dirs['certpath']}/cert-{$ikeid}.crt";
2911
				if (!file_put_contents($ph1certfile, base64_decode($cert['crt']))) {
2912
					log_error(sprintf(gettext("Error: Cannot write phase1 certificate file for %s"), $ph1ent['name']));
2913
					@unlink($ph1keyfile);
2914
					continue;
2915
				}
2916
				@chmod($ph1certfile, 0600);
2917

    
2918
				$scconf['secrets']['private-' . $suffix++] = array('file' => $ph1keyfile);
2919
			} else if (strstr($ph1ent['authentication_method'], 'pkcs11')) {
2920
				$p11_id = array();
2921
				$output = shell_exec('/usr/local/bin/pkcs15-tool -c');
2922
				preg_match_all('/ID\s+: (.*)/', $output, $p11_id);
2923
				if (!empty($ph1ent['pkcs11certref']) && in_array($ph1ent['pkcs11certref'], $p11_id[1])) {
2924
					$scconf['secrets']['token-' . $suffix++] = array(
2925
						'handle' => $ph1ent['pkcs11certref'],
2926
						'pin' => $ph1ent['pkcs11pin'],
2927
					);
2928
				} else {
2929
					log_error(sprintf(gettext("Error: Invalid phase1 PKCS#11 certificate reference or PKCS#11 is not present for %s"), $ph1ent['name']));
2930
					continue;
2931
				}
2932
			} else {
2933
				/* Setup pre-shared keys */
2934
				list($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local', array(), $gateways_status);
2935
				list($peerid_type, $peerid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap, $gateways_status);
2936
				$myid = trim($myid_data);
2937

    
2938
				if (empty($peerid_data)) {
2939
					continue;
2940
				}
2941

    
2942
				$myid = isset($ph1ent['mobile']) ? ipsec_fixup_id($myid_type, trim($myid_data)) : "%any";
2943
				$peerid = ($peerid_data != 'allusers') ? ipsec_fixup_id($peerid_type, trim($peerid_data)) : '';
2944

    
2945
				if (!empty($ph1ent['pre-shared-key'])) {
2946
					$scconf['secrets']['ike-' . $suffix++] = array(
2947
						'secret' => '0s' . base64_encode(trim($ph1ent['pre-shared-key'])),
2948
						'id-0' => $myid,
2949
						'id-1' => $peerid,
2950
					);
2951
					if (isset($ph1ent['mobile'])) {
2952
						$scconf['secrets']['ike-' . $suffix++] = array(
2953
							'secret' => '0s' . base64_encode(trim($ph1ent['pre-shared-key'])),
2954
							'id-0' => $myid,
2955
							'id-1' => '%any',
2956
						);
2957
						$scconf['secrets']['ike-' . $suffix++] = array(
2958
							'secret' => '0s' . base64_encode(trim($ph1ent['pre-shared-key'])),
2959
						);
2960
					}
2961
				}
2962
			}
2963

    
2964
			/* if the client authenticates with a cert add the
2965
			 * client cert CA chain to the list of CAs to write
2966
			 */
2967
			if (in_array($ph1ent['authentication_method'],
2968
			    array('cert', 'eap-tls', 'xauth_cert_server', 'pkcs11'))) {
2969
				if (!empty($ph1ent['caref']) && !array_key_exists($ph1ent['caref'], $vpncas)) {
2970
					$thisca = lookup_ca($ph1ent['caref']);
2971
					$vpncas[$ph1ent['caref']] = $thisca;
2972
					/* follow chain up to root */
2973
					$cachain = ca_chain_array($thisca);
2974
					if ($cachain and is_array($cachain)) {
2975
						foreach ($cachain as $cacrt) {
2976
							$vpncas[$cacrt['refid']] = $cacrt;
2977
						}
2978
					}
2979
				}
2980
			}
2981
		}
2982
	}
2983

    
2984
	/* Write the required CAs */
2985
	foreach ($vpncas as $carefid => $cadata) {
2986
		$cacrt = base64_decode($cadata['crt']);
2987
		$cacrtattrs = openssl_x509_parse($cacrt);
2988
		if (!is_array($cacrtattrs) || !isset($cacrtattrs['hash'])) {
2989
			log_error(sprintf(gettext("Error: Invalid certificate hash info for %s"), $cadata['descr']));
2990
			continue;
2991
		}
2992
		$cafilename = "{$ipsec_swanctl_dirs['capath']}/{$cacrtattrs['hash']}.0";
2993
		if (!@file_put_contents($cafilename, $cacrt)) {
2994
				log_error(sprintf(gettext("Error: Cannot write IPsec CA file for %s"), $cadata['descr']));
2995
				continue;
2996
		}
2997
	}
2998

    
2999
	/* Add user PSKs */
3000
	if ($mobilepsk && is_array($config['system']) && is_array($config['system']['user'])) {
3001
		foreach ($config['system']['user'] as $user) {
3002
			if (!empty($user['ipsecpsk'])) {
3003
				$scconf['secrets']['ike-' . $suffix++] = array(
3004
					'secret' => '0s' . base64_encode(trim($user['ipsecpsk'])),
3005
					'id-0' => $myid,
3006
					'id-1' => $user['name'],
3007
				);
3008
			}
3009
		}
3010
		unset($user);
3011
	}
3012

    
3013
	/* add PSKs/EAPs for mobile clients */
3014
	if (is_array($ipseccfg['mobilekey'])) {
3015
		foreach ($ipseccfg['mobilekey'] as $key) {
3016
			if (($mobileeap && ($key['type'] == 'EAP')) ||
3017
			    ($key['type'] == 'PSK')) {
3018
				if (($key['ident'] == 'allusers') ||
3019
				    ($key['ident'] == 'any')) {
3020
					$key['ident'] = '%any';
3021
				}
3022
				$userkeyprefix = (strtolower($key['type']) == 'eap') ? 'eap' : 'ike';
3023
				$scconf['secrets'][$userkeyprefix . '-' . $suffix++] = array(
3024
					'secret' => '0s' . base64_encode(trim($key['pre-shared-key'])),
3025
					'id-0' => $key['ident'],
3026
				);
3027
			}
3028
		}
3029
		unset($key);
3030
	}
3031
	return;
3032
}
3033

    
3034
/****f* ipsec/ipsec_configure
3035
 * NAME
3036
 *   ipsec_configure - Configure IPsec
3037
 * INPUTS
3038
 *   $restart: Boolean (default false), whether or not to restart the IPsec
3039
 *             daemons.
3040
 * RESULT
3041
 *   IPsec-related configuration files are written, daemon is started or stopped
3042
 *   appropriately.
3043
 ******/
3044
function ipsec_configure($restart = false) {
3045
	global $aggressive_mode_psk, $a_client, $a_phase1, $a_phase2, $config,
3046
		$filterdns_list, $g, $ifacesuse, $ipsec_idhandling, $ipsec_log_cats,
3047
		$ipsec_log_sevs, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs,
3048
		$ipseccfg, $mobile_ipsec_auth, $natfilterrules, $p1_ealgos,
3049
		$p2_ealgos, $rgmap, $sa, $sn, $scconf, $tunnels, $mobile_configured,
3050
		$ipsec_vti_cleanup_ifs, $conn_defaults, $pool_addrs;
3051

    
3052
	/* service may have been enabled, disabled, or otherwise changed in a
3053
	 *way requiring rule updates */
3054
	filter_configure();
3055

    
3056
	if (!ipsec_enabled()) {
3057
		/* IPsec is disabled */
3058
		/* Stop charon */
3059
		mwexec("/usr/local/sbin/strongswanrc stop");
3060
		/* Stop dynamic monitoring */
3061
		killbypid("{$g['varrun_path']}/filterdns-ipsec.pid");
3062
		/* Wait for process to die */
3063
		sleep(2);
3064
		/* Stop PC/SC Smart Card Services */
3065
		killbyname("pcscd");
3066
		/* Shutdown enc0 interface*/
3067
		mwexec("/sbin/ifconfig enc0 down");
3068
		ipsec_gre_default_mtu(); 
3069
		return 0;
3070
	} else {
3071
		/* Startup enc0 interface */
3072
		mwexec("/sbin/ifconfig enc0 up");
3073
		/* PC/SC daemon must be started before strongswan */
3074
		if (isset($config['ipsec']['pkcs11support']) && !isvalidproc("pcscd")) {
3075
			if (platform_booting()) {
3076
				echo gettext("Starting PC/SC Smart Card Services...");
3077
			}
3078
			mwexec_bg("/usr/local/sbin/pcscd");
3079
			if (platform_booting()) {
3080
				echo gettext("done.\n");
3081
			}
3082
		} elseif (!isset($config['ipsec']['pkcs11support']) && isvalidproc("pcscd")) {
3083
			/* strongswan must be stopped first, otherwise it will flood the logs */
3084
			mwexec("/usr/local/sbin/strongswanrc stop");
3085
			killbyname("pcscd");
3086
		}
3087
	}
3088

    
3089
	if (platform_booting()) {
3090
		echo gettext("Configuring IPsec VPN... ");
3091
	}
3092

    
3093
	$ipsecstartlock = lock('ipsec', LOCK_EX);
3094

    
3095
	/* Prepare automatic ping_hosts.sh data */
3096
	unlink_if_exists("{$g['vardb_path']}/ipsecpinghosts");
3097
	touch("{$g['vardb_path']}/ipsecpinghosts");
3098
	$ipsecpinghostsactive = false;
3099

    
3100
	/* Populate convenience variables */
3101
	$syscfg = $config['system'];
3102
	init_config_arr(array('ipsec', 'phase1'));
3103
	$ipseccfg = $config['ipsec'];
3104
	$a_phase1 = $config['ipsec']['phase1'];
3105
	init_config_arr(array('ipsec', 'phase2'));
3106
	$a_phase2 = $config['ipsec']['phase2'];
3107
	init_config_arr(array('ipsec', 'client'));
3108
	$a_client = $config['ipsec']['client'];
3109

    
3110
	$mobile_configured = false;
3111

    
3112

    
3113
	/* Setup a single structured array to process, to avoid repeatedly
3114
	 * looping through the arrays to locate entries later. */
3115
	$tunnels = array();
3116
	foreach ($a_phase1 as $p1) {
3117
		if (empty($p1)) {
3118
			continue;
3119
		}
3120
		if (isset($p1['mobile']) && !isset($p1['disabled'])) {
3121
			$mobile_configured = true;
3122
		}
3123
		$tunnels[$p1['ikeid']] = $p1;
3124
		$tunnels[$p1['ikeid']]['p2'] = array();
3125
	}
3126
	foreach ($a_phase2 as $p2) {
3127
		$tunnels[$p2['ikeid']]['p2'][] = $p2;
3128
	}
3129

    
3130
	$ipsec_vti_cleanup_ifs = array();
3131
	$rgmap = array();
3132
	$filterdns_list = array();
3133
	$aggressive_mode_psk = false;
3134
	$mobile_ipsec_auth = "";
3135
	$ifacesuse = array();
3136
	$natfilterrules = false;
3137

    
3138
	/* Configure asynchronous crypto. See https://redmine.pfsense.org/issues/8772 */
3139
	set_sysctl(array('net.inet.ipsec.async_crypto' => (int) (isset($ipseccfg['async_crypto']) && ($ipseccfg['async_crypto'] == "enabled"))));
3140

    
3141
	/* Build a list of all IPsec interfaces configured on the firewall at the OS level */
3142
	$gateways_status = return_gateways_status(true);
3143
	foreach (get_interface_arr() as $thisif) {
3144
		if (substr($thisif, 0, 5) == "ipsec") {
3145
			$ipsec_vti_cleanup_ifs[] = $thisif;
3146
		}
3147
	}
3148
	/* Create directory structure for IPsec */
3149
	ipsec_create_dirs();
3150
	/* Setup gateways and interfaces */
3151
	ipsec_setup_gwifs($gateways_status);
3152
	/* Setup and write strongswan.conf */
3153
	ipsec_setup_strongswan();
3154
	/* Start Global Connection default values */
3155
	$conn_defaults = array();
3156
	/* Fragmentation is on for everyone */
3157
	$conn_defaults['fragmentation'] = "yes";
3158
	/* Default to 'replace' for unique IDs (was 'yes' in ipsec.conf previously) */
3159
	$conn_defaults['unique'] = 'replace';
3160
	/* If the configuration has a valid alternate value for unique ID handling,
3161
	 * use it instead. */
3162
	if (!empty($config['ipsec']['uniqueids']) &&
3163
	    array_key_exists($config['ipsec']['uniqueids'], $ipsec_idhandling)) {
3164
		$conn_defaults['unique'] = $config['ipsec']['uniqueids'];
3165
	}
3166
	/* Disable ipcomp for now. redmine #6167
3167
	if (isset($config['ipsec']['compression'])) {
3168
		$conn_defaults['compress'] = "yes";
3169
	}
3170
	set_single_sysctl('net.inet.ipcomp.ipcomp_enable', (isset($config['ipsec']['compression'])) ? 1 : 0);
3171
	*/
3172
	/* End Global Connection Defaults */
3173

    
3174
	/* Start swanctl configuration (scconf) */
3175
	$scconf = array();
3176
	$scconf[] = "# This file is automatically generated. Do not edit";
3177
	$scconf['connections'] = array();
3178
	/* Setup IPsec bypass */
3179
	ipsec_setup_bypass($gateways_status);
3180
	/* Setup connections */
3181
	ipsec_setup_tunnels($gateways_status);
3182
	$scconf['pools'] = array();
3183
	if ($mobile_configured) {
3184
		/* Setup mobile address pools */
3185
		ipsec_setup_pools();
3186
		/* Setup per-user pools */
3187
		ipsec_setup_userpools();
3188
	}
3189
	/* Setup secret data */
3190
	$scconf['secrets'] = array();
3191
	ipsec_setup_secrets($gateways_status);
3192

    
3193
	@file_put_contents("{$g['varetc_path']}/ipsec/swanctl.conf", ipsec_strongswan_confgen($scconf));
3194

    
3195
	/* Clean up unused VTI interfaces */
3196
	foreach ($ipsec_vti_cleanup_ifs as $cleanif) {
3197
		if (does_interface_exist($cleanif)) {
3198
			mwexec("/sbin/ifconfig " . escapeshellarg($cleanif) . " destroy", false);
3199
		}
3200
	}
3201

    
3202
	/* set default MTU to 1400 for GRE over IPsec, othewise to 1476 */
3203
	ipsec_gre_default_mtu(); 
3204

    
3205
	/* Manage process */
3206
	if ($restart === true) {
3207
		mwexec_bg("/usr/local/sbin/strongswanrc restart", false);
3208
	} else {
3209
		if (isvalidpid("{$g['varrun_path']}/charon.pid")) {
3210
			mwexec_bg("/usr/local/sbin/strongswanrc reload", false);
3211
		} else {
3212
			mwexec_bg("/usr/local/sbin/strongswanrc start", false);
3213
		}
3214
	}
3215

    
3216
	// Run ping_hosts.sh once if it's enabled to avoid wait for minicron
3217
	if ($ipsecpinghostsactive) {
3218
		mwexec_bg("/usr/local/bin/ping_hosts.sh");
3219
	}
3220

    
3221
	if ($natfilterrules == true) {
3222
		filter_configure();
3223
	}
3224
	/* start filterdns, if necessary */
3225
	if (count($filterdns_list) > 0) {
3226
		$interval = 60;
3227
		if (!empty($ipseccfg['dns-interval']) && is_numeric($ipseccfg['dns-interval'])) {
3228
			$interval = $ipseccfg['dns-interval'];
3229
		}
3230

    
3231
		$hostnames = "";
3232
		array_unique($filterdns_list);
3233
		foreach ($filterdns_list as $hostname) {
3234
			$hostnames .= "cmd {$hostname} '/usr/local/sbin/pfSctl -c \"service reload ipsecdns\"'\n";
3235
		}
3236
		file_put_contents("{$g['varetc_path']}/ipsec/filterdns-ipsec.hosts", $hostnames);
3237
		unset($hostnames);
3238

    
3239
		if (isvalidpid("{$g['varrun_path']}/filterdns-ipsec.pid")) {
3240
			sigkillbypid("{$g['varrun_path']}/filterdns-ipsec.pid", "HUP");
3241
		} else {
3242
			mwexec_bg("/usr/local/sbin/filterdns -p {$g['varrun_path']}/filterdns-ipsec.pid -i {$interval} -c {$g['varetc_path']}/ipsec/filterdns-ipsec.hosts -d 1");
3243
		}
3244
	} else {
3245
		killbypid("{$g['varrun_path']}/filterdns-ipsec.pid");
3246
		@unlink("{$g['varrun_path']}/filterdns-ipsec.pid");
3247
	}
3248

    
3249
	if (platform_booting()) {
3250
		echo "done\n";
3251
	}
3252

    
3253
	unlock($ipsecstartlock);
3254
	return count($filterdns_list);
3255
}
3256

    
3257
function ipsec_gre_default_mtu() {
3258
	global $config;
3259

    
3260
	foreach ($config['interfaces'] as $if => $ifdetail) { 
3261
		if (interface_is_type($ifdetail['if'], 'gre') && !isset($ifdetail['mtu'])) {
3262
			if (is_greipsec($ifdetail['if'])) {
3263
				set_interface_mtu($ifdetail['if'], 1400);
3264
			} else {
3265
				set_interface_mtu($ifdetail['if'], 1476);
3266
			}
3267
		}
3268
	}
3269
}
3270

    
3271
/* Return the larger of derived SA rekey time and reauth time */
3272
function ipsec_get_renewmax($entry) {
3273
	if (empty($entry) || !is_array($entry)) {
3274
		return 0;
3275
	}
3276
	return max(ipsec_get_rekey_time($entry), ipsec_get_reauth_time($entry));
3277
}
3278

    
3279
/* Determine the life time of an SA entry (Hard upper total time limit for SA before it is removed) */
3280
function ipsec_get_life_time($entry) {
3281
	if (empty($entry) || !is_array($entry)) {
3282
		return 0;
3283
	}
3284
	/* Use a hardcoded value if present in the configuration */
3285
	if ($entry['lifetime'] > 0) {
3286
		return $entry['lifetime'];
3287
	}
3288
	/* If rekey or reauth are enabled, attempt to derive a lifetime from one of those */
3289
	$renewmax = ipsec_get_renewmax($entry);
3290
	if ($renewmax > 0) {
3291
		return intval($renewmax / 0.9);
3292
	}
3293
	/* To reach here, rekey_time and lifetime are both 0 which is invalid
3294
	 * Default to 16000 for p1 and 4000 for p2 */
3295
	if ($entry['iketype']) {
3296
		return 16000;
3297
	} else {
3298
		return 4000;
3299
	}
3300
}
3301

    
3302
/* Determine the rekey time of an SA entry (Time at which to rekey IKEv2 or Child SA entries) */
3303
function ipsec_get_rekey_time($entry) {
3304
	if (empty($entry) || !is_array($entry)) {
3305
		return 0;
3306
	}
3307
	/* Use a hardcoded value if present in the configuration */
3308
	if (strlen($entry['rekey_time'])) {
3309
		/* Check via strlen since 0 is a valid value */
3310
		return $entry['rekey_time'];
3311
	} elseif ($entry['lifetime'] > 0) {
3312
		/* If rekey_time is empty and lifetime is non-zero, use 90% lifetime */
3313
		return intval($entry['lifetime'] * 0.9);
3314
	}
3315
	/* To reach here, rekey_time and lifetime are empty
3316
	 * Default to 14400 for p1 and 3600 for p2 */
3317
	if ($entry['iketype']) {
3318
		return 14400;
3319
	} else {
3320
		return 3600;
3321
	}
3322
}
3323

    
3324
/* Determine the reauth time of an SA entry (IKE SA tear-down/reauthenticate) */
3325
function ipsec_get_reauth_time($entry) {
3326
	if (empty($entry) || !is_array($entry)) {
3327
		return 0;
3328
	}
3329
	/* Use a hardcoded value if present in the configuration */
3330
	if (strlen($entry['reauth_time'])) {
3331
		/* Check via strlen since 0 is a valid value */
3332
		return $entry['reauth_time'];
3333
	} elseif ($entry['lifetime'] > 0) {
3334
		/* If reauth_time is empty and lifetime is non-zero,
3335
		 * use 90% lifetime for IKEv1, disable for IKEv2/auto */
3336
		if ($entry['iketype'] == 'ikev1') {
3337
			return intval($entry['lifetime'] * 0.9);
3338
		} else {
3339
			return 0;
3340
		}
3341
	}
3342
	/* To reach here, rekey_time and lifetime are empty
3343
	 * Default to disabled (0) */
3344
	return 0;
3345
}
3346

    
3347
/* Determine the over time of an SA entry (Hard upper IKE SA time limit, relative to rekey/reauth time) */
3348
function ipsec_get_over_time($entry) {
3349
	if (empty($entry) || !is_array($entry)) {
3350
		return 0;
3351
	}
3352
	/* Automatically derive the value for rand_time */
3353
	$lifetime = ipsec_get_life_time($entry);
3354
	$renewmax = ipsec_get_renewmax($entry);
3355
	if (($lifetime > 0) && ($renewmax > 0)) {
3356
		/* If life time and rekey/reauth time both have values, subtract to get rand time */
3357
		return $lifetime - $renewmax;
3358
	} elseif ($lifetime > 0) {
3359
		/* If only life time has a value, use 10% of that */
3360
		return intval($lifetime * 0.1);
3361
	} elseif ($renewmax > 0) {
3362
		/* If only rekey/reauth time has a value, use 10% of that */
3363
		return intval($renewmax * 0.1);
3364
	}
3365
	/* No value can be determined, default to 0 */
3366
	return 0;
3367
}
3368

    
3369
/* Determine the rand time of an SA entry (random value subtracted from renewal time to prevent collisions) */
3370
function ipsec_get_rand_time($entry) {
3371
	if (empty($entry) || !is_array($entry)) {
3372
		return 0;
3373
	}
3374
	/* Use a hardcoded value if present in the configuration */
3375
	if (strlen($entry['rand_time'])) {
3376
		/* Check via strlen since 0 is a valid value */
3377
		return $entry['rand_time'];
3378
	}
3379
	/* Logic to automatically determine rand time is identical to calculating over time */
3380
	return ipsec_get_over_time($entry);
3381
}
3382

    
3383
?>
(26-26/61)