Project

General

Profile

Download (110 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-2024 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
	'chacha20poly1305' => array('name' => 'CHACHA20-POLY1305')
139
);
140

    
141
global $p2_ealgos;
142
$p2_ealgos = array(
143
	'aes' => array('name' => 'AES', 'keysel' => array('lo' => 128, 'hi' => 256, 'step' => 64)),
144
	'aes128gcm' => array('name' => 'AES128-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)),
145
	'aes192gcm' => array('name' => 'AES192-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)),
146
	'aes256gcm' => array('name' => 'AES256-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)),
147
	'chacha20poly1305' => array('name' => 'CHACHA20-POLY1305')
148
);
149

    
150
global $p1_halgos;
151
$p1_halgos = array(
152
	'sha1' => 'SHA1',
153
	'sha256' => 'SHA256',
154
	'sha384' => 'SHA384',
155
	'sha512' => 'SHA512',
156
	'aesxcbc' => 'AES-XCBC'
157
);
158

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

    
185
global $p2_halgos;
186
$p2_halgos = array(
187
	'hmac_sha1' => 'SHA1',
188
	'hmac_sha256' => 'SHA256',
189
	'hmac_sha384' => 'SHA384',
190
	'hmac_sha512' => 'SHA512',
191
	'aesxcbc' => 'AES-XCBC'
192
);
193

    
194
global $p1_authentication_methods;
195
$p1_authentication_methods = array(
196
	'hybrid_cert_server' => array('name' => gettext('Hybrid Certificate + Xauth'), 'mobile' => true),
197
	'xauth_cert_server' => array('name' => gettext('Mutual Certificate + Xauth'), 'mobile' => true),
198
	'xauth_psk_server' => array('name' => gettext('Mutual PSK + Xauth'), 'mobile' => true),
199
	'eap-tls' => array('name' => gettext('EAP-TLS'), 'mobile' => true),
200
	'eap-radius' => array('name' => gettext('EAP-RADIUS'), 'mobile' => true),
201
	'eap-mschapv2' => array('name' => gettext('EAP-MSChapv2'), 'mobile' => true),
202
	'cert' => array('name' => gettext('Mutual Certificate'), 'mobile' => false),
203
	'pkcs11' => array('name' => gettext('Mutual Certificate (PKCS#11)'), 'mobile' => false),
204
	'pre_shared_key' => array('name' => gettext('Mutual PSK'), 'mobile' => false)
205
);
206

    
207
global $ipsec_preshared_key_type;
208
$ipsec_preshared_key_type = array(
209
	'PSK' => 'PSK',
210
	'EAP' => 'EAP'
211
);
212

    
213
global $ipsec_startactions;
214
$ipsec_startactions = array(
215
	'' => gettext('Default'),
216
	'none' => gettext('None (Responder Only)'),
217
	'start' => gettext('Initiate at start (VTI or Tunnel Mode)'),
218
	'trap' => gettext('Initiate on demand (Tunnel mode only)'),
219
);
220

    
221
global $ipsec_closeactions;
222
$ipsec_closeactions = array(
223
	'' => gettext('Default'),
224
	'none' => gettext('Close connection and clear SA'),
225
	'start' => gettext('Restart/Reconnect'),
226
	'trap' => gettext('Close connection and reconnect on demand'),
227
);
228

    
229
global $p2_modes;
230
$p2_modes = array(
231
	'tunnel' => gettext('Tunnel IPv4'),
232
	'tunnel6' => gettext('Tunnel IPv6'),
233
	'transport' => gettext('Transport'),
234
	'vti' => gettext('Routed (VTI)')
235
);
236

    
237
global $p2_protos;
238
$p2_protos = array(
239
	'esp' => 'ESP',
240
	'ah' => 'AH'
241
);
242

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

    
270
function ipsec_enabled() {
271
	if (!is_array(config_get_path('ipsec'))) {
272
		return false;
273
	}
274

    
275
	/* Check if at least one phase 1 entry is enabled. */
276
	foreach (config_get_path('ipsec/phase1', []) as $phase1) {
277
		if (!isset($phase1['disabled'])) {
278
			return true;
279
		}
280
	}
281

    
282
	return false;
283
}
284

    
285
/*
286
 * ikeid management functions
287
 */
288

    
289
function ipsec_ikeid_used($ikeid) {
290
	init_config_arr(array('ipsec', 'phase1'));
291
	foreach (config_get_path('ipsec/phase1', []) as $ph1ent) {
292
		if ($ikeid == $ph1ent['ikeid']) {
293
			return true;
294
		}
295
	}
296
	return false;
297
}
298

    
299
function ipsec_ikeid_next() {
300
	$ikeid = 1;
301
	init_config_arr(array('ipsec', 'phase1'));
302
	$ipsecikeid = lock('ipsecikeids', LOCK_EX);
303
	$ikeids = array();
304

    
305
	foreach (config_get_path('ipsec/phase1', []) as $ph1) {
306
		$ikeids[$ph1['ikeid']] = $ph1['ikeid'];
307
	}
308

    
309
	for ($i = 1; $i < 16000; $i++) {
310
		if (!isset($ikeids[$i])) {
311
			$ikeid = $i;
312
			break;
313
		}
314
	}
315

    
316
	unlock($ipsecikeid);
317
	return $ikeid;
318
}
319

    
320
function ipsec_new_reqid() {
321
	init_config_arr(array('ipsec', 'phase2'));
322
	$ipsecreqid = lock('ipsecreqids', LOCK_EX);
323
	$keyids = array();
324
	$keyid = 1;
325

    
326
	foreach (config_get_path('ipsec/phase2', []) as $ph2) {
327
		$keyids[$ph2['reqid']] = $ph2['reqid'];
328
	}
329

    
330
	for ($i = 1; $i < 16000; $i++) {
331
		if (!isset($keyids[$i])) {
332
			$keyid = $i;
333
			break;
334
		}
335
	}
336

    
337
	unlock($ipsecreqid);
338
	return $keyid;
339
}
340

    
341
function ipsec_get_phase1($ikeid) {
342
	init_config_arr(array('ipsec', 'phase1'));
343

    
344
	foreach (config_get_path('ipsec/phase1', []) as $p1) {
345
		if ($p1['ikeid'] == $ikeid) {
346
			return $p1;
347
		}
348
	}
349
}
350

    
351
function ipsec_get_p1_descr($ikeid) {
352
	init_config_arr(array('ipsec', 'phase1'));
353
	$p1 = ipsec_get_phase1($ikeid);
354
	return (!empty($p1['descr'])) ? $p1['descr'] : '';
355
}
356

    
357
/*
358
 * Return copy of phase1 for a given phase2
359
 */
360
function ipsec_lookup_phase1($p2, &$p1_disabled) {
361
	init_config_arr(array('ipsec', 'phase1'));
362

    
363
	foreach (config_get_path('ipsec/phase1', []) as $p1) {
364
		if ($p1['ikeid'] == $p2['ikeid']) {
365
			$p1_disabled = $p1['disabled'];
366
			return true;
367
		}
368
	}
369

    
370
	return false;
371
}
372

    
373
function ipsec_get_phase2($reqid) {
374
	init_config_arr(array('ipsec', 'phase2'));
375

    
376
	foreach (config_get_path('ipsec/phase2', []) as $p2) {
377
		if ($p2['reqid'] == $reqid) {
378
			return $p2;
379
		}
380
	}
381
}
382

    
383
function ipsec_get_phase2_by_ikeid($ikeid) {
384
	init_config_arr(array('ipsec', 'phase2'));
385
	$results = array();
386
	foreach (config_get_path('ipsec/phase2', []) as $p2) {
387
		if ($p2['ikeid'] == $ikeid) {
388
			$results[] = $p2;
389
		}
390
	}
391
	return $results;
392
}
393

    
394
function ipsec_get_number_of_phase2($ikeid) {
395
	return count(ipsec_get_phase2_by_ikeid($ikeid));
396
}
397

    
398
/* Return a nested hash with P1 indexed by ikeid and P2 indexed by reqid */
399
function ipsec_map_config_by_id() {
400
	init_config_arr(array('ipsec', 'phase1'));
401
	init_config_arr(array('ipsec', 'phase2'));
402
	$map = array();
403

    
404
	foreach (config_get_path('ipsec/phase1', []) as $p1) {
405
		$map[$p1['ikeid']] = array();
406
		$map[$p1['ikeid']]['p1'] = $p1;
407
	}
408
	foreach (config_get_path('ipsec/phase2', []) as $p2) {
409
		if (!is_array($map[$p2['ikeid']]['p2'])) {
410
			$map[$p2['ikeid']]['p2'] = array();
411
		}
412
		$map[$p2['ikeid']]['p2'][$p2['reqid']] = $p2;
413
	}
414

    
415
	return $map;
416
}
417

    
418
function ipsec_conid($p1 = null, $p2 = null) {
419
	global $ipsec_conid_prefix, $ipsec_conid_separator;
420

    
421
	$conid = NULL;
422
	$have_p1 = (!empty($p1) && is_array($p1));
423
	$have_p2 = (!empty($p2) && is_array($p2));
424

    
425
	if (!$have_p1 && $have_p2) {
426
		$p1 = ipsec_get_phase1($p2['ikeid']);
427
	}
428

    
429
	if (isset($p1['mobile'])) {
430
		return "{$ipsec_conid_prefix}-mobile";
431
	}
432

    
433
	$conid = "{$ipsec_conid_prefix}{$p1['ikeid']}";
434

    
435
	if ($have_p2) {
436
		/* IKEv1 or IKEv2+Split Connections use separate P2 entries
437
		 * so add reqid to ID */
438
		if (($p1['iketype'] != 'ikev2') || isset($p1['splitconn'])) {
439
			$conid .= "{$ipsec_conid_separator}{$p2['reqid']}";
440
		}
441
	}
442

    
443
	return $conid;
444
}
445

    
446
function ipsec_id_by_conid($conid) {
447
	global $ipsec_conid_prefix, $ipsec_conid_separator;
448

    
449
	if ($conid == "{$ipsec_conid_prefix}-mobile") {
450
		init_config_arr(array('ipsec', 'phase1'));
451
		foreach (config_get_path('ipsec/phase1', []) as $p1) {
452
			if (isset($p1['mobile'])) {
453
				$conid = $ipsec_conid_prefix . $p1['ikeid'];
454
			}
455
		}
456
	}
457

    
458
	if (substr($conid, 0, strlen($ipsec_conid_prefix)) == $ipsec_conid_prefix) {
459
		$conid = substr($conid, strlen($ipsec_conid_prefix));
460
	}
461
	$parts = explode($ipsec_conid_separator, $conid);
462
	if (count($parts) == 1) {
463
		$parts[1] = NULL;
464
	}
465
	return array($parts[0], $parts[1]);
466
}
467

    
468
/*
469
 * Return phase1 local address
470
 */
471
function ipsec_get_phase1_src(& $ph1ent) {
472
	global $interface_ip_arr_cache;
473
	global $interface_sn_arr_cache;
474
	if ($ph1ent['interface']) {
475
		/* get_interface_ip() already calls get_failover_interface so no need for special handling */
476
		$if = $ph1ent['interface'];
477
	} else {
478
		$if = "wan";
479
	}
480

    
481
	$ifips = array();
482
	if (in_array($ph1ent['protocol'], array('inet', 'both'))) {
483
		$ip4 = get_interface_ip($if);
484
		if (!empty($ip4)) {
485
			$ifips[] = $ip4;
486
		}
487
	}
488

    
489
	if (in_array($ph1ent['protocol'], array('inet6', 'both'))) {
490
		/* get correct interface name for 6RD/6to4 interfaces
491
		 * see https://redmine.pfsense.org/issues/11643 */
492
		if (is_stf_interface($ph1ent['interface'])) {
493
			$ip6 = get_interface_ipv6($ph1ent['interface'], false, false);
494
		} else {
495
			$ip6 = get_interface_ipv6($if, false, false);
496
		}
497
		if (!empty($ip6)) {
498
			$ifips[] = $ip6;
499
		}
500
	}
501

    
502
	return implode(',', $ifips);
503
}
504

    
505
/*
506
 * Return phase1 local address
507
 */
508
function ipsec_get_phase1_dst(& $ph1ent) {
509
	global $g;
510

    
511
	if (empty($ph1ent['remote-gateway'])) {
512
		return false;
513
	}
514
	$rg = $ph1ent['remote-gateway'];
515
	if (!is_ipaddr($rg)) {
516
		if (!is_platform_booting()) {
517
			$result = resolve_retry($rg, $ph1ent['protocol']);
518
			if (!empty($result)) {
519
				return $result;
520
			} else {
521
				return false;
522
			}
523
		}
524
	}
525
	if (!is_ipaddr($rg)) {
526
		return false;
527
	}
528

    
529
	return $rg;
530
}
531

    
532
/*
533
 * Return phase2 idinfo in cidr format
534
 */
535
function ipsec_idinfo_to_cidr(& $idinfo, $addrbits = false, $mode = "") {
536
	switch ($idinfo['type']) {
537
		case "address":
538
			if ($addrbits) {
539
				if ($mode == "tunnel6") {
540
					return $idinfo['address']."/128";
541
				} elseif (($mode == "vti") && is_ipaddrv4($idinfo['address'])) {
542
					return $idinfo['address']."/30";
543
				} elseif (($mode == "vti") && is_ipaddrv6($idinfo['address'])) {
544
					return $idinfo['address']."/64";
545
				} else {
546
					return $idinfo['address']."/32";
547
				}
548
			} else {
549
				return $idinfo['address'];
550
			}
551
			break; /* NOTREACHED */
552
		case "network":
553
			return "{$idinfo['address']}/{$idinfo['netbits']}";
554
			break; /* NOTREACHED */
555
		case "none":
556
		case "mobile":
557
			return '0.0.0.0/0';
558
			break; /* NOTREACHED */
559
		default:
560
			if (empty($mode) && !empty($idinfo['mode'])) {
561
				$mode = $idinfo['mode'];
562
			}
563

    
564
			if ($mode == "tunnel6") {
565
				$address = get_interface_ipv6($idinfo['type']);
566
				$netbits = get_interface_subnetv6($idinfo['type']);
567
				$address = gen_subnetv6($address, $netbits);
568
				return "{$address}/{$netbits}";
569
			} else {
570
				$address = get_interface_ip($idinfo['type']);
571
				$netbits = get_interface_subnet($idinfo['type']);
572
				$address = gen_subnet($address, $netbits);
573
				return "{$address}/{$netbits}";
574
			}
575
			break; /* NOTREACHED */
576
	}
577
}
578

    
579
/*
580
 * Return phase2 idinfo in address/netmask format
581
 */
582
function ipsec_idinfo_to_subnet(& $idinfo, $addrbits = false) {
583
	switch ($idinfo['type']) {
584
		case "address":
585
			if ($addrbits) {
586
				if ($idinfo['mode'] == "tunnel6") {
587
					return $idinfo['address']."/128";
588
				} else {
589
					return $idinfo['address']."/255.255.255.255";
590
				}
591
			} else {
592
				return $idinfo['address'];
593
			}
594
			break; /* NOTREACHED */
595
		case "none":
596
		case "network":
597
			return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']);
598
			break; /* NOTREACHED */
599
		case "mobile":
600
			return "0.0.0.0/0";
601
			break; /* NOTREACHED */
602
		default:
603
			if ($idinfo['mode'] == "tunnel6") {
604
				$address = get_interface_ipv6($idinfo['type']);
605
				$netbits = get_interface_subnetv6($idinfo['type']);
606
				$address = gen_subnetv6($address, $netbits);
607
				return $address."/".$netbits;
608
			} else {
609
				$address = get_interface_ip($idinfo['type']);
610
				$netbits = get_interface_subnet($idinfo['type']);
611
				$address = gen_subnet($address, $netbits);
612
				return $address."/".$netbits;
613
			}
614
			break; /* NOTREACHED */
615
	}
616
}
617

    
618
/*
619
 *  Return phase2 idinfo in text format
620
 */
621
function ipsec_idinfo_to_text(& $idinfo) {
622
	switch ($idinfo['type']) {
623
		case "address":
624
			return $idinfo['address'];
625
			break; /* NOTREACHED */
626
		case "network":
627
			return $idinfo['address']."/".$idinfo['netbits'];
628
			break; /* NOTREACHED */
629
		case "mobile":
630
			return gettext("Mobile Client");
631
			break; /* NOTREACHED */
632
		case "none":
633
			return gettext("None");
634
			break; /* NOTREACHED */
635
		default:
636
			$specialnet = get_specialnet('', [SPECIALNET_IFSUB]);
637
			if (array_key_exists($idinfo['type'], $specialnet)) {
638
				return $specialnet[$idinfo['type']];
639
			} else {
640
				return strtoupper($idinfo['type']);
641
			}
642
			break; /* NOTREACHED */
643
	}
644
}
645

    
646
/*
647
 * Wrapper to call pfSense_ipsec_list_sa() when IPsec is enabled
648
 */
649
function ipsec_list_sa() {
650
	if (ipsec_enabled()) {
651
		return pfSense_ipsec_list_sa();
652
	}
653
	return array();
654
}
655

    
656
function ipsec_status() {
657
	$cmap = ipsec_map_config_by_id();
658
	$status = ipsec_list_sa();
659

    
660
	/*
661
	Start:
662
	$cmap[$ikeid]['p1'] => Phase 1 config
663
	$cmap[$ikeid]['p2'] => Array of P2 configs indexed by reqid
664

    
665
	End:
666
	$cmap[$ikeid]['p1']['status'] => Array of IKE SA entries and child SAs for this P1
667
	$cmap[$ikeid]['p1']['connected'] => true/false if the P1 is connected
668
	$cmap[$ikeid]['p2'][$reqid]['connected'] => true/false if the P2 is connected
669
		If non-split IKEv2 then mark all as 'true'
670
	*/
671

    
672
	if (!is_array($status)) {
673
		$status = array();
674
	}
675

    
676
	$cmap['connected'] = array(
677
		/* List of connected P1 conid entries (excluding mobile) */
678
		'p1' => array(),
679
		/* List of connected P2 conid entries (excluding mobile) */
680
		'p2' => array(),
681
	);
682
	$cmap['disconnected'] = array(
683
		/* List of disconnected P1 conid entries (excluding mobile) */
684
		'p1' => array(),
685
		/* List of disconnected P2 conid entries (excluding mobile) */
686
		'p2' => array(),
687
	);
688

    
689
	/* First level of array has IKE SA content */
690
	foreach ($status as $ikesa) {
691
		/* If the con-id isn't there it must not be a proper entry */
692
		if (!isset($ikesa['con-id'])) {
693
			continue;
694
		}
695

    
696
		/* Mark this IKE ID as connected and drop the status info underneath the P1 config */
697
		list($ikeid, $reqid) = ipsec_id_by_conid($ikesa['con-id']);
698
		if (!array_key_exists($ikeid, $cmap)) {
699
			// Doesn't match known tunnel
700
			$cmap['connected']['p1'][$ikesa['con-id']] = $ikesa['con-id'];
701
		} else {
702
			/* Track this IKE ID in the connection list */
703
			$cmap['connected']['p1'][$ikeid] = $ikesa['con-id'];
704
			/* Copy the status data. Use an array since it could be connected multiple times. */
705
			if (!is_array($cmap[$ikeid]['status'])) {
706
				$cmap[$ikeid]['status'] = array();
707
			}
708
			$cmap[$ikeid]['status'][] = $ikesa;
709
			$cmap[$ikeid]['p1']['connected'] = true;
710
		}
711

    
712
		/* If there are child SAs under this IKE entry, process them */
713
		if (array_key_exists('child-sas', $ikesa) &&
714
		    is_array($ikesa['child-sas'])) {
715
			foreach ($ikesa['child-sas'] as $childid => $childsa) {
716
				/* Mark this reqid as connected */
717
				list($childikeid, $childreqid) = ipsec_id_by_conid($childsa['name']);
718

    
719
				if ($childreqid != null) {
720
					if (!isset($cmap[$childikeid]['p2'][$childreqid]['mobile'])) {
721
						$cmap['connected']['p2'][$childreqid] = $childsa['name'];
722
						$cmap[$childikeid]['p2'][$childreqid]['connected'] = true;
723
					}
724
				} else {
725
					/* If this is IKEv2 w/o Split, mark all reqids for the P1 as connected */
726
					if ($cmap[$childikeid]['p1']['iketype'] == 'ikev2' &&
727
					    !isset($cmap[$childikeid]['p1']['splitconn'])) {
728
						foreach ($cmap[$ikeid]['p2'] as $p2) {
729
							if (isset($p2['mobile'])) {
730
								continue;
731
							}
732
							$cmap['connected']['p2'][$p2['reqid']] = $childsa['name'];
733
							$cmap[$childikeid]['p2'][$p2['reqid']]['connected'] = true;
734
						}
735
					}
736
				}
737
			}
738
		}
739
	}
740
	if (is_array($cmap)) {
741
		foreach ($cmap as $k => $tunnel) {
742
			if (in_array($k, array('connected', 'disconnected')) ||
743
			    (!array_key_exists('p1', $tunnel) ||
744
			    isset($tunnel['p1']['disabled'])) ||
745
			    isset($tunnel['p1']['mobile'])) {
746
				continue;
747
			}
748
			if (!array_key_exists($tunnel['p1']['ikeid'], $cmap['connected']['p1'])) {
749
				$cmap['disconnected']['p1'][$tunnel['p1']['ikeid']] = ipsec_conid($tunnel['p1']);
750
			}
751
			if (is_array($tunnel['p2'])) {
752
				foreach ($tunnel['p2'] as $p2) {
753
					if (isset($p2['mobile'])) {
754
						continue;
755
					}
756
					if (!array_key_exists($p2['reqid'], $cmap['connected']['p2'])) {
757
						/* This P2 is not connected */
758
						$cmap['disconnected']['p2'][$p2['reqid']] = ipsec_conid($tunnel['p1'], $p2);
759
					}
760
				}
761
			}
762
		}
763
	}
764
	return $cmap;
765
}
766

    
767
function ipsec_status_button($req, $act, $type, $conid, $uniqueid, $full) {
768
	$btn = '<a href="';
769
	if ($act == 'disconnect') {
770
		$buttonclass = ($type == 'ike') ? 'danger' : 'warning';
771
		$title = gettext("Disconnect");
772
		$icon = 'fa-solid fa-trash-can';
773
	} else {
774
		$buttonclass = 'success';
775
		$title = gettext("Connect");
776
		$icon = 'fa-solid fa-right-to-bracket';
777
	}
778
	if ($full) {
779
		$linkclass = 'btn btn-xs btn-' . htmlspecialchars($buttonclass);
780
	} else {
781
		$linkclass = '';
782
	}
783

    
784
	$title .= " ";
785
	switch ($type) {
786
		case 'all':
787
			$title .= gettext("P1 and P2s");
788
			break;
789
		case 'child':
790
			$title .= gettext("P2");
791
			break;
792
		case 'ike':
793
		default:
794
			$title .= gettext("P1");
795
			break;
796
	}
797

    
798
	if ($req == 'post') {
799
		$btn .= 'status_ipsec.php?act=' . htmlspecialchars($act) .
800
			'&amp;type=' . htmlspecialchars($type) .
801
			'&amp;conid=' . htmlspecialchars($conid);
802
		if (!empty($uniqueid)) {
803
			$btn .= '&amp;uniqueid=' . htmlspecialchars($uniqueid);
804
		}
805
	} else {
806
		$btn .= '#" id="' . 'ipsecstatus-' .
807
			htmlspecialchars($act) . '-' .
808
			htmlspecialchars($type) . '-' .
809
			htmlspecialchars($conid);
810
		if (!empty($uniqueid)) {
811
			$btn .= '-' . htmlspecialchars($uniqueid);
812
		}
813
	}
814

    
815
	$btn .= '"';
816
	if ($full) {
817
		$btn .= ' class="' . htmlspecialchars($linkclass) . '"';
818
	}
819
	$btn .= ' title="' . htmlspecialchars($title) . '"';
820

    
821
	if ($req == 'post') {
822
		$btn .= ' usepost';
823
	}
824
	$btn .= '>';
825
	$btn .= '<i class="' . htmlspecialchars($icon);
826
	if ($req == 'post') {
827
		$btn .= ' icon-embed-btn';
828
	}
829
	$btn .= '"></i>';
830

    
831
	if ($full) {
832
		$btn .= ' ' . htmlspecialchars($title);
833
	}
834
	$btn .= '</a>';
835
	return $btn;
836
}
837

    
838
function ipsec_initiate($type = 'ike', $p1 = null, $p2 = null) {
839
	return ipsec_initiate_by_conid($type, ipsec_conid($p1, $p2));
840
}
841

    
842
function ipsec_initiate_by_conid($type, $conid) {
843
	if (empty($conid)) {
844
		return false;
845
	}
846
	switch ($type) {
847
		case 'all':
848
			/* Find relevant P2s and initiate all of them */
849
			$cmap = ipsec_map_config_by_id();
850
			list($ikeid, $reqid) = ipsec_id_by_conid($conid);
851
			if (array_key_exists($ikeid, $cmap)) {
852
				if ($cmap[$ikeid]['p1']['iketype'] == 'ikev2' && !isset($cmap[$ikeid]['p1']['splitconn'])) {
853
					/* IKEv2 without split only has one P2, named the same as the P1 */
854
					ipsec_initiate_by_conid('child', ipsec_conid($cmap[$ikeid]['p1']));
855
				} else {
856
					foreach ($cmap[$ikeid]['p2'] as $p2) {
857
						ipsec_initiate_by_conid('child', ipsec_conid($cmap[$ikeid]['p1'], $p2));
858
					}
859
				}
860
			}
861
			break;
862
		case 'ike':
863
		case 'child':
864
			mwexec_bg("/usr/local/sbin/swanctl --initiate --timeout 5 " . escapeshellarg('--' . $type) . " " . escapeshellarg($conid));
865
			break;
866
		default:
867
			/* Do nothing. Invalid type. */
868
			break;
869
	}
870
	return true;
871
}
872

    
873
function ipsec_terminate($type = 'ike', $p1 = null, $p2 = null, $uniqueid = null) {
874
	return ipsec_terminate_by_conid($type, ipsec_conid($p1, $p2), $uniqueid);
875
}
876

    
877
function ipsec_terminate_by_conid($type = 'ike', $conid = null, $uniqueid = null) {
878
	/* Must have one or the other */
879
	if (($conid === null) && ($uniqueid === null)) {
880
		return false;
881
	}
882
	$selector = '--';
883
	$selector .= ($type == 'ike') ? 'ike' : 'child';
884

    
885
	if (ctype_digit(strval($uniqueid)) && ($uniqueid > 0)) {
886
		$selector .= '-id';
887
		$term = $uniqueid;
888
	} else {
889
		$term = $conid;
890
	}
891
	mwexec_bg("/usr/local/sbin/swanctl --terminate --force " . escapeshellarg($selector) . " " . escapeshellarg($term));
892
	return true;
893
}
894

    
895
/*
896
 * Return dump of SPD table
897
 */
898
function ipsec_dump_spd() {
899
	$fd = @popen("/sbin/setkey -DP", "r");
900
	$spd = array();
901
	if ($fd) {
902
		while (!feof($fd)) {
903
			$line = chop(fgets($fd));
904
			if (!$line) {
905
				continue;
906
			}
907
			if ($line == "No SPD entries.") {
908
				break;
909
			}
910
			if ($line[0] != "\t") {
911
				if (is_array($cursp)) {
912
					$spd[] = $cursp;
913
				}
914
				$cursp = array();
915
				$linea = explode(" ", $line);
916
				$cursp['srcid'] = substr($linea[0], 0, strpos($linea[0], "["));
917
				$cursp['dstid'] = substr($linea[1], 0, strpos($linea[1], "["));
918
				$i = 0;
919
			} else if (is_array($cursp)) {
920
				$line = trim($line, "\t\r\n ");
921
				$linea = explode(" ", $line);
922
				switch ($i) {
923
					case 1:
924
						if ($linea[1] == "none")	/* don't show default anti-lockout rule */ {
925
							unset($cursp);
926
						} else {
927
							$cursp['dir'] = $linea[0];
928
						}
929
						break;
930
					case 2:
931
						$upperspec = explode("/", $linea[0]);
932
						$cursp['proto'] = $upperspec[0];
933
						list($cursp['src'], $cursp['dst']) = explode("-", $upperspec[2]);
934
						$cursp['unique'] = substr($upperspec[3], strpos($upperspec[3], ":")+1);
935
						break;
936
					default:
937
						if (substr($linea[0], 0, 4) != 'spid') {
938
							break;
939
						}
940
						$cursp['scope'] = substr($linea[3], strpos($linea[3], "=")+1);
941
						if ($cursp['scope'] == 'ifnet') {
942
							$cursp['ifname'] = substr($linea[4], strpos($linea[4], "=")+1);
943
						}
944
						break;
945
				}
946
			}
947
			$i++;
948
		}
949
		if (is_array($cursp) && count($cursp)) {
950
			$spd[] = $cursp;
951
		}
952
		pclose($fd);
953
	}
954

    
955
	return $spd;
956
}
957

    
958
/*
959
 * Return dump of SAD table
960
 */
961
function ipsec_dump_sad() {
962
	$fd = @popen("/sbin/setkey -D", "r");
963
	$sad = array();
964
	if ($fd) {
965
		while (!feof($fd)) {
966
			$line = chop(fgets($fd));
967
			if (!$line || $line[0] == " ") {
968
				continue;
969
			}
970
			if ($line == "No SAD entries.") {
971
				break;
972
			}
973
			if ($line[0] != "\t") {
974
				if (is_array($cursa)) {
975
					$sad[] = $cursa;
976
				}
977
				$cursa = array();
978
				list($cursa['src'], $cursa['dst']) = explode(" ", $line);
979
			} else {
980
				$line = trim($line, "\t\n\r ");
981
				$linea = explode(" ", $line);
982
				foreach ($linea as $idx => $linee) {
983
					if ($linee == 'esp' || $linee == 'ah' || $linee[0] == '#') {
984
						$cursa['proto'] = $linee;
985
					} else if (substr($linee, 0, 3) == 'spi') {
986
						$cursa['spi'] = substr($linee, strpos($linee, 'x') + 1, -1);
987
					} else if (substr($linee, 0, 5) == 'reqid') {
988
						$cursa['reqid'] = substr($linee, strpos($linee, 'x') + 1, -1);
989
					} else if (substr($linee, 0, 2) == 'E:') {
990
						$cursa['ealgo'] = $linea[$idx + 1];
991
						break;
992
					} else if (substr($linee, 0, 2) == 'A:') {
993
						$cursa['aalgo'] = $linea[$idx + 1];
994
						break;
995
					} else if (substr($linee, 0, 8) == 'current:') {
996
						$cursa['data'] = substr($linea[$idx + 1], 0, strpos($linea[$idx + 1], 'bytes') - 1) . ' B';
997
						break;
998
					}
999
				}
1000
			}
1001
		}
1002
		if (is_array($cursa) && count($cursa)) {
1003
			$sad[] = $cursa;
1004
		}
1005
		pclose($fd);
1006
	}
1007

    
1008
	return $sad;
1009
}
1010

    
1011
/*
1012
 * Return dump of mobile user list
1013
 */
1014
function ipsec_dump_mobile() {
1015
	global $g;
1016

    
1017
	if(!config_path_enabled('ipsec/client')) {
1018
		return array();
1019
	}
1020

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

    
1024
	if ($rc != 0) {
1025
		log_error(gettext("Unable to find IPsec daemon leases file. Could not display mobile user stats!"));
1026
		return array();
1027
	}
1028

    
1029
	$response = array(
1030
		'pool' => array(),
1031
	);
1032

    
1033
	/* swanctl --list-pools --leases example output, field names can be confirmed by
1034
	 * looking at --raw or --pretty output. */
1035
	/* mobile-pool          10.7.200.0                          0 / 1 / 254 */
1036
	$pool_regex='/^(?P<name>\S+)\s+(?P<base>\S+)\s+(?P<online>\d+) \/ (?P<offline>\d+) \/ (?P<size>\d+)/';
1037
	/*   10.7.200.1                     online   'jimp' */
1038
	$lease_regex='/\s*(?P<host>[\d\.]+)\s+(?P<status>online|offline)\s+\'(?P<id>.*)\'/';
1039
	foreach ($output as $line) {
1040
		if (preg_match($pool_regex, $line, $matches)) {
1041
			$id++;
1042
			$response['pool'][$id] = array(
1043
			    'name'   => $matches['name'],
1044
			    'base'   => $matches['base'],
1045
			    'online' => $matches['online'],
1046
			    'offline'  => $matches['offline'],
1047
			    'size'   => $matches['size'],
1048
			);
1049
		} elseif (preg_match($lease_regex, $line, $matches)) {
1050
			$response['pool'][$id]['lease'][] = array(
1051
			    'host'   => $matches['host'],
1052
			    'status' => $matches['status'],
1053
			    'id'     => $matches['id']
1054
			);
1055
		}
1056
	}
1057

    
1058
	unset($_gb, $output, $rc, $pool_regex);
1059

    
1060
	return $response;
1061
}
1062

    
1063
function ipsec_mobilekey_sort() {
1064
	function mobilekeycmp($a, $b) {
1065
		return strcmp($a['ident'][0], $b['ident'][0]);
1066
	}
1067

    
1068
	usort($config['ipsec']['mobilekey'], "mobilekeycmp");
1069
}
1070

    
1071
function ipsec_fixup_ip($ipaddr) {
1072
	if (is_ipaddrv6($ipaddr) || is_subnetv6($ipaddr)) {
1073
		return text_to_compressed_ip6($ipaddr);
1074
	} else {
1075
		return $ipaddr;
1076
	}
1077
}
1078

    
1079
function ipsec_find_id(& $ph1ent, $side = "local", $rgmap = array()) {
1080
	if ($side == "local") {
1081
		$id_type = $ph1ent['myid_type'];
1082
		$id_data = $ph1ent['myid_data'];
1083

    
1084
		$addr = ipsec_get_phase1_src($ph1ent);
1085
		if (!$addr) {
1086
			return array();
1087
		}
1088
		/* When automatically guessing, use the first address. */
1089
		$addr = explode(',', $addr);
1090
		$addr = $addr[0];
1091
	} elseif ($side == "peer") {
1092
		$id_type = $ph1ent['peerid_type'];
1093
		$id_data = $ph1ent['peerid_data'];
1094

    
1095
		if (isset($ph1ent['mobile'])) {
1096
			$addr = "%any";
1097
		} else {
1098
			$addr = $ph1ent['remote-gateway'];
1099
		}
1100
	} else {
1101
		return array();
1102
	}
1103

    
1104
	$thisid_type = $id_type;
1105
	switch ($thisid_type) {
1106
		case 'myaddress':
1107
			$thisid_type = 'address';
1108
			$thisid_data = $addr;
1109
			break;
1110
		case 'dyn_dns':
1111
			$thisid_type = 'dns';
1112
			$thisid_data = $id_data;
1113
			break;
1114
		case 'peeraddress':
1115
			$thisid_type = 'address';
1116
			$thisid_data = $rgmap[$ph1ent['remote-gateway']];
1117
			break;
1118
		case 'keyid tag':
1119
			$thisid_type = 'keyid';
1120
			$thisid_data = $id_data;
1121
			break;
1122
		case 'user_fqdn':
1123
			$thisid_type = 'userfqdn';
1124
			$thisid_data = $id_data;
1125
			break;
1126
		case 'any':
1127
			$thisid_data = '%any';
1128
			break;
1129
		case 'address':
1130
		case 'fqdn':
1131
		case 'asn1dn':
1132
		case 'auto':
1133
			$thisid_data = $id_data;
1134
			break;
1135
		default:
1136
			break;
1137
	}
1138
	return array($thisid_type, $thisid_data);
1139
}
1140

    
1141
/*
1142
 * Fixup ID type/data to include prefix when necessary, add quotes, etc.
1143
 */
1144
function ipsec_fixup_id($type, $data) {
1145
	/* List of types to pass through as-is without changes or adding prefix */
1146
	$auto_types = array('address', 'auto');
1147
	/* List of types which need the resulting string double quoted. */
1148
	$quote_types = array('keyid', 'asn1dn', 'auto');
1149

    
1150
	/* If the first character of asn1dn type data is not #, then rely on
1151
	 * automatic parsing https://redmine.pfsense.org/issues/4792 */
1152
	if (($type == 'asn1dn') && !empty($data) && ($data[0] != '#')) {
1153
		$auto_types[] = 'asn1dn';
1154
	}
1155

    
1156
	if ($type == 'any') {
1157
		$idstring = "%any";
1158
	} elseif (!in_array($type, $auto_types)) {
1159
		$idstring = "{$type}:{$data}";
1160
	} else {
1161
		$idstring = $data;
1162
	}
1163

    
1164
	if (in_array($type, $quote_types)) {
1165
		$idstring = "\"{$idstring}\"";
1166
	}
1167

    
1168
	return $idstring;
1169
}
1170

    
1171
function ipsec_fixup_network($network) {
1172
	if (substr($network, -3) == '|/0') {
1173
		$result = substr($network, 0, -3);
1174
	} else {
1175
		$tmp = explode('|', $network);
1176
		if (isset($tmp[1])) {
1177
			$result = $tmp[1];
1178
		} else {
1179
			$result = $tmp[0];
1180
		}
1181
		unset($tmp);
1182
	}
1183

    
1184
	return $result;
1185
}
1186

    
1187
function ipsec_get_loglevels() {
1188
	global $ipsec_log_cats;
1189
	$def_loglevel = '1';
1190

    
1191
	$levels = array();
1192

    
1193
	foreach (array_keys($ipsec_log_cats) as $cat) {
1194
		if (strlen(config_get_path('ipsec/logging/' . $cat)) > 0) {
1195
			$levels[$cat] = config_get_path('ipsec/logging/' . $cat);
1196
		} elseif (in_array($cat, array('ike', 'chd', 'cfg'))) {
1197
			$levels[$cat] = "2";
1198
		} else {
1199
			$levels[$cat] = $def_loglevel;
1200
		}
1201
	}
1202
	return $levels;
1203
}
1204

    
1205
function ipsec_vti($p1, $returnaddresses = false, $skipdisabled = true) {
1206
	$is_vti = false;
1207
	$vtisubnet_spec = array();
1208

    
1209
	foreach(ipsec_get_phase2_by_ikeid($p1['ikeid']) as $p2) {
1210
		if ($p2['mode'] == 'vti') {
1211
			if ($skipdisabled && isset($p2['disabled'])) {
1212
				continue;
1213
			} else {
1214
				$is_vti = true;
1215
			}
1216
			if ($returnaddresses) {
1217
				$vtisubnet_spec[] = array(
1218
					'left' => ipsec_idinfo_to_cidr($p2['localid'], true, $p2['mode']),
1219
					'right' => ipsec_idinfo_to_cidr($p2['remoteid'], false, $p2['mode']),
1220
					'descr' => $p2['descr'],
1221
					'reqid' => $p2['reqid'],
1222
				);
1223
			}
1224
		}
1225
	}
1226
	return ($returnaddresses) ? $vtisubnet_spec : $is_vti;
1227
}
1228

    
1229
/* Called when IPsec is reloaded through rc.newipsecdns and similar, gives
1230
 * packages an opportunity to execute custom code when IPsec reloads.
1231
 */
1232
function ipsec_reload_package_hook() {
1233
	global $g;
1234

    
1235
	init_config_arr(array('installedpackages', 'package'));
1236
	foreach (config_get_path('installedpackages/package', []) as $package) {
1237
		if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) {
1238
			continue;
1239
		}
1240

    
1241
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $package['configurationfile'], 'packagegui');
1242
		if (!empty($pkg_config['include_file']) &&
1243
		    file_exists($pkg_config['include_file'])) {
1244
			require_once($pkg_config['include_file']);
1245
		}
1246
		if (empty($pkg_config['ipsec_reload_function'])) {
1247
			continue;
1248
		}
1249
		$pkg_ipsec_reload = $pkg_config['ipsec_reload_function'];
1250
		if (!function_exists($pkg_ipsec_reload)) {
1251
			continue;
1252
		}
1253
		$pkg_ipsec_reload();
1254
	}
1255
}
1256

    
1257
/****f* certs/ipsec_convert_to_modp
1258
 * NAME
1259
 *   ipsec_convert_to_modp - Take a DH Group number value and return the
1260
 *                           associated name
1261
 * INPUTS
1262
 *   $index: DH group index number to look up
1263
 * RESULT
1264
 *   Returns the Diffie Hellman Group keyword associated with the group number.
1265
 ******/
1266
function ipsec_convert_to_modp($index) {
1267
	$modpmap = array(
1268
		'1' => 'modp768',
1269
		'2' => 'modp1024',
1270
		'5' => 'modp1536',
1271
		'14' => 'modp2048',
1272
		'15' => 'modp3072',
1273
		'16' => 'modp4096',
1274
		'17' => 'modp6144',
1275
		'18' => 'modp8192',
1276
		'19' => 'ecp256',
1277
		'20' => 'ecp384',
1278
		'21' => 'ecp521',
1279
		'22' => 'modp1024s160',
1280
		'23' => 'modp2048s224',
1281
		'24' => 'modp2048s256',
1282
		'25' => 'ecp192',
1283
		'26' => 'ecp224',
1284
		'27' => 'ecp224bp',
1285
		'28' => 'ecp256bp',
1286
		'29' => 'ecp384bp',
1287
		'30' => 'ecp512bp',
1288
		'31' => 'curve25519',
1289
		'32' => 'curve448',
1290
	);
1291

    
1292
	return $modpmap[$index];
1293
}
1294

    
1295
/*
1296
 * Forcefully restart IPsec
1297
 * This is required for when dynamic interfaces reload
1298
 * For all other occasions the normal ipsec_configure()
1299
 * will gracefully reload the settings without restarting
1300
 */
1301
function ipsec_force_reload($interface = "", $protocol = "") {
1302
	global $g;
1303

    
1304
	if (!ipsec_enabled()) {
1305
		return;
1306
	}
1307

    
1308
	$ipseccfg = config_get_path('ipsec');
1309

    
1310
	if (!empty($interface) && is_array($ipseccfg['phase1'])) {
1311
		$found = false;
1312
		$realif = array();
1313
		$ifcache = array();
1314
		foreach ($ipseccfg['phase1'] as $ipsec) {
1315
			if (isset($ipsec['disabled'])) {
1316
				continue;
1317
			}
1318
			if (!empty($protocol) &&
1319
			    ($ipsec['protocol'] != $protocol) &&
1320
			    ($ipsec['protocol'] != 'both')) {
1321
				continue;
1322
			}
1323
			if ($ipsec['interface'] == $interface) {
1324
				$found = true;
1325
				break;
1326
			} else {
1327
				$fam = (!empty($ipsec['protocol'])) ? $ipsec['protocol'] : 'inet';
1328

    
1329
				/* Find the real active passed interface for this address family, cache the result */
1330
				if (empty($realif) ||
1331
				    empty($realif[$fam])) {
1332
					$realif[$fam] = get_failover_interface($interface, $fam);
1333
				}
1334

    
1335
				/* Find the real active tunnel interface for this address family, cache the result */
1336
				if (empty($ifcache) ||
1337
				    empty($ifcache[$ipsec['interface']]) ||
1338
				    empty($ifcache[$ipsec['interface']][$fam])) {
1339
					if (!is_array($ifcache[$ipsec['interface']])) {
1340
						$ifcache[$ipsec['interface']] = array();
1341
					}
1342
					$ifcache[$ipsec['interface']][$fam] = get_failover_interface($ipsec['interface'], $fam);
1343
				}
1344

    
1345
				if ($realif[$fam] == $ifcache[$ipsec['interface']][$fam]) {
1346
					$found = true;
1347
					break;
1348
				}
1349
			}
1350
		}
1351
		if (!$found) {
1352
			log_error(sprintf(gettext("Ignoring IPsec reload since there are no tunnels on interface %s"), $interface));
1353
			return;
1354
		}
1355
	}
1356

    
1357
	/* If we get this far then we need to take action. */
1358
	log_error(gettext("Forcefully reloading IPsec"));
1359
	ipsec_configure();
1360
}
1361

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

    
1406
global $g, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs;
1407
$ipsec_swanctl_basedir = "{$g['varetc_path']}/ipsec";
1408
$ipsec_swanctl_dirs = array(
1409
	'conf'     => "{$ipsec_swanctl_basedir}/conf.d",
1410
	'certpath' => "{$ipsec_swanctl_basedir}/x509",
1411
	'capath'   => "{$ipsec_swanctl_basedir}/x509ca",
1412
	'aapath'   => "{$ipsec_swanctl_basedir}/x509aa",
1413
	'ocsppath' => "{$ipsec_swanctl_basedir}/x509ocsp",
1414
	'crlpath'  => "{$ipsec_swanctl_basedir}/x509crl",
1415
	'acpath'   => "{$ipsec_swanctl_basedir}/x509ac",
1416
	'rsakeys'  => "{$ipsec_swanctl_basedir}/rsa",
1417
	'eckeys'   => "{$ipsec_swanctl_basedir}/ecdsa",
1418
	'keypath'  => "{$ipsec_swanctl_basedir}/private",
1419
	'pubkpath' => "{$ipsec_swanctl_basedir}/pubkey",
1420
	'blisspath' => "{$ipsec_swanctl_basedir}/bliss",
1421
	'pkcs8path' => "{$ipsec_swanctl_basedir}/pkcs8",
1422
	'pkcs12path' => "{$ipsec_swanctl_basedir}/pkcs12",
1423
);
1424

    
1425
/****f* ipsec/ipsec_create_dirs
1426
 * NAME
1427
 *   ipsec_create_dirs - Create default set of strongSwan configuration directories.
1428
 * INPUTS
1429
 *   None
1430
 * RESULT
1431
 *   Cleans up and creates strongSwan configuration directories and links.
1432
 ******/
1433
function ipsec_create_dirs() {
1434
	global $g, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs;
1435

    
1436
	/* Cleanup base paths to ensure old files are not left behind (#5238) */
1437
	if (!empty($ipsec_swanctl_basedir)) {
1438
		@rmdir_recursive($ipsec_swanctl_basedir, false);
1439
	}
1440
	/* Create directory structure */
1441
	array_map('safe_mkdir', array_values($ipsec_swanctl_dirs));
1442

    
1443
	/* Create and link strongSwan config */
1444
	$ssdpath = "{$g['varetc_path']}/ipsec/strongswan.d";
1445
	if (!file_exists($ssdpath) || !is_link($ssdpath)) {
1446
		if (is_dir($ssdpath) && !is_link($ssdpath)) {
1447
			@rmdir_recursive($ssdpath, false);
1448
		} else {
1449
			@unlink($ssdpath);
1450
		}
1451
		@symlink("/usr/local/etc/strongswan.d",
1452
		    $ssdpath);
1453
	}
1454
	if (!file_exists("/usr/local/etc/strongswan.conf") ||
1455
	    !is_link("/usr/local/etc/strongswan.conf")) {
1456
		@unlink("/usr/local/etc/strongswan.conf");
1457
		@symlink("{$g['varetc_path']}/ipsec/strongswan.conf",
1458
		    "/usr/local/etc/strongswan.conf");
1459
	}
1460
}
1461

    
1462
/****f* ipsec/ipsec_setup_gwifs
1463
 * NAME
1464
 *   ipsec_setup_gwifs - Setup IPsec-related interfaces and gateways
1465
 * INPUTS
1466
 *   None
1467
 * RESULT
1468
 *   Sets up VTI interfaces and gateways, populates ipsecpinghosts
1469
 ******/
1470
function ipsec_setup_gwifs() {
1471
	global $g, $rgmap, $filterdns_list,
1472
		$aggressive_mode_psk, $mobile_ipsec_auth, $ifacesuse,
1473
		$ipsecpinghostsactive;
1474
	/* resolve all local, peer addresses and setup pings */
1475

    
1476
	unset($iflist);
1477
	$ipsecpinghosts = array();
1478
	/* step through each phase1 entry */
1479
	foreach (config_get_path('ipsec/phase1', []) as $ph1ent) {
1480
		if (isset($ph1ent['disabled'])) {
1481
			continue;
1482
		}
1483
		if (substr($ph1ent['interface'], 0, 4) == "_vip") {
1484
			$vpninterface = get_configured_vip_interface($ph1ent['interface']);
1485
			$ifacesuse[] = get_real_interface($vpninterface);
1486
		} else {
1487
			$vpninterface = get_failover_interface($ph1ent['interface'], 'all');
1488
			if (substr($vpninterface, 0, 4) == "_vip") {
1489
				$vpninterface = get_configured_vip_interface($vpninterface);
1490
				$ifacesuse[] = get_real_interface($vpninterface);
1491
			} elseif (!empty($vpninterface)) {
1492
				$ifacesuse[] = $vpninterface;
1493
			}
1494
		}
1495
		if ($ph1ent['mode'] == "aggressive" && ($ph1ent['authentication_method'] == "pre_shared_key" || $ph1ent['authentication_method'] == "xauth_psk_server")) {
1496
			$aggressive_mode_psk = true;
1497
		}
1498

    
1499
		$ikeid = $ph1ent['ikeid'];
1500

    
1501
		$local_spec = ipsec_get_phase1_src($ph1ent);
1502
		/* When automatically guessing, use the first address. */
1503
		$local_spec  = explode(',', $local_spec);
1504
		$local_spec  = $local_spec[0];
1505
		if (!is_ipaddr($local_spec)) {
1506
			log_error(sprintf(gettext("IPsec ERROR: Could not find phase 1 source for connection %s. Omitting from configuration file."), $ph1ent['descr']));
1507
			continue;
1508
		}
1509

    
1510
		if (isset($ph1ent['mobile'])) {
1511
			$mobile_ipsec_auth = $ph1ent['authentication_method'];
1512
			continue;
1513
		}
1514

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

    
1533
		$is_vti = false;
1534
		/* step through each phase2 entry */
1535
		foreach (config_get_path('ipsec/phase2', []) as $ph2ent) {
1536
			if ($ph2ent['ikeid'] != $ph1ent['ikeid']) {
1537
				continue;
1538
			}
1539
			if ($ph2ent['mode'] == 'vti') {
1540
				$is_vti = true;
1541
			}
1542
			if (isset($ph2ent['disabled']) || ($ikeid != $ph2ent['ikeid'])) {
1543
				continue;
1544
			}
1545

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

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

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

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

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

    
1657
	if ($aggressive_mode_psk) {
1658
		$stronconf = '';
1659
		if (file_exists("{$g['varetc_path']}/ipsec/strongswan.conf")) {
1660
			$stronconf = file_get_contents("{$g['varetc_path']}/ipsec/strongswan.conf");
1661
		}
1662
		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.");
1663
		$restart = (!empty($stronconf) && strpos($stronconf, 'i_dont_care_about_security_and_use_aggressive_mode_psk') === FALSE);
1664
		unset($stronconf);
1665
		$ssconf['charon']['i_dont_care_about_security_and_use_aggressive_mode_psk'] = "yes";
1666
	}
1667

    
1668
	if (config_path_enabled('ipsec', 'acceptunencryptedmainmode')) {
1669
		$ssconf['charon']['accept_unencrypted_mainmode_messages'] = "yes";
1670
	}
1671

    
1672
	if (!empty(config_get_path('ipsec/maxexchange'))) {
1673
		$ssconf['charon']['max_ikev1_exchanges'] = config_get_path('ipsec/maxexchange');
1674
	}
1675

    
1676
	$unity_enabled = config_path_enabled('ipsec', 'unityplugin') ? 'yes' : 'no';
1677
	$ssconf['charon']['cisco_unity'] = $unity_enabled;
1678

    
1679
	if (config_path_enabled('ipsec', 'enableinterfacesuse') && !empty($ifacesuse)) {
1680
		$ssconf['charon']['interfaces_use'] = implode(',', array_unique($ifacesuse));
1681
	}
1682

    
1683
	if (config_path_enabled('ipsec', 'makebeforebreak')) {
1684
		$ssconf['charon']['make_before_break'] = "yes";
1685
	}
1686

    
1687
	if (!empty(config_get_path('ipsec/port'))) {
1688
		$ssconf['charon']['port'] = config_get_path('ipsec/port');
1689
	}
1690

    
1691
	if (!empty(config_get_path('ipsec/port_nat_t'))) {
1692
		$ssconf['charon']['port_nat_t'] = config_get_path('ipsec/port_nat_t');
1693
	}
1694

    
1695
	/* IKEv2 retransmit options, see https://redmine.pfsense.org/issues/12184 */
1696
	if (!empty(config_get_path('ipsec/ikev2_retransmit_tries'))) {
1697
		$ssconf['charon']['retransmit_tries'] = config_get_path('ipsec/ikev2_retransmit_tries');
1698
	}
1699
	if (!empty(config_get_path('ipsec/ikev2_retransmit_timeout'))) {
1700
		$ssconf['charon']['retransmit_timeout'] = config_get_path('ipsec/ikev2_retransmit_timeout');
1701
	}
1702
	if (!empty(config_get_path('ipsec/ikev2_retransmit_base'))) {
1703
		$ssconf['charon']['retransmit_base'] = config_get_path('ipsec/ikev2_retransmit_base');
1704
	}
1705
	if (!empty(config_get_path('ipsec/ikev2_retransmit_jitter'))) {
1706
		$ssconf['charon']['retransmit_jitter'] = config_get_path('ipsec/ikev2_retransmit_jitter');
1707
	}
1708
	if (!empty(config_get_path('ipsec/ikev2_retransmit_limit'))) {
1709
		$ssconf['charon']['retransmit_limit'] = config_get_path('ipsec/ikev2_retransmit_limit');
1710
	}
1711

    
1712
	$ssconf['charon']['syslog'] = array();
1713
	$ssconf['charon']['syslog']['identifier'] = 'charon';
1714
	$ssconf['charon']['syslog'][] = "# log everything under daemon since it ends up in the same place regardless with our syslog.conf";
1715
	$ssconf['charon']['syslog']['daemon'] = array();
1716
	$ssconf['charon']['syslog']['daemon']['ike_name'] = 'yes';
1717
	$ssconf['charon']['syslog']['daemon'] = array_merge($ssconf['charon']['syslog']['daemon'], ipsec_logging_config());
1718
	$ssconf['charon']['syslog'][] = '# disable logging under auth so logs aren\'t duplicated';
1719
	$ssconf['charon']['syslog']['auth'] = array();
1720
	$ssconf['charon']['syslog']['auth']['default'] = -1;
1721

    
1722
	$ssconf['charon']['plugins'] = array();
1723
	$ssconf['charon']['plugins'][] = "# Load defaults";
1724
	$ssconf['charon']['plugins'][] = "include {$g['varetc_path']}/ipsec/strongswan.d/charon/*.conf";
1725
	$ssconf['charon']['plugins']['unity'] = array('load' => $unity_enabled);
1726
	$ssconf['charon']['plugins']['curve25519'] = array('load' => "yes");
1727

    
1728
	if (config_path_enabled('ipsec', 'pkcs11support')) {
1729
		$ssconf['charon']['plugins']['pkcs11'] = array();
1730
		$ssconf['charon']['plugins']['pkcs11']['load'] = "yes";
1731
		$ssconf['charon']['plugins']['pkcs11']['modules'] = array();
1732
		$ssconf['charon']['plugins']['pkcs11']['modules']['opensc'] = array();
1733
		$ssconf['charon']['plugins']['pkcs11']['modules']['opensc']['load_certs'] = "yes";
1734
		$ssconf['charon']['plugins']['pkcs11']['modules']['opensc']['path'] = "/usr/local/lib/opensc-pkcs11.so";
1735
	}
1736

    
1737
	/* Find RADIUS servers designated for Mobile IPsec user auth */
1738
	$radius_servers = array();
1739
	foreach (explode(',', config_get_path('ipsec/client/user_source', '')) as $user_source) {
1740
		$auth_server = auth_get_authserver($user_source);
1741
		$nice_user_source = strtolower(preg_replace('/[\s\.]+/', '_', $user_source));
1742
		if ($auth_server && ($auth_server['type'] === 'radius')) {
1743
			$radius_servers[$nice_user_source] = array();
1744
			$radius_servers[$nice_user_source]['address'] = $auth_server['host'];
1745
			$radius_servers[$nice_user_source]['secret'] = "\"{$auth_server['radius_secret']}\"";
1746
			$radius_servers[$nice_user_source]['auth_port'] = empty($auth_server['radius_auth_port']) ? 1812 : $auth_server['radius_auth_port'];;
1747
			$radius_servers[$nice_user_source]['acct_port'] = empty($auth_server['radius_acct_port']) ? 1813 : $auth_server['radius_acct_port'];
1748
		}
1749
	}
1750

    
1751
	/* Generate an eap-radius config section if appropriate */
1752
	if (count($radius_servers) && ($mobile_ipsec_auth === "eap-radius")) {
1753
		$ssconf['charon']['plugins']['eap-radius'] = array();
1754
		$ssconf['charon']['plugins']['eap-radius']['load'] = "2";
1755
		$ssconf['charon']['plugins']['eap-radius']['class_group'] = "yes";
1756
		$ssconf['charon']['plugins']['eap-radius']['eap_start'] = "no";
1757
		/* Activate RADIUS accounting only if it was selected on the IPsec Mobile Clients tab */
1758
		if ($auth_server && isset($auth_server['radius_acct_port']) &&
1759
		    (config_get_path('ipsec/client/radiusaccounting') == 'enabled')) {
1760
			$ssconf['charon']['plugins']['eap-radius']['accounting'] = "yes";
1761
			$ssconf['charon']['plugins']['eap-radius']['accounting_close_on_timeout'] = "no";
1762
			$ssconf['charon']['plugins']['eap-radius']['accounting_requires_vip'] = "yes";
1763
		}
1764
		/* advanced parameters, see https://redmine.pfsense.org/issues/11211 */
1765
		if (config_get_path('ipsec/client/radius_retransmit_base')) {
1766
			$ssconf['charon']['plugins']['eap-radius']['retransmit_base'] = config_get_path('ipsec/client/radius_retransmit_base');
1767
		}
1768
		if (config_get_path('ipsec/client/radius_retransmit_timeout')) {
1769
			$ssconf['charon']['plugins']['eap-radius']['retransmit_timeout'] = config_get_path('ipsec/client/radius_retransmit_timeout');
1770
		}
1771
		if (config_get_path('ipsec/client/radius_retransmit_tries')) {
1772
			$ssconf['charon']['plugins']['eap-radius']['retransmit_tries'] = config_get_path('ipsec/client/radius_retransmit_tries');
1773
		}
1774
		if (config_get_path('ipsec/client/radius_sockets')) {
1775
			$ssconf['charon']['plugins']['eap-radius']['sockets'] = config_get_path('ipsec/client/radius_sockets');
1776
		}
1777
		$ssconf['charon']['plugins']['eap-radius']['servers'] = $radius_servers;
1778
	}
1779

    
1780
	if (config_path_enabled('ipsec/client')) {
1781
		if (config_get_path('ipsec/client/user_source') != "none") {
1782
			$ssconf['charon']['plugins']['xauth-generic'] = array();
1783
			$ssconf['charon']['plugins']['xauth-generic']['script'] = "/etc/inc/ipsec.auth-user.php";
1784
			$authcfgs = array();
1785
			foreach (explode(",", config_get_path('ipsec/client/user_source')) as $authcfg) {
1786
				$authcfgs[] = ($authcfg == "system") ? "Local Database" : $authcfg;
1787
			}
1788
			$ssconf['charon']['plugins']['xauth-generic']['authcfg'] = implode(",", $authcfgs);
1789
		}
1790

    
1791
		$ssconf['charon']['plugins']['attr'] = array();
1792

    
1793
		$rightdnsservers = array();
1794
		if (!empty(config_get_path('ipsec/client/dns_server1'))) {
1795
			$rightdnsservers[] = config_get_path('ipsec/client/dns_server1');
1796
		}
1797
		if (!empty(config_get_path('ipsec/client/dns_server2'))) {
1798
			$rightdnsservers[] = config_get_path('ipsec/client/dns_server2');
1799
		}
1800
		if (!empty(config_get_path('ipsec/client/dns_server3'))) {
1801
			$rightdnsservers[] = config_get_path('ipsec/client/dns_server3');
1802
		}
1803
		if (!empty(config_get_path('ipsec/client/dns_server4'))) {
1804
			$rightdnsservers[] = config_get_path('ipsec/client/dns_server4');
1805
		}
1806
		if (count($rightdnsservers)) {
1807
			$ssconf['charon']['plugins']['attr']['dns'] = implode(',', $rightdnsservers);
1808
		}
1809

    
1810
		$cfgservers = array();
1811
		if (!empty(config_get_path('ipsec/client/wins_server1'))) {
1812
			$cfgservers[] = config_get_path('ipsec/client/wins_server1');
1813
		}
1814
		if (!empty(config_get_path('ipsec/client/wins_server2'))) {
1815
			$cfgservers[] = config_get_path('ipsec/client/wins_server2');
1816
		}
1817
		if (!empty($cfgservers)) {
1818
			$ssconf['charon']['plugins']['attr']['nbns'] = implode(',', $cfgservers);
1819
		}
1820
		unset($cfgservers);
1821

    
1822
		$net_list = array();
1823
		if (config_path_enabled('ipsec/client', 'net_list')) {
1824
			foreach (config_get_path('ipsec/phase2', []) as $ph2ent) {
1825
				if (isset($ph2ent['disabled']) ||
1826
				    !isset($ph2ent['mobile'])) {
1827
					continue;
1828
				}
1829
				$nlent = ipsec_idinfo_to_cidr($ph2ent['localid'], true, $ph2ent['mode']);
1830
				if (is_subnet($nlent)) {
1831
					$net_list[] = $nlent;
1832
				}
1833
				unset($nlent);
1834
			}
1835
		}
1836

    
1837
		if (!empty($net_list)) {
1838
			$ssconf['charon']['plugins']['attr']['subnet'] = implode(',', $net_list);
1839
			$ssconf['charon']['plugins']['attr']['split-include'] = implode(',', $net_list);
1840
		}
1841

    
1842
		if (!empty(config_get_path('ipsec/client/dns_domain'))) {
1843
			$ssconf['charon']['plugins']['attr'][] = "# Search domain and default domain";
1844
			$ssconf['charon']['plugins']['attr']['27674'] = config_get_path('ipsec/client/dns_domain');
1845
			if (!empty(config_get_path('ipsec/client/dns_split'))) {
1846
				$ssconf['charon']['plugins']['attr']['27675'] = config_get_path('ipsec/client/dns_domain');
1847
			}
1848
		}
1849

    
1850
		if (!empty(config_get_path('ipsec/client/dns_split'))) {
1851
			$ssconf['charon']['plugins']['attr'][] = "# Split DNS (UNITY_SPLITDNS_NAME)";
1852
			$ssconf['charon']['plugins']['attr']['28675'] = config_get_path('ipsec/client/dns_split');
1853
			$ssconf['charon']['plugins']['attr'][] = "# Split DNS (INTERNAL_DNS_DOMAIN, RFC 8598)";
1854
			$ssconf['charon']['plugins']['attr']['25'] = str_replace(' ', ',', config_get_path('ipsec/client/dns_split'));
1855
		}
1856

    
1857
		if (!empty(config_get_path('ipsec/client/login_banner'))) {
1858
			$ssconf['charon']['plugins']['attr'][] = "# Login Banner";
1859
			$ssconf['charon']['plugins']['attr']['28672'] = config_get_path('ipsec/client/login_banner');
1860
		}
1861

    
1862
		if (config_path_enabled('ipsec/client', 'save_passwd')) {
1863
			$ssconf['charon']['plugins']['attr'][] = "# Save Xauth Password";
1864
			$ssconf['charon']['plugins']['attr']['28673'] = "1";
1865
		}
1866

    
1867
		if (config_get_path('ipsec/client/pfs_group')) {
1868
			$ssconf['charon']['plugins']['attr'][] = "# Phase2 PFS Group";
1869
			$ssconf['charon']['plugins']['attr']['28679'] = config_get_path('ipsec/client/pfs_group');
1870
		}
1871
	}
1872

    
1873
	@file_put_contents("{$g['varetc_path']}/ipsec/strongswan.conf", ipsec_strongswan_confgen($ssconf));
1874
}
1875

    
1876
/****f* ipsec/ipsec_setup_pools
1877
 * NAME
1878
 *   ipsec_setup_pools - Generate primary mobile pool configuration for swanctl
1879
  * INPUTS
1880
 *   None
1881
 * RESULT
1882
 *   Adds configured mobile pool settings to $scconf
1883
 * NOTES
1884
 *   These values were formerly in strongswan.conf but may now be configured in
1885
 *   pools, making future expansion to support multiple pools possible.
1886
 ******/
1887
function ipsec_setup_pools() {
1888
	global $g, $mobile_ipsec_auth, $scconf;
1889
	if (!config_path_enabled('ipsec/client')) {
1890
		return;
1891
	}
1892
	if (($mobile_ipsec_auth == "eap-radius") && empty(config_get_path('ipsec/client/pool_address')) &&
1893
	    empty(config_get_path('ipsec/client/pool_address_v6'))) {
1894
		return;
1895
	}
1896

    
1897
	/* Keep the comment in the array to ensure the entry makes it into the
1898
	 * config in all cases.
1899
	 * See https://redmine.pfsense.org/issues/11891 */
1900
	$scconf['mobile-pool'] = array("# Mobile pool settings template");
1901
	$scconf['pools'] = array();
1902
	$pool_common =& $scconf['mobile-pool'];
1903

    
1904
	if (!empty(config_get_path('ipsec/client/pool_address'))) {
1905
		$scconf['pools']['mobile-pool-v4 : mobile-pool'] = array();
1906
		$v4pool =& $scconf['pools']['mobile-pool-v4 : mobile-pool'];
1907
		$v4pool['addrs'] = config_get_path('ipsec/client/pool_address') . '/' . config_get_path('ipsec/client/pool_netbits');
1908
	}
1909
	if (!empty(config_get_path('ipsec/client/pool_address_v6'))) {
1910
		$scconf['pools']['mobile-pool-v6 : mobile-pool'] = array();
1911
		$v6pool =& $scconf['pools']['mobile-pool-v6 : mobile-pool'];
1912
		$v6pool['addrs'] = config_get_path('ipsec/client/pool_address_v6') . '/' . config_get_path('ipsec/client/pool_netbits_v6');
1913
	}
1914

    
1915
	return;
1916
}
1917

    
1918
/****f* ipsec/ipsec_setup_userpools
1919
 * NAME
1920
 *   ipsec_setup_userpools - Generate per-user custom pool settings for swanctl
1921
  * INPUTS
1922
 *   None
1923
 * RESULT
1924
 *   Adds configured per-user pool settings to $scconf using the primary mobile
1925
 *   pool as a base configuration.
1926
 * NOTES
1927
 *   Given this new flexible format, it is now possible to override any valid
1928
 *   pool setting, so future expansion of per-user settings is possible.
1929
 ******/
1930
function ipsec_setup_userpools() {
1931
	global $scconf;
1932
	$a_mobilekey = config_get_path('ipsec/mobilekey');
1933

    
1934
	/* Do not waste time if we do not have all the necessary information. */
1935
	if (!is_array($a_mobilekey) ||
1936
	    empty($a_mobilekey) ||
1937
	    !is_array($scconf['connections']) ||
1938
	    !is_array($scconf['con-mobile-defaults']) ||
1939
	    !is_array($scconf['pools']) ||
1940
	    !is_array($scconf['mobile-pool'])) {
1941
		return;
1942
	}
1943

    
1944
	$suffix = 1;
1945
	foreach ($a_mobilekey as $mkent) {
1946
		if (($mkent['type'] != "EAP") ||
1947
		    !isset($mkent['ident_type']) ||
1948
		    !isset($mkent['pool_address']) ||
1949
		    !isset($mkent['pool_netbits']) ||
1950
		    (strlen($mkent['pool_address']) < 1) ||
1951
		    !is_ipaddr($mkent['pool_address'])) {
1952
			continue;
1953
		}
1954
		/* The name of just this pool */
1955
		$upbase = "mobile-userpool-{$suffix}";
1956
		/* The full connection name including a reference to the primary
1957
		 * mobile connection */
1958
		$upconn = "con-{$upbase} : con-mobile-defaults";
1959
		/* The full pool name including a reference to the primary
1960
		 * mobile pool */
1961
		$upname = "{$upbase} : mobile-pool";
1962

    
1963
		$scconf['connections'][$upconn] = array();
1964
		$scconf['pools'][$upname] = array();
1965

    
1966
		$clientid = (in_array($mkent['ident_type'], array('auto', 'none'))) ? "\"{$mkent['ident']}\"" : "{$mkent['ident_type']}:{$mkent['ident']}";
1967

    
1968
		/* Craft a cloned connection with the ID information to match */
1969
		$scconf['connections'][$upconn]['remote'] = array();
1970
		$scconf['connections'][$upconn]['remote']['id'] = $clientid;
1971
		$scconf['connections'][$upconn]['remote']['eap_id'] = '%any';
1972
		$scconf['connections'][$upconn]['pools'] = $upbase;
1973

    
1974
		/* Craft a cloned pool with pool settings to override for this user */
1975
		$scconf['pools'][$upname]['addrs'] = "{$mkent['pool_address']}/{$mkent['pool_netbits']}";
1976
		if (isset($mkent['dns_address']) && strlen($mkent['dns_address']) > 0 && is_ipaddr($mkent['dns_address'])) {
1977
			$scconf['pools'][$upname]['dns'] = $mkent['dns_address'];
1978
		}
1979
		$suffix++;
1980
	}
1981
	return;
1982
}
1983

    
1984
/****f* ipsec/ipsec_setup_bypass
1985
 * NAME
1986
 *   ipsec_setup_bypass - Generate a bypass connection for LAN-LAN and custom rules traffic
1987
 * INPUTS
1988
 *   None
1989
 * RESULT
1990
 *   Sets up a bypass connection to prevent local traffic from being caught by
1991
 *   IPsec policies.
1992
 ******/
1993
function ipsec_setup_bypass() {
1994
	global $scconf;
1995

    
1996
	$scconf['connections']['bypass'] = array();
1997
	/* Prevents the connection from being considered for remote peers */
1998
	$scconf['connections']['bypass']['remote_addrs'] = "127.0.0.1";
1999
	$scconf['connections']['bypass']['children'] = array();
2000

    
2001
	/* Locate the LAN IPv4 and IPv6 subnets */
2002
	$bypassnets = array();
2003
	if (!empty(config_get_path('interfaces/lan'))) {
2004
		$lanip = get_interface_ip("lan");
2005
		if (!empty($lanip) && is_ipaddrv4($lanip)) {
2006
			$lansn = get_interface_subnet("lan");
2007
			$lansa = gen_subnetv4($lanip, $lansn);
2008
			if (!empty($lansa) && !empty($lansn)) {
2009
				$bypassnets[] = "{$lansa}/{$lansn}";
2010
			}
2011
		}
2012
		$lanip6 = get_interface_ipv6("lan");
2013
		if (!empty($lanip6) && is_ipaddrv6($lanip6)) {
2014
			$lansn6 = get_interface_subnetv6("lan");
2015
			$lansa6 = gen_subnetv6($lanip6, $lansn6);
2016
			if (!empty($lansa6) && !empty($lansn6)) {
2017
				$bypassnets[] = "{$lansa6}/{$lansn6}";
2018
			}
2019
		}
2020
	}
2021
	/* If we have viable targets, setup a bypass LAN connection */
2022
	if (!empty($bypassnets) && !config_path_enabled('ipsec', 'noshuntlaninterfaces')) {
2023
		$bypass = implode(',', $bypassnets);
2024
		$scconf['connections']['bypass']['children']['bypasslan'] = array();
2025
		$scconf['connections']['bypass']['children']['bypasslan']['local_ts'] = $bypass;
2026
		$scconf['connections']['bypass']['children']['bypasslan']['remote_ts'] = $bypass;
2027
		$scconf['connections']['bypass']['children']['bypasslan']['mode'] = "pass";
2028
		$scconf['connections']['bypass']['children']['bypasslan']['start_action'] = "trap";
2029
	}
2030

    
2031
	/* Setup custom bypass rules */
2032
	if (config_path_enabled('ipsec', 'ipsecbypass')) {
2033
		foreach (config_get_path('ipsec/bypassrules/rule', []) as $id => $rule) {
2034
			$scconf['connections']['bypass']['children']["rule{$id}"] = array();
2035
			$scconf['connections']['bypass']['children']["rule{$id}"]["local_ts"] = $rule['source'] .
2036
			       	"/" . $rule['srcmask'];
2037
			$scconf['connections']['bypass']['children']["rule{$id}"]["remote_ts"] = $rule['destination'] .
2038
			       	"/" . $rule['dstmask'];
2039
			$scconf['connections']['bypass']['children']["rule{$id}"]["mode"] = "pass";
2040
			$scconf['connections']['bypass']['children']["rule{$id}"]["start_action"] = "trap";
2041
		}
2042
	}
2043

    
2044
	return;
2045
}
2046

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

    
2111
/****f* ipsec/ipsec_setup_authentication
2112
 * NAME
2113
 *   ipsec_setup_authentication - Generate an array with local/remote
2114
 *                                authentication information for a given IPsec
2115
 *                                Phase 1.
2116
 * INPUTS
2117
 *   $ph1ent: An IPsec Phase 1 configuration
2118
 *   $conn  : A swanctl connection array corresponding to the IPsec Phase 1.
2119
 * RESULT
2120
 *   Populates $conn with local and remote arrays containing authentication
2121
 *   details.
2122
 ******/
2123
function ipsec_setup_authentication(& $ph1ent, & $conn) {
2124
	global $rgmap, $ipsec_swanctl_dirs;
2125
	$local = array();
2126
	$remote = array();
2127
	$remote2 = array();
2128

    
2129
	list($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local', array());
2130
	$localid = ipsec_fixup_id($myid_type, $myid_data);
2131
	if (!empty($localid)) {
2132
		$local['id'] = $localid;
2133
	}
2134

    
2135
	// Only specify peer ID if we are not dealing with mobile PSK
2136
	if (!(isset($ph1ent['mobile']) &&
2137
	    in_array($ph1ent['authentication_method'], array("pre_shared_key", "xauth_psk_server")))) {
2138
		list ($remoteid_type, $remoteid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap);
2139
		$remoteid = ipsec_fixup_id($remoteid_type, $remoteid_data);
2140
	}
2141
	if (!empty($remoteid)) {
2142
		$remote['id'] = $remoteid;
2143
	}
2144

    
2145
	if (!empty($ph1ent['caref'])) {
2146
		$ca = lookup_ca($ph1ent['caref']);
2147
		$ca = $ca['item'];
2148
		if ($ca) {
2149
			/* Get hash value to use for filename */
2150
			$ca_details = openssl_x509_parse(base64_decode($ca['crt']));
2151
			$cafn = "{$ipsec_swanctl_dirs['capath']}/{$ca_details['hash']}.0";
2152
		}
2153
	}
2154

    
2155
	$authentication = "";
2156
	switch ($ph1ent['authentication_method']) {
2157
		case 'eap-mschapv2':
2158
			if (isset($ph1ent['mobile'])) {
2159
				$local['auth'] = "pubkey";
2160
				$remote['eap_id'] = "%any";
2161
				$remote['auth'] = "eap-mschapv2";
2162
			}
2163
			break;
2164
		case 'eap-tls':
2165
			if (isset($ph1ent['mobile'])) {
2166
				$local['auth'] = "pubkey";
2167
				$remote['eap_id'] = "%any";
2168
			} else {
2169
				$local['auth'] = "eap-tls";
2170
			}
2171
			$remote['auth'] = "eap-tls";
2172
			break;
2173
		case 'eap-radius':
2174
			if (isset($ph1ent['mobile'])) {
2175
				$local['auth'] = "pubkey";
2176
				$remote['eap_id'] = "%any";
2177
			} else {
2178
				$local['auth'] = "eap-radius";
2179
			}
2180
			if ((config_get_path('ipsec/client/group_source') == 'enabled') &&
2181
			    !empty(config_get_path('ipsec/client/auth_groups'))) {
2182
				$remote['groups'] = config_get_path('ipsec/client/auth_groups');
2183
			}
2184
			$remote['auth'] = "eap-radius";
2185
			break;
2186
		case 'xauth_cert_server':
2187
			$local['auth'] = "pubkey";
2188
			$remote['auth'] = "pubkey";
2189
			$remote2['auth'] = "xauth-generic";
2190
			break;
2191
		case 'xauth_psk_server':
2192
			$local['auth'] = "psk";
2193
			$remote['auth'] = "psk";
2194
			$remote2['auth'] = "xauth-generic";
2195
			break;
2196
		case 'pre_shared_key':
2197
			$local['auth'] = "psk";
2198
			$remote['auth'] = "psk";
2199
			break;
2200
		case 'cert':
2201
		case 'pkcs11':
2202
			$local['auth'] = "pubkey";
2203
			$remote['auth'] = "pubkey";
2204
			break;
2205
		case 'hybrid_cert_server':
2206
			$local['auth'] = "pubkey";
2207
			$remote['auth'] = "xauth-generic";
2208
			break;
2209
	}
2210
	if (in_array($ph1ent['authentication_method'], array('eap-mschapv2', 'eap-tls', 'eap-radius', 'xauth_cert_server', 'cert', 'hybrid_cert_server')) &&
2211
	    !empty($ph1ent['certref'])) {
2212
		$local['cert'] = array('file' => "{$ipsec_swanctl_dirs['certpath']}/cert-{$ph1ent['ikeid']}.crt");
2213
		$conn['send_cert'] = "always";
2214
	}
2215
	if ($ph1ent['authentication_method'] == 'pkcs11') {
2216
		$local['cert'] = array('handle' => "{$ph1ent['pkcs11certref']}");
2217
		$conn['send_cert'] = "always";
2218
	}
2219
	if (in_array($ph1ent['authentication_method'], array('eap-tls', 'xauth_cert_server', 'cert', 'pkcs11')) &&
2220
	    isset($cafn)) {
2221
		$remote['cacerts'] = $cafn;
2222
		if (config_path_enabled('ipsec', 'strictcrlpolicy')) {
2223
			$remote['revocation'] = "strict";
2224
		}
2225
	}
2226

    
2227
	$conn['local'] = $local;
2228
	/* If there is data for a second remote auth round, setup two remote
2229
	 * arrays with unique names, otherwise setup a single remote. */
2230
	if (empty($remote2)) {
2231
		$conn['remote'] = $remote;
2232
	} else {
2233
		$conn['remote-1'] = $remote;
2234
		$conn['remote-2'] = $remote2;
2235
	}
2236
}
2237

    
2238
/****f* ipsec/ipsec_setup_proposal_algo
2239
 * NAME
2240
 *   ipsec_setup_proposal_algo - Form a single proposal algorithm string
2241
 * INPUTS
2242
 *   $ealg_id: Encryption algorithm ID
2243
 *   $keylen : Key length for the encryption algorithm
2244
 *   $halgo  : Hash algorithm
2245
 *   $modp   : DH Group number
2246
 * RESULT
2247
 *   Returns a string using the available information to form a single proposal
2248
 *   algorithm definition.
2249
 * NOTES
2250
 *   Values left empty will not be added to the string. Some combinations may
2251
 *   require one or more parts to be omitted.
2252
 ******/
2253
function ipsec_setup_proposal_algo($ealg_id, $keylen, $halgo, $prfalgo, $modp) {
2254
	$palgo = "";
2255

    
2256
	/* Add the encryption algorithm (if present) */
2257
	if (!empty($ealg_id)) {
2258
		$palgo .= "{$ealg_id}";
2259
	}
2260

    
2261
	/* Add the key length (if present) */
2262
	if (!empty($keylen)) {
2263
		$palgo .= "{$keylen}";
2264
	}
2265

    
2266
	/* Add the hash algorithm (if present) */
2267
	if (!empty($halgo)) {
2268
		/* If there is some content in the proposal already, add a
2269
		 * separator */
2270
		if (!empty($palgo)) {
2271
			$palgo .= "-";
2272
		}
2273
		$halgo = str_replace('hmac_', '', $halgo);
2274
		$palgo .= "{$halgo}";
2275
	}
2276

    
2277
	if (!empty($prfalgo)) {
2278
		$palgo .= "-prf{$prfalgo}";
2279
	}
2280

    
2281
	/* Convert the DH group to its keyword and add (if present) */
2282
	$modp = ipsec_convert_to_modp($modp);
2283
	if (!empty($modp)) {
2284
		$palgo .= "-{$modp}";
2285
	}
2286

    
2287
	return $palgo;
2288
}
2289

    
2290
/****f* ipsec/ipsec_setup_proposal_entry
2291
 * NAME
2292
 *   ipsec_setup_proposal_entry - Generate a full proposal string for an IPsec
2293
 *                                Phase 2 configuration.
2294
 * INPUTS
2295
 *   $ph2ent  : An IPsec Phase 2 configuration
2296
 *   $algo_arr: An array in which all proposal algorithms are collected
2297
 *   $ealg_id : Encryption algorithm ID
2298
 *   $keylen  : Key length for the encryption algorithm
2299
 * RESULT
2300
 *   Returns a string containing all proposal elements, and merges the entries
2301
 *   to $algo_arr as well.
2302
 ******/
2303
function ipsec_setup_proposal_entry(& $ph2ent, & $algo_arr, $ealg_id, $keylen) {
2304
	global $p2_ealgos;
2305
	$proposal = array();
2306

    
2307
	/* If multiple hash algorithms are present, loop through and add them all. */
2308
	if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option']) 
2309
	    && !(strpos($ealg_id, "gcm") || $ealg_id == "chacha20poly1305")) {
2310
		foreach ($ph2ent['hash-algorithm-option'] as $halgo) {
2311
			$proposal[] = ipsec_setup_proposal_algo($ealg_id, $keylen, $halgo, false, $ph2ent['pfsgroup']);
2312
		}
2313
	} else {
2314
		$proposal[] = ipsec_setup_proposal_algo($ealg_id, $keylen, false, false, $ph2ent['pfsgroup']);
2315
	}
2316

    
2317
	/* Add to master list */
2318
	$algo_arr = array_merge($algo_arr, $proposal);
2319
	return implode(',', $proposal);
2320
}
2321

    
2322
function ipsec_get_ifname($p1, $reqid = 0) {
2323
	$ifname = "ipsec";
2324
	if ($reqid > 0) {
2325
		$ifname .= "{$reqid}";
2326
	} else {
2327
		$p2s = ipsec_get_phase2_by_ikeid($p1['ikeid']);
2328
		if (!empty($p2s)) {
2329
			$ifname .= $p2s[0]['reqid'];
2330
		}
2331
	}
2332
	return $ifname;
2333
}
2334

    
2335
function ipsec_get_ifname_by_conf($p1 = array(), $p2 = array()) {
2336
	$have_p1 = (!empty($p1) && is_array($p1));
2337
	$have_p2 = (!empty($p2) && is_array($p2));
2338
	$reqid = null;
2339

    
2340
	if (!$have_p1 && $have_p2) {
2341
		$p1 = ipsec_get_phase1($p2['ikeid']);
2342
	}
2343

    
2344
	if ($have_p2) {
2345
		$reqid = $p2['reqid'];
2346
	}
2347

    
2348
	return ipsec_get_ifname($p1, $reqid);
2349
}
2350

    
2351
/****f* ipsec/ipsec_setup_vtireq
2352
 * NAME
2353
 *   ipsec_setup_vtireq - Setup a VTI type IPsec request
2354
 * INPUTS
2355
 *   $child                : A swanctl child array
2356
 *   $ipsec_vti_cleanup_ifs: An array of VTI interface names for later cleanup
2357
 *   $p1                   : The IKE config under which this child exists
2358
 *   $reqid                : The reqid of the P2 if known, otherwise the first P2 reqid for this P1 is looked up.
2359
 * RESULT
2360
 *   Sets up VTI-specific values for a request.
2361
 ******/
2362
function ipsec_setup_vtireq(& $child, & $ipsec_vti_cleanup_ifs, $p1, $reqid = 0) {
2363
	global $ipsec_reqid_base;
2364
	if ($reqid > 0) {
2365
		$child['reqid'] = $ipsec_reqid_base + $reqid;
2366
	} else {
2367
		$p2s = ipsec_get_phase2_by_ikeid($p1['ikeid']);
2368
		if (!empty($p2s)) {
2369
			$reqid = $p2s[0]['reqid'];
2370
			$child['reqid'] = $ipsec_reqid_base + $reqid;
2371
		} else {
2372
			return false;
2373
		}
2374
	}
2375
	/* This interface will be a valid IPsec interface, so remove it from the cleanup list. */
2376
	$ipsec_vti_cleanup_ifs = array_diff($ipsec_vti_cleanup_ifs, array(ipsec_get_ifname($p1, $reqid)));
2377
	$child['local_ts'] .= ",0.0.0.0/0,::/0";
2378
	$child['remote_ts'] .= ",0.0.0.0/0,::/0";
2379
	return true;
2380
}
2381

    
2382
/****f* ipsec/ipsec_setup_tunnels
2383
 * NAME
2384
 *   ipsec_setup_tunnels - Configure all P1/P2 entries as swanctl connections
2385
 * INPUTS
2386
 *   None
2387
 * RESULT
2388
 *   Sets up a swanctl array for all connections in the configuration along with
2389
 *   their children, authentication, etc.
2390
 ******/
2391
function ipsec_setup_tunnels() {
2392
	global $aggressive_mode_psk,
2393
		$filterdns_list, $g, $ifacesuse, $ipsec_idhandling, $ipsec_log_cats,
2394
		$ipsec_log_sevs, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs,
2395
		$ipseccfg, $mobile_ipsec_auth, $natfilterrules, $p1_ealgos,
2396
		$p2_ealgos, $rgmap, $sa, $sn, $scconf, $conn, $tunnels,
2397
		$ipsec_vti_cleanup_ifs, $conn_defaults;
2398

    
2399
	$a_groups = return_gateway_groups_array(true);
2400

    
2401
	foreach ($tunnels as $ph1ent) {
2402
		/* Skip disabled entries */
2403
		if (isset($ph1ent['disabled'])) {
2404
			continue;
2405
		}
2406
		/* If the tunnel has no encryption algorithms, it isn't valid. */
2407
		if (count(array_get_path($ph1ent, 'encryption/item', [])) == 0) {
2408
			continue;
2409
		}
2410
		/* If the local source is invalid, skip this entry. */
2411
		$local_spec = ipsec_get_phase1_src($ph1ent);
2412
		if (!$local_spec) {
2413
			continue;
2414
		}
2415

    
2416
		/* Determine the name of this connection, either con-mobile for
2417
		 * mobile clients, or a name based on the IKE ID otherwise. */
2418
		if (isset($ph1ent['mobile'])) {
2419
			$cname = "con-mobile";
2420
			/* Start with common default values */
2421
			$scconf["{$cname}-defaults"] = $conn_defaults;
2422
			/* Array reference to make things easier */
2423
			$conn =& $scconf["{$cname}-defaults"];
2424
			$scconf['connections']["{$cname} : {$cname}-defaults"] = array("# Stub to load con-mobile-defaults");
2425
		} else {
2426
			$cname = ipsec_conid($ph1ent);
2427
			/* Start with common default values */
2428
			$scconf['connections'][$cname] = $conn_defaults;
2429
			$descr = "# P1 (ikeid {$ph1ent['ikeid']})";
2430
			if (!empty($ph1ent['descr'])) {
2431
				$descr .= ": {$ph1ent['descr']}";
2432
			}
2433
			array_unshift($scconf['connections'][$cname], $descr);
2434
			/* Array reference to make things easier */
2435
			$conn =& $scconf['connections'][$cname];
2436
		}
2437

    
2438
		/* Common parameters for all children */
2439
		$child_params = array();
2440
		if (!empty($ph1ent['closeaction'])) {
2441
			$child_params['close_action'] = $ph1ent['closeaction'];
2442
		}
2443

    
2444
		$ikeid = $ph1ent['ikeid'];
2445

    
2446
		$carpbackup = false;
2447
		if (((substr($ph1ent['interface'], 0, 4) == "_vip") &&
2448
		    in_array(get_carp_bind_status($ph1ent['interface']), array('BACKUP', 'INIT'))) ||
2449
		    (is_array($a_groups[$ph1ent['interface']]) &&
2450
		    !empty($a_groups[$ph1ent['interface']][0]['vip']) &&
2451
		    in_array(get_carp_bind_status($a_groups[$ph1ent['interface']][0]['vip']), array('BACKUP', 'INIT')))) {
2452
			$carpbackup = true;
2453
		}
2454

    
2455
		/* "trap" adds policies to start a tunnel when interesting
2456
		 * traffic is observed by the host. */
2457
		$start_action = "trap";
2458
		if ($carpbackup) {
2459
			$start_action = "none";
2460
		}
2461

    
2462
		/* Set the IKE version appropriately (empty = IKEv1) */
2463
		switch ($ph1ent['iketype']) {
2464
			case 'auto':
2465
				$ikeversion = 0;
2466
				break;
2467
			case 'ikev2':
2468
				$ikeversion = 2;
2469
				break;
2470
			case 'ikev1':
2471
			default:
2472
				$ikeversion = 1;
2473
				break;
2474
		}
2475
		$conn['version'] = $ikeversion;
2476

    
2477
		/* For IKEv1 or auto, setup aggressive mode if configured */
2478
		if ($ikeversion != 2){
2479
			$conn['aggressive'] = ($ph1ent['mode'] == "aggressive") ? "yes" : "no";
2480
		}
2481

    
2482
		if (isset($ph1ent['mobile'])) {
2483
			/* Mobile tunnels allow 'any' as a peer */
2484
			$remote_spec = "0.0.0.0/0,::/0";
2485
			/* Mobile tunnels cannot start automatically */
2486
			$start_action = 'none';
2487
		} else {
2488
			if (($ph1ent['protocol'] == 'both') &&
2489
			    (($ph1ent['remote-gateway'] == '0.0.0.0') ||
2490
			    ($ph1ent['remote-gateway'] == '::'))) {
2491
				$remote_spec = '%any';
2492
			} else {
2493
				$remote_spec = $ph1ent['remote-gateway'];
2494
			}
2495
			$sourcehost = (is_ipaddr($remote_spec)) ? $remote_spec : $rgmap[$remote_spec];
2496
			$ifacesuse = ipsec_setup_routes($ph1ent['interface'], $ph1ent['protocol'], $sourcehost, isset($ph1ent['gw_duplicates']));
2497
		}
2498

    
2499
		/* Setup IKE proposals */
2500
		$ciphers = array();
2501
		foreach(array_get_path($ph1ent, 'encryption/item', []) as $p1enc) {
2502
			if (empty($p1enc) ||
2503
			    empty(array_get_path($p1enc, 'encryption-algorithm/name', '')) ||
2504
			    empty(array_get_path($p1enc, 'hash-algorithm'))) {
2505
				continue;
2506
			}
2507
			if ($ph1ent['prfselect_enable'] != 'yes') {
2508
				$p1enc['prf-algorithm'] = false;
2509
			}
2510
			$ciphers[] = ipsec_setup_proposal_algo($p1enc['encryption-algorithm']['name'],
2511
								$p1enc['encryption-algorithm']['keylen'],
2512
								$p1enc['hash-algorithm'],
2513
								$p1enc['prf-algorithm'],
2514
								$p1enc['dhgroup']);
2515
		}
2516
		if (!empty($ciphers)) {
2517
			$conn['proposals'] = implode(",", $ciphers);
2518
		}
2519

    
2520
		/* Configure DPD values. The DPD action is a per-child parameter,
2521
		 * not per-connection like the delay and timeout. */
2522
		if ($ph1ent['dpd_delay'] && $ph1ent['dpd_maxfail']) {
2523
			if ($start_action == "trap") {
2524
				$child_params['dpd_action'] = "trap";
2525
			} else {
2526
				$child_params['dpd_action'] = "clear";
2527
			}
2528
			$conn['dpd_delay'] = "{$ph1ent['dpd_delay']}s";
2529
			if ($ph1ent['iketype'] == 'ikev1') {
2530
				/* in IKEv2 the default retransmission timeout applies,
2531
				 * see https://redmine.pfsense.org/issues/12184 */
2532
				$conn['dpd_timeout'] =  $ph1ent['dpd_delay'] * ($ph1ent['dpd_maxfail'] + 1) . "s";
2533
			}
2534

    
2535
			/* Adjust dpd_action if the close_action is explicitly set */
2536
			if (!empty($child_params['close_action'])) {
2537
				switch ($ph1ent['closeaction']) {
2538
					case 'trap':
2539
						$child_params['dpd_action'] = 'trap';
2540
						break;
2541
					case 'start':
2542
						$child_params['dpd_action'] = 'restart';
2543
						break;
2544
					case 'none':
2545
					default:
2546
						$child_params['dpd_action'] = 'clear';
2547
				}
2548
			}
2549
		} else {
2550
			$child_params['dpd_action'] = "clear";
2551
		}
2552

    
2553
		/* Rekey (IKEv2 or Auto only, not supported by IKEv1) */
2554
		if (($ikeversion == 0) || ($ikeversion == 2)) {
2555
			$conn['rekey_time'] = ipsec_get_rekey_time($ph1ent) . "s";
2556
		}
2557

    
2558
		/* Reauth (Any) */
2559
		$conn['reauth_time'] = ipsec_get_reauth_time($ph1ent) . "s";
2560

    
2561
		/* Over Time */
2562
		$conn['over_time'] = ipsec_get_over_time($ph1ent) . "s";
2563

    
2564
		/* Rand Time */
2565
		$conn['rand_time'] = ipsec_get_rand_time($ph1ent) . "s";
2566

    
2567
		/* NAT Traversal */
2568
		$conn['encap'] = ($ph1ent['nat_traversal'] == 'force') ? "yes" : "no";
2569

    
2570
		/* MOBIKE support */
2571
		$conn['mobike'] = ($ph1ent['mobike'] == 'on') ? "yes" : "no";
2572

    
2573
		/* TFC Padding */
2574
		if (isset($ph1ent['tfc_enable'])) {
2575
			$conn['tfc_padding'] = (isset($ph1ent['tfc_bytes']) && is_numericint($ph1ent['tfc_bytes'])) ? $ph1ent['tfc_bytes'] : "mtu";
2576
		}
2577

    
2578
		/* Custom Ports */
2579
		if (isset($ph1ent['ikeport'])) {
2580
			/* For a connection without encapsulation, do not set local_port */
2581
			$conn['remote_port'] = $ph1ent['ikeport'];
2582
		} elseif (isset($ph1ent['nattport'])) {
2583
			/* For an encapsulated connection,
2584
			 * set local_port to the server NAT-T port value or default (4500) */
2585
			$conn['local_port'] = config_get_path('ipsec/port_nat_t', 4500);
2586
			$conn['remote_port'] = $ph1ent['nattport'];
2587
		}
2588

    
2589
		/* Arrays for P2s/children */
2590
		$children = array();
2591
		$remote_ts_spec = array();
2592
		$local_ts_spec = array();
2593
		$reqids = array();
2594
		$has_vti = false;
2595
		$ealgoAHsp2arr = array();
2596
		$ealgoESPsp2arr = array();
2597

    
2598
		foreach ($ph1ent['p2'] as $ph2ent) {
2599
			/* If this entry is disabled, or cannot be configured, skip. */
2600
			if (isset($ph2ent['disabled']) ||
2601
			    (isset($ph2ent['mobile']) &&
2602
			    !config_path_enabled('ipsec/client'))) {
2603
				continue;
2604
			}
2605
			$child = array();
2606
			$local_ts = "";
2607
			$remote_ts = "";
2608
			if (($ph2ent['mode'] == 'tunnel') or ($ph2ent['mode'] == 'tunnel6')) {
2609
				/* Normal tunnel child config */
2610
				$child['mode'] = "tunnel";
2611
				$child['policies'] = "yes";
2612

    
2613
				$localid_type = $ph2ent['localid']['type'];
2614
				$localsubnet_data = ipsec_idinfo_to_cidr($ph2ent['localid'], false, $ph2ent['mode']);
2615

    
2616
				/* Do not print localid in some cases, such as a pure-psk or psk/xauth single phase2 mobile tunnel */
2617
				if (($localid_type == "none" || $localid_type == "mobile") &&
2618
				    isset($ph1ent['mobile']) && (ipsec_get_number_of_phase2($ikeid) == 1)) {
2619
					$local_spec = '0.0.0.0/0,::/0';
2620
				} else {
2621
					if ($localid_type != "address") {
2622
						$localid_type = "subnet";
2623
					}
2624
					// Don't let an empty subnet into config, it can cause parse errors. Ticket #2201.
2625
					if (!is_ipaddr($localsubnet_data) && !is_subnet($localsubnet_data) && ($localsubnet_data != "0.0.0.0/0")) {
2626
						log_error("Invalid IPsec Phase 2 \"{$ph2ent['descr']}\" - {$ph2ent['localid']['type']} has no subnet.");
2627
						continue;
2628
					}
2629
					if (!empty($ph2ent['natlocalid'])) {
2630
						$natlocalsubnet_data = ipsec_idinfo_to_cidr($ph2ent['natlocalid'], false, $ph2ent['mode']);
2631
						if ($ph2ent['natlocalid']['type'] != "address") {
2632
							if (is_subnet($natlocalsubnet_data)) {
2633
								$localsubnet_data = "{$natlocalsubnet_data}|{$localsubnet_data}";
2634
							}
2635
						} else {
2636
							if (is_ipaddr($natlocalsubnet_data)) {
2637
								$localsubnet_data = "{$natlocalsubnet_data}|{$localsubnet_data}";
2638
							}
2639
						}
2640
						$natfilterrules = true;
2641
					}
2642
				}
2643

    
2644
				$local_ts = $localsubnet_data;
2645

    
2646
				if (!isset($ph2ent['mobile'])) {
2647
					$remote_ts = ipsec_idinfo_to_cidr($ph2ent['remoteid'], false, $ph2ent['mode']);
2648
				} elseif (!empty(config_get_path('ipsec/client/pool_address'))) {
2649
					$remote_ts = config_get_path('ipsec/client/pool_address') . '/' . config_get_path('ipsec/client/pool_netbits');
2650
				}
2651

    
2652
			} elseif ($ph2ent['mode'] == 'vti') {
2653
				/* VTI-specific child config */
2654
				$child['policies'] = "no";
2655
				/* VTI cannot use trap policies, so start automatically instead */
2656
				$start_action = 'start';
2657
				if ($carpbackup) {
2658
					$start_action = "none";
2659
				}
2660
				if ($child_params['dpd_action'] == "trap") {
2661
					$child_params['dpd_action'] = "restart";
2662
				}
2663
				$localid_type = $ph2ent['localid']['type'];
2664
				$localsubnet_data = ipsec_idinfo_to_cidr($ph2ent['localid'], false, $ph2ent['mode']);
2665
				$local_ts = $localsubnet_data;
2666
				$remote_ts = ipsec_idinfo_to_cidr($ph2ent['remoteid'], false, $ph2ent['mode']);
2667
				$has_vti = true;
2668
			} else {
2669
				/* Transport mode child config */
2670
				$child['mode'] = "transport";
2671
				$child['policies'] = "yes";
2672

    
2673
				if ((($ph1ent['authentication_method'] == "xauth_psk_server") ||
2674
				    ($ph1ent['authentication_method'] == "pre_shared_key")) &&
2675
				    isset($ph1ent['mobile'])) {
2676
					$local_spec = "0.0.0.0/0,::/0";
2677
				} else {
2678
					$local_ts = ipsec_get_phase1_src($ph1ent);
2679
				}
2680

    
2681
				if (!isset($ph2ent['mobile'])) {
2682
					$remote_ts = $remote_spec;
2683
				}
2684
			}
2685

    
2686
			if (!empty($local_ts)) {
2687
				$local_ts_spec[] = $local_ts;
2688
			}
2689
			if (!empty($remote_ts)) {
2690
				$remote_ts_spec[] = $remote_ts;
2691
			}
2692

    
2693
			/* If a PFS group is configured on the Mobile Clients tab,
2694
			 * and this is a mobile P2, use it here. */
2695
			if (config_get_path('ipsec/client/pfs_group') &&
2696
			    isset($ph2ent['mobile'])) {
2697
				$ph2ent['pfsgroup'] = config_get_path('ipsec/client/pfs_group');
2698
			}
2699

    
2700
			$reqids[] = $ph2ent['reqid'];
2701

    
2702
			if (!empty($ph2ent['lifetime'])) {
2703
				$child['life_time'] = ipsec_get_life_time($ph2ent) . "s";
2704
				/* Rekey at 90% of lifetime */
2705
				$child['rekey_time'] = ipsec_get_rekey_time($ph2ent) . "s";
2706
				/* Random rekey fuzz time */
2707
				$child['rand_time'] = ipsec_get_rand_time($ph2ent) . "s";
2708
			}
2709

    
2710
			/* Set Child SA Start Action based on user preference, except
2711
			 * for invalid combinations such as 'trap' on VTI */
2712
			if (!isset($ph2ent['mobile']) && !$carpbackup &&
2713
			    !(($ph2ent['mode'] == 'vti') && ($ph1ent['startaction'] == 'trap'))) {
2714
				$start_action = !empty($ph1ent['startaction']) ? $ph1ent['startaction'] : $start_action;
2715
			}
2716
			$child['start_action'] = $start_action;
2717

    
2718
			/* Setup child SA proposals */
2719
			$proposal = array();
2720
			$ph2ent_ealgos = array();
2721
			$ph2ent_ealgos_aead = array();
2722
			if ($ph2ent['protocol'] == 'esp') {
2723
				if (is_array($ph2ent['encryption-algorithm-option'])) {
2724
					foreach ($ph2ent['encryption-algorithm-option'] as $ealg) {
2725
						if (strpos($ealg['name'], "gcm")) {
2726
							/* put AEAD ciphers on top, 
2727
							*  see https://redmine.pfsense.org/issues/11078 */
2728
							$ph2ent_ealgos_aead[] = $ealg;
2729
						} else {
2730
							$ph2ent_ealgos[] = $ealg;
2731
						}
2732
					}
2733
					$ph2ent_ealgos = array_merge(array_reverse($ph2ent_ealgos_aead), $ph2ent_ealgos);
2734
					foreach ($ph2ent_ealgos as $ealg) {
2735
						$ealg_id = $ealg['name'];
2736
						$ealg_kl = $ealg['keylen'];
2737

    
2738
						if (!empty($ealg_kl) && $ealg_kl == "auto") {
2739
							if (empty($p2_ealgos) || !is_array($p2_ealgos)) {
2740
								require_once("ipsec.inc");
2741
							}
2742
							$key_hi = $p2_ealgos[$ealg_id]['keysel']['hi'];
2743
							$key_lo = $p2_ealgos[$ealg_id]['keysel']['lo'];
2744
							$key_step = $p2_ealgos[$ealg_id]['keysel']['step'];
2745
							/* XXX: in some cases where include ordering is suspect these variables
2746
							 * are somehow 0 and we enter this loop forever and timeout after 900
2747
							 * seconds wrecking bootup */
2748
							if ($key_hi != 0 and $key_lo != 0 and $key_step != 0) {
2749
								for ($keylen = $key_hi; $keylen >= $key_lo; $keylen -= $key_step) {
2750
									$proposal[] = ipsec_setup_proposal_entry($ph2ent, $ealgoESPsp2arr, $ealg_id, $keylen);
2751
								}
2752
							}
2753
						} else {
2754
							$proposal[] = ipsec_setup_proposal_entry($ph2ent, $ealgoESPsp2arr, $ealg_id, $ealg_kl);
2755
						}
2756
					}
2757
				}
2758
			} else if ($ph2ent['protocol'] == 'ah') {
2759
				$proposal[] = ipsec_setup_proposal_entry($ph2ent, $ealgoAHsp2arr, '', '');
2760
			}
2761

    
2762
			/* Not mobile, and IKEv1 or Split Connections active */
2763
			if (!isset($ph1ent['mobile']) && (($ikeversion == 1) || isset($ph1ent['splitconn']))) {
2764
				if (!empty($remote_ts)) {
2765
					/* Setup child sub-connections using unique names */
2766
					$subconname = ipsec_conid($ph1ent, $ph2ent);
2767
					$children[$subconname] = $child;
2768

    
2769
					$descr = "# P2 (reqid {$ph2ent['reqid']})";
2770
					if (!empty($ph2ent['descr'])) {
2771
						$descr .= ": {$ph2ent['descr']}";
2772
					}
2773
					array_unshift($children[$subconname], $descr);
2774
					$children[$subconname]['local_ts'] = $local_ts;
2775
					$children[$subconname]['remote_ts'] = $remote_ts;
2776
					if ($has_vti) {
2777
						ipsec_setup_vtireq($children[$subconname], $ipsec_vti_cleanup_ifs, $ph1ent, $ph2ent['reqid']);
2778
					}
2779
					if (!empty($ph2ent['protocol']) && !empty($proposal)) {
2780
						$children[$subconname][$ph2ent['protocol'] . '_proposals'] = implode(',', $proposal);
2781
					}
2782
				} else {
2783
					log_error(sprintf(gettext("No phase2 specifications for tunnel with ikeid = %s"), $ikeid));
2784
				}
2785
			} else {
2786
				/* TODO: Fix this to nicely merge all P2 params for single child case */
2787
				if (!is_array($children[$cname])) {
2788
					$children[$cname] = array();
2789
				}
2790
				$children[$cname] = array_merge($children[$cname], $child);
2791

    
2792
				$descr = "# P2 (reqid {$ph2ent['reqid']})";
2793
				if (!empty($ph2ent['descr'])) {
2794
					$descr .= ": {$ph2ent['descr']}";
2795
				}
2796
				array_unshift($children[$cname], $descr);
2797
			}
2798
		}
2799

    
2800
		$conn['local_addrs'] = $local_spec;
2801
		$conn['remote_addrs'] = $remote_spec;
2802

    
2803
		$pools = array();
2804
		if (isset($ph1ent['mobile'])) {
2805
			if (($ph1ent['authentication_method'] == 'eap-radius') &&
2806
			    empty(config_get_path('ipsec/client/pool_address')) &&
2807
			    empty(config_get_path('ipsec/client/pool_address_v6'))) {
2808
				$pools[] = "radius";
2809
			} else {
2810
				if (!empty(config_get_path('ipsec/client/pool_address'))) {
2811
					$pools[] = "mobile-pool-v4";
2812
				}
2813
				if (!empty(config_get_path('ipsec/client/pool_address_v6'))) {
2814
					$pools[] = "mobile-pool-v6";
2815
				}
2816
				if (config_path_enabled('ipsec/client', 'radius_ip_priority_enable')) {
2817
					$pools[] = "radius";
2818
				}
2819
			}
2820
		}
2821
		if (!empty($pools)) {
2822
			$conn['pools'] = implode(', ', $pools);
2823
		}
2824

    
2825
		/* For IKEv2 without Split Connections, setup combined sets of
2826
		 * local/remote traffic selectors and proposals */
2827
		if (!(!isset($ph1ent['mobile']) && (($ikeversion == 1) || isset($ph1ent['splitconn'])))) {
2828
			if (!isset($ph1ent['mobile']) && !empty($remote_ts_spec)) {
2829
				$children[$cname]['remote_ts'] = implode(",", $remote_ts_spec);
2830
			}
2831
			if (!empty($local_ts_spec)) {
2832
				$children[$cname]['local_ts'] = implode(",", $local_ts_spec);
2833
			}
2834
			if ($has_vti) {
2835
				ipsec_setup_vtireq($children[$cname], $ipsec_vti_cleanup_ifs, $ph1ent);
2836
			}
2837
			if (!empty($ealgoAHsp2arr)) {
2838
				$halgos = array();
2839
				$halgos_sha = array();
2840
				foreach ($ealgoAHsp2arr as $halg) {
2841
					if (strstr($halg, "sha")) {
2842
						/* put SHA ciphers on top, 
2843
						*  see https://redmine.pfsense.org/issues/12323 */
2844
						$halgos_sha[] = $halg;
2845
					} else {
2846
						$halgos[] = $halg;
2847
					}
2848
				}
2849
				$ealgoAHsp2arr = array_merge(array_reverse($halgos_sha), $halgos);
2850
				$children[$cname]['ah_proposals'] = implode(',', array_unique($ealgoAHsp2arr));
2851
			}
2852
			if (!empty($ealgoESPsp2arr)) {
2853
				$children[$cname]['esp_proposals'] = implode(',', array_unique($ealgoESPsp2arr));
2854
			}
2855
		}
2856

    
2857
		/* Setup connection authentication */
2858
		ipsec_setup_authentication($ph1ent, $conn);
2859

    
2860
		/* Add children to this connection, including default child parameters */
2861
		if (count($children)) {
2862
			foreach($children as $name => $child) {
2863
				$conn['children'][$name] = array_merge($child, $child_params);
2864
			}
2865
		}
2866
		unset ($ph2ent);
2867
	}
2868

    
2869
	return;
2870
}
2871

    
2872
/****f* ipsec/ipsec_setup_secrets
2873
 * NAME
2874
 *   ipsec_setup_secrets - Setup swanctl authentication secrets
2875
 * INPUTS
2876
 *   None
2877
 * RESULT
2878
 *   Returns a swanctl array containing secrets (PSKs, certs, etc) and writes out
2879
 *   necessary CA, CRL, and certificate data.
2880
 ******/
2881
function ipsec_setup_secrets() {
2882
	global $ipsec_swanctl_dirs, $ipseccfg, $rgmap, $scconf;
2883

    
2884
	$suffix = 0;
2885

    
2886
	$vpncas = array();
2887
	foreach (config_get_path('ipsec/phase1', []) as $ph1ent) {
2888
		if (isset($ph1ent['disabled'])) {
2889
			continue;
2890
		}
2891

    
2892
		if (isset($ph1ent['mobile'])) {
2893
			if ($ph1ent['authentication_method'] == 'pre_shared_key') {
2894
				$mobilepsk = true;
2895
			} elseif ($ph1ent['authentication_method'] == 'eap-mschapv2') {
2896
				$mobileeap = true;
2897
			}
2898
		}
2899

    
2900
		if (strstr($ph1ent['authentication_method'], 'cert') ||
2901
		    in_array($ph1ent['authentication_method'], array('eap-mschapv2', 'eap-tls', 'eap-radius'))) {
2902
			/* Write certificate and private key, point to private key */
2903
			$certline = '';
2904

    
2905
			$ikeid = $ph1ent['ikeid'];
2906
			$cert = lookup_cert($ph1ent['certref']);
2907
			$cert = $cert['item'];
2908

    
2909
			if (!$cert) {
2910
				log_error(sprintf(gettext("Error: Invalid phase1 certificate reference for %s"), $ph1ent['name']));
2911
				continue;
2912
			}
2913

    
2914
			/* add signing CA cert chain of server cert
2915
			 * to the list of CAs to write
2916
			 */
2917
			$cachain = ca_chain_array($cert);
2918
			if ($cachain && is_array($cachain)) {
2919
				foreach ($cachain as $cacrt) {
2920
					$vpncas[$cacrt['refid']] = $cacrt;
2921
				}
2922
			}
2923

    
2924
			@chmod($ipsec_swanctl_dirs['certpath'], 0600);
2925

    
2926
			$ph1keyfile = "{$ipsec_swanctl_dirs['keypath']}/cert-{$ikeid}.key";
2927
			if (!file_put_contents($ph1keyfile, base64_decode($cert['prv']))) {
2928
				log_error(sprintf(gettext("Error: Cannot write phase1 key file for %s"), $ph1ent['name']));
2929
				continue;
2930
			}
2931
			@chmod($ph1keyfile, 0600);
2932

    
2933
			$ph1certfile = "{$ipsec_swanctl_dirs['certpath']}/cert-{$ikeid}.crt";
2934
			if (!file_put_contents($ph1certfile, base64_decode($cert['crt']))) {
2935
				log_error(sprintf(gettext("Error: Cannot write phase1 certificate file for %s"), $ph1ent['name']));
2936
				@unlink($ph1keyfile);
2937
				continue;
2938
			}
2939
			@chmod($ph1certfile, 0600);
2940

    
2941
			$scconf['secrets']['private-' . $suffix++] = array('file' => $ph1keyfile);
2942
		} else if (strstr($ph1ent['authentication_method'], 'pkcs11')) {
2943
			$p11_id = array();
2944
			$output = shell_exec('/usr/local/bin/pkcs15-tool -c');
2945
			preg_match_all('/ID\s+: (.*)/', $output, $p11_id);
2946
			if (!empty($ph1ent['pkcs11certref']) && in_array($ph1ent['pkcs11certref'], $p11_id[1])) {
2947
				$scconf['secrets']['token-' . $suffix++] = array(
2948
					'handle' => $ph1ent['pkcs11certref'],
2949
					'pin' => $ph1ent['pkcs11pin'],
2950
				);
2951
			} else {
2952
				log_error(sprintf(gettext("Error: Invalid phase1 PKCS#11 certificate reference or PKCS#11 is not present for %s"), $ph1ent['name']));
2953
				continue;
2954
			}
2955
		} else {
2956
			/* Setup pre-shared keys */
2957
			list($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local', array());
2958
			list($peerid_type, $peerid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap);
2959
			$myid = trim($myid_data);
2960

    
2961
			if (empty($peerid_data)) {
2962
				continue;
2963
			}
2964

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

    
2968
			if (!empty($ph1ent['pre-shared-key'])) {
2969
				$scconf['secrets']['ike-' . $suffix++] = array(
2970
					'secret' => '0s' . base64_encode(trim($ph1ent['pre-shared-key'])),
2971
					'id-0' => $myid,
2972
					'id-1' => $peerid,
2973
				);
2974
				if (isset($ph1ent['mobile'])) {
2975
					$scconf['secrets']['ike-' . $suffix++] = array(
2976
						'secret' => '0s' . base64_encode(trim($ph1ent['pre-shared-key'])),
2977
						'id-0' => $myid,
2978
						'id-1' => '%any',
2979
					);
2980
					$scconf['secrets']['ike-' . $suffix++] = array(
2981
						'secret' => '0s' . base64_encode(trim($ph1ent['pre-shared-key'])),
2982
					);
2983
				}
2984
			}
2985
		}
2986

    
2987
		/* if the client authenticates with a cert add the
2988
		 * client cert CA chain to the list of CAs to write
2989
		 */
2990
		if (in_array($ph1ent['authentication_method'],
2991
		    array('cert', 'eap-tls', 'xauth_cert_server', 'pkcs11'))) {
2992
			if (!empty($ph1ent['caref']) && !array_key_exists($ph1ent['caref'], $vpncas)) {
2993
				$thisca = lookup_ca($ph1ent['caref']);
2994
				$thisca = $thisca['item'];
2995
				$vpncas[$ph1ent['caref']] = $thisca;
2996
				/* follow chain up to root */
2997
				$cachain = ca_chain_array($thisca);
2998
				if ($cachain and is_array($cachain)) {
2999
					foreach ($cachain as $cacrt) {
3000
						$vpncas[$cacrt['refid']] = $cacrt;
3001
					}
3002
				}
3003
			}
3004
		}
3005
	}
3006

    
3007
	/* Write the required CAs */
3008
	foreach ($vpncas as $carefid => $cadata) {
3009
		$cacrt = base64_decode($cadata['crt']);
3010
		$cacrtattrs = openssl_x509_parse($cacrt);
3011
		if (!is_array($cacrtattrs) || !isset($cacrtattrs['hash'])) {
3012
			log_error(sprintf(gettext("Error: Invalid certificate hash info for %s"), $cadata['descr']));
3013
			continue;
3014
		}
3015
		$cafilename = "{$ipsec_swanctl_dirs['capath']}/{$cacrtattrs['hash']}.0";
3016
		if (!@file_put_contents($cafilename, $cacrt)) {
3017
				log_error(sprintf(gettext("Error: Cannot write IPsec CA file for %s"), $cadata['descr']));
3018
				continue;
3019
		}
3020
	}
3021

    
3022
	/* write out CRL files */
3023
	if (!empty($vpncas)) {
3024
		foreach (config_get_path('crl', []) as $crl) {
3025
			if (!isset($crl['text'])) {
3026
				log_error(sprintf(gettext("Warning: Missing CRL data for %s"), $crl['descr']));
3027
				continue;
3028
			}
3029
			$fpath = "{$ipsec_swanctl_dirs['crlpath']}/{$crl['refid']}.crl";
3030
			if (!@file_put_contents($fpath, base64_decode($crl['text']))) {
3031
				log_error(sprintf(gettext("Error: Cannot write IPsec CRL file for %s"), $crl['descr']));
3032
				continue;
3033
			}
3034
		}
3035
	}
3036

    
3037
	/* Add user PSKs */
3038
	if ($mobilepsk) {
3039
		foreach (config_get_path('system/user', []) as $user) {
3040
			if (!empty($user['ipsecpsk'])) {
3041
				$scconf['secrets']['ike-' . $suffix++] = array(
3042
					'secret' => '0s' . base64_encode(trim($user['ipsecpsk'])),
3043
					'id-0' => $myid,
3044
					'id-1' => $user['name'],
3045
				);
3046
			}
3047
		}
3048
		unset($user);
3049
	}
3050

    
3051
	/* add PSKs/EAPs for mobile clients */
3052
	if (is_array($ipseccfg['mobilekey'])) {
3053
		foreach ($ipseccfg['mobilekey'] as $key) {
3054
			if (($mobileeap && ($key['type'] == 'EAP')) ||
3055
			    ($key['type'] == 'PSK')) {
3056
				if (($key['ident'] == 'allusers') ||
3057
				    ($key['ident'] == 'any')) {
3058
					$key['ident'] = '%any';
3059
				}
3060
				$userkeyprefix = (strtolower($key['type']) == 'eap') ? 'eap' : 'ike';
3061
				$scconf['secrets'][$userkeyprefix . '-' . $suffix++] = array(
3062
					'secret' => '0s' . base64_encode(trim($key['pre-shared-key'])),
3063
					'id-0' => $key['ident'],
3064
				);
3065
			}
3066
		}
3067
		unset($key);
3068
	}
3069
	return;
3070
}
3071

    
3072
/****f* ipsec/ipsec_configure
3073
 * NAME
3074
 *   ipsec_configure - Configure IPsec
3075
 * INPUTS
3076
 *   $restart: Boolean (default false), whether or not to restart the IPsec
3077
 *             daemons.
3078
 * RESULT
3079
 *   IPsec-related configuration files are written, daemon is started or stopped
3080
 *   appropriately.
3081
 ******/
3082
function ipsec_configure($restart = false, $filterdns_restart = false) {
3083
	global $aggressive_mode_psk,
3084
		$filterdns_list, $g, $ifacesuse, $ipsec_idhandling, $ipsec_log_cats,
3085
		$ipsec_log_sevs, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs,
3086
		$ipseccfg, $mobile_ipsec_auth, $natfilterrules, $p1_ealgos,
3087
		$p2_ealgos, $rgmap, $sa, $sn, $scconf, $tunnels, $mobile_configured,
3088
		$ipsec_vti_cleanup_ifs, $conn_defaults, $pool_addrs;
3089

    
3090
	/* service may have been enabled, disabled, or otherwise changed in a
3091
	 *way requiring rule updates */
3092
	filter_configure();
3093

    
3094
	if (!ipsec_enabled()) {
3095
		/* IPsec is disabled */
3096
		/* Stop charon */
3097
		mwexec("/usr/local/sbin/strongswanrc stop");
3098
		/* Stop dynamic monitoring */
3099
		killbypid("{$g['varrun_path']}/filterdns-ipsec.pid");
3100
		/* Wait for process to die */
3101
		sleep(2);
3102
		/* Stop PC/SC Smart Card Services */
3103
		killbyname("pcscd");
3104
		/* Shutdown enc0 interface*/
3105
		mwexec("/sbin/ifconfig enc0 down");
3106
		ipsec_gre_default_mtu(); 
3107
		return 0;
3108
	} else {
3109
		/* Startup enc0 interface */
3110
		mwexec("/sbin/ifconfig enc0 up");
3111
		/* PC/SC daemon must be started before strongswan */
3112
		if (config_path_enabled('ipsec', 'pkcs11support') && !isvalidproc("pcscd")) {
3113
			if (is_platform_booting()) {
3114
				echo gettext("Starting PC/SC Smart Card Services...");
3115
			}
3116
			mwexec_bg("/usr/local/sbin/pcscd");
3117
			if (is_platform_booting()) {
3118
				echo gettext("done.\n");
3119
			}
3120
		} elseif (!config_path_enabled('ipsec', 'pkcs11support') && isvalidproc("pcscd")) {
3121
			/* strongswan must be stopped first, otherwise it will flood the logs */
3122
			mwexec("/usr/local/sbin/strongswanrc stop");
3123
			killbyname("pcscd");
3124
		}
3125
	}
3126

    
3127
	if (is_platform_booting()) {
3128
		echo gettext("Configuring IPsec VPN... ");
3129
	}
3130

    
3131
	$ipsecstartlock = lock('ipsec', LOCK_EX);
3132

    
3133
	/* Prepare automatic ping_hosts.sh data */
3134
	unlink_if_exists("{$g['vardb_path']}/ipsecpinghosts");
3135
	touch("{$g['vardb_path']}/ipsecpinghosts");
3136
	$ipsecpinghostsactive = false;
3137

    
3138
	/* Populate convenience variables */
3139
	$syscfg = config_get_path('system');
3140
	init_config_arr(array('ipsec', 'phase1'));
3141
	$ipseccfg = config_get_path('ipsec');
3142
	$a_phase1 = config_get_path('ipsec/phase1');
3143
	init_config_arr(array('ipsec', 'phase2'));
3144
	$a_phase2 = config_get_path('ipsec/phase2');
3145

    
3146
	$mobile_configured = false;
3147

    
3148

    
3149
	/* Setup a single structured array to process, to avoid repeatedly
3150
	 * looping through the arrays to locate entries later. */
3151
	$tunnels = array();
3152
	foreach ($a_phase1 as $p1) {
3153
		if (empty($p1)) {
3154
			continue;
3155
		}
3156
		if (isset($p1['mobile']) && !isset($p1['disabled'])) {
3157
			$mobile_configured = true;
3158
		}
3159
		$tunnels[$p1['ikeid']] = $p1;
3160
		$tunnels[$p1['ikeid']]['p2'] = array();
3161
	}
3162
	foreach ($a_phase2 as $p2) {
3163
		$tunnels[$p2['ikeid']]['p2'][] = $p2;
3164
	}
3165

    
3166
	$ipsec_vti_cleanup_ifs = array();
3167
	$rgmap = array();
3168
	$filterdns_list = array();
3169
	$aggressive_mode_psk = false;
3170
	$mobile_ipsec_auth = "";
3171
	$ifacesuse = array();
3172
	$natfilterrules = false;
3173

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

    
3177
	/* Build a list of all IPsec interfaces configured on the firewall at the OS level */
3178
	refresh_gateways(); // make sure automatic VTI gateways are created
3179
	foreach (get_interface_arr() as $thisif) {
3180
		if (substr($thisif, 0, 5) == "ipsec") {
3181
			$ipsec_vti_cleanup_ifs[] = $thisif;
3182
		}
3183
	}
3184
	/* Create directory structure for IPsec */
3185
	ipsec_create_dirs();
3186
	/* Setup gateways and interfaces */
3187
	ipsec_setup_gwifs();
3188
	/* Setup and write strongswan.conf */
3189
	ipsec_setup_strongswan();
3190
	/* Start Global Connection default values */
3191
	$conn_defaults = array();
3192
	/* Fragmentation is on for everyone */
3193
	$conn_defaults['fragmentation'] = "yes";
3194
	/* Default to 'replace' for unique IDs (was 'yes' in ipsec.conf previously) */
3195
	$conn_defaults['unique'] = 'replace';
3196
	/* If the configuration has a valid alternate value for unique ID handling,
3197
	 * use it instead. */
3198
	if (array_key_exists(config_get_path('ipsec/uniqueids', ""), $ipsec_idhandling)) {
3199
		$conn_defaults['unique'] = config_get_path('ipsec/uniqueids');
3200
	}
3201
	/* Disable ipcomp for now. redmine #6167
3202
	if (isset($config['ipsec']['compression'])) {
3203
		$conn_defaults['compress'] = "yes";
3204
	}
3205
	set_single_sysctl('net.inet.ipcomp.ipcomp_enable', (isset($config['ipsec']['compression'])) ? 1 : 0);
3206
	*/
3207
	/* End Global Connection Defaults */
3208

    
3209
	/* Start swanctl configuration (scconf) */
3210
	$scconf = array();
3211
	$scconf[] = "# This file is automatically generated. Do not edit";
3212
	$scconf['connections'] = array();
3213
	/* Setup IPsec bypass */
3214
	ipsec_setup_bypass();
3215
	/* Setup connections */
3216
	ipsec_setup_tunnels();
3217
	$scconf['pools'] = array();
3218
	if ($mobile_configured) {
3219
		/* Setup mobile address pools */
3220
		ipsec_setup_pools();
3221
		/* Setup per-user pools */
3222
		ipsec_setup_userpools();
3223
	}
3224
	/* Setup secret data */
3225
	$scconf['secrets'] = array();
3226
	ipsec_setup_secrets();
3227

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

    
3230
	/* Clean up unused VTI interfaces */
3231
	foreach ($ipsec_vti_cleanup_ifs as $cleanif) {
3232
		if (does_interface_exist($cleanif)) {
3233
			mwexec("/sbin/ifconfig " . escapeshellarg($cleanif) . " destroy", false);
3234
		}
3235
	}
3236

    
3237
	/* set default MTU to 1400 for GRE over IPsec, otherwise to 1476 */
3238
	ipsec_gre_default_mtu(); 
3239

    
3240
	/* Manage process */
3241
	if ($restart === true) {
3242
		mwexec_bg("/usr/local/sbin/strongswanrc restart", false);
3243
	} else {
3244
		if (isvalidpid("{$g['varrun_path']}/charon.pid")) {
3245
			mwexec_bg("/usr/local/sbin/strongswanrc reload", false);
3246
		} else {
3247
			mwexec_bg("/usr/local/sbin/strongswanrc start", false);
3248
		}
3249
	}
3250

    
3251
	// Run ping_hosts.sh once if it's enabled to avoid wait for minicron
3252
	if ($ipsecpinghostsactive) {
3253
		mwexec_bg("/usr/local/bin/ping_hosts.sh");
3254
	}
3255

    
3256
	if ($natfilterrules == true) {
3257
		filter_configure();
3258
	}
3259
	/* start filterdns, if necessary */
3260
	if (count($filterdns_list) > 0) {
3261
		$interval = 60;
3262
		if (!empty($ipseccfg['dns-interval']) && is_numeric($ipseccfg['dns-interval'])) {
3263
			$interval = $ipseccfg['dns-interval'];
3264
		}
3265

    
3266
		$hostnames = "";
3267
		array_unique($filterdns_list);
3268
		foreach ($filterdns_list as $hostname) {
3269
			$hostnames .= "cmd {$hostname} '/usr/local/sbin/pfSctl -c \"service reload ipsecdns\"'\n";
3270
		}
3271
		file_put_contents("{$g['varetc_path']}/ipsec/filterdns-ipsec.hosts", $hostnames);
3272
		unset($hostnames);
3273

    
3274
		if (isvalidpid("{$g['varrun_path']}/filterdns-ipsec.pid")) {
3275
			if ($filterdns_restart) {
3276
				killbypid("{$g['varrun_path']}/filterdns-ipsec.pid");
3277
				sleep(2);
3278
				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");
3279
			} else {
3280
				sigkillbypid("{$g['varrun_path']}/filterdns-ipsec.pid", "HUP");
3281
			}
3282
		} else {
3283
			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");
3284
		}
3285
	} else {
3286
		killbypid("{$g['varrun_path']}/filterdns-ipsec.pid");
3287
		@unlink("{$g['varrun_path']}/filterdns-ipsec.pid");
3288
	}
3289

    
3290
	if (is_platform_booting()) {
3291
		echo "done\n";
3292
	}
3293

    
3294
	unlock($ipsecstartlock);
3295
	return count($filterdns_list);
3296
}
3297

    
3298
function ipsec_gre_default_mtu() {
3299
	foreach (config_get_path('interfaces', []) as $if => $ifdetail) { 
3300
		if (interface_is_type($ifdetail['if'], 'gre') && !isset($ifdetail['mtu'])) {
3301
			if (is_greipsec($ifdetail['if'])) {
3302
				set_interface_mtu($ifdetail['if'], 1400);
3303
			} else {
3304
				set_interface_mtu($ifdetail['if'], 1476);
3305
			}
3306
		}
3307
	}
3308
}
3309

    
3310
/* Return the larger of derived SA rekey time and reauth time */
3311
function ipsec_get_renewmax($entry) {
3312
	if (empty($entry) || !is_array($entry)) {
3313
		return 0;
3314
	}
3315
	return max(ipsec_get_rekey_time($entry), ipsec_get_reauth_time($entry));
3316
}
3317

    
3318
/* Determine the life time of an SA entry (Hard upper total time limit for SA before it is removed) */
3319
function ipsec_get_life_time($entry) {
3320
	if (empty($entry) || !is_array($entry)) {
3321
		return 0;
3322
	}
3323
	/* Use a hardcoded value if present in the configuration */
3324
	if ($entry['lifetime'] > 0) {
3325
		return $entry['lifetime'];
3326
	}
3327
	/* If rekey or reauth are enabled, attempt to derive a lifetime from one of those */
3328
	$renewmax = ipsec_get_renewmax($entry);
3329
	if ($renewmax > 0) {
3330
		return intval($renewmax / 0.9);
3331
	}
3332
	/* To reach here, rekey_time and lifetime are both 0 which is invalid
3333
	 * Default to 16000 for p1 and 4000 for p2 */
3334
	if ($entry['iketype']) {
3335
		return 16000;
3336
	} else {
3337
		return 4000;
3338
	}
3339
}
3340

    
3341
/* Determine the rekey time of an SA entry (Time at which to rekey IKEv2 or Child SA entries) */
3342
function ipsec_get_rekey_time($entry) {
3343
	if (empty($entry) || !is_array($entry)) {
3344
		return 0;
3345
	}
3346
	/* Use a hardcoded value if present in the configuration */
3347
	if (strlen($entry['rekey_time'])) {
3348
		/* Check via strlen since 0 is a valid value */
3349
		return $entry['rekey_time'];
3350
	} elseif ($entry['lifetime'] > 0) {
3351
		/* If rekey_time is empty and lifetime is non-zero, use 90% lifetime */
3352
		return intval($entry['lifetime'] * 0.9);
3353
	}
3354
	/* To reach here, rekey_time and lifetime are empty
3355
	 * Default to 14400 for p1 and 3600 for p2 */
3356
	if ($entry['iketype']) {
3357
		return 14400;
3358
	} else {
3359
		return 3600;
3360
	}
3361
}
3362

    
3363
/* Determine the reauth time of an SA entry (IKE SA tear-down/reauthenticate) */
3364
function ipsec_get_reauth_time($entry) {
3365
	if (empty($entry) || !is_array($entry)) {
3366
		return 0;
3367
	}
3368
	/* Use a hardcoded value if present in the configuration */
3369
	if (strlen($entry['reauth_time'])) {
3370
		/* Check via strlen since 0 is a valid value */
3371
		return $entry['reauth_time'];
3372
	} elseif ($entry['lifetime'] > 0) {
3373
		/* If reauth_time is empty and lifetime is non-zero,
3374
		 * use 90% lifetime for IKEv1, disable for IKEv2/auto */
3375
		if ($entry['iketype'] == 'ikev1') {
3376
			return intval($entry['lifetime'] * 0.9);
3377
		} else {
3378
			return 0;
3379
		}
3380
	}
3381
	/* To reach here, rekey_time and lifetime are empty
3382
	 * Default to disabled (0) */
3383
	return 0;
3384
}
3385

    
3386
/* Determine the over time of an SA entry (Hard upper IKE SA time limit, relative to rekey/reauth time) */
3387
function ipsec_get_over_time($entry) {
3388
	if (empty($entry) || !is_array($entry)) {
3389
		return 0;
3390
	}
3391
	/* Automatically derive the value for rand_time */
3392
	$lifetime = ipsec_get_life_time($entry);
3393
	$renewmax = ipsec_get_renewmax($entry);
3394
	if (($lifetime > 0) && ($renewmax > 0)) {
3395
		/* If life time and rekey/reauth time both have values, subtract to get rand time */
3396
		return $lifetime - $renewmax;
3397
	} elseif ($lifetime > 0) {
3398
		/* If only life time has a value, use 10% of that */
3399
		return intval($lifetime * 0.1);
3400
	} elseif ($renewmax > 0) {
3401
		/* If only rekey/reauth time has a value, use 10% of that */
3402
		return intval($renewmax * 0.1);
3403
	}
3404
	/* No value can be determined, default to 0 */
3405
	return 0;
3406
}
3407

    
3408
/* Determine the rand time of an SA entry (random value subtracted from renewal time to prevent collisions) */
3409
function ipsec_get_rand_time($entry) {
3410
	if (empty($entry) || !is_array($entry)) {
3411
		return 0;
3412
	}
3413
	/* Use a hardcoded value if present in the configuration */
3414
	if (strlen($entry['rand_time'])) {
3415
		/* Check via strlen since 0 is a valid value */
3416
		return $entry['rand_time'];
3417
	}
3418
	/* Logic to automatically determine rand time is identical to calculating over time */
3419
	return ipsec_get_over_time($entry);
3420
}
3421

    
3422
/* Delete a list of IPsec P1 and related P2 entries
3423
 * Takes no action if any of the passed items are assigned VTI interfaces
3424
 * Caller must write the config after if the function succeeded.
3425
 */
3426
function delete_p1_and_children($p1list = []) {
3427
	global $input_errors;
3428

    
3429
	if (!is_array($p1list) || !count($p1list)) {
3430
		/* Nothing to do */
3431
		return 0;
3432
	}
3433
	$delp1ids = [];
3434
	$delp2ids = [];
3435

    
3436
	/* Check for relevant P1 and P2 entries to remove */
3437
	foreach ($p1list as $p1idx) {
3438
		$p1_has_vti = false;
3439
		$ikeid = config_get_path('ipsec/phase1/' . $p1idx . '/ikeid');
3440

    
3441
		/* Find relevant P2 entries */
3442
		foreach (config_get_path('ipsec/phase2', []) as $p2idx => $p2) {
3443
			/* Flag and remove invalid entries */
3444
			if (empty($p2) || !is_array($p2)) {
3445
				$delp2ids[] = $p2idx;
3446
				continue;
3447
			}
3448
			if ($p2['ikeid'] == $ikeid) {
3449
				if (is_interface_ipsec_vti_assigned($p2)) {
3450
					/* This entry is active VTI and cannot be deleted! */
3451
					$p1_has_vti = true;
3452
				} else {
3453
					$delp2ids[] = $p2idx;
3454
				}
3455
			}
3456
		}
3457

    
3458
		if ($p1_has_vti) {
3459
			$input_errors[] = gettext("Cannot delete a Phase 1 which contains an active VTI Phase 2 with an interface assigned. Remove the interface assignment before deleting this P1.");
3460
		} else {
3461
			$delp1ids[] = $p1idx;
3462
		}
3463
	}
3464

    
3465
	/* If there are errors, take no actions */
3466
	if (!empty($input_errors)) {
3467
		return 0;
3468
	}
3469

    
3470
	/* Remove P1 entries */
3471
	foreach ($delp1ids as $delp1) {
3472
		/* remove static route if interface is not WAN */
3473
		if (config_get_path('ipsec/phase1/' . $delp1 . '/interface') <> "wan") {
3474
			route_del(config_get_path('ipsec/phase1/' . $delp1 . '/remote-gateway'));
3475
		}
3476
		config_del_path('ipsec/phase1/' . $delp1);
3477
	}
3478
	/* Remove P2 entries */
3479
	foreach ($delp2ids as $dp2idx) {
3480
		config_del_path('ipsec/phase2/' . $dp2idx);
3481
	}
3482

    
3483
	/* Return the total number of entries removed. */
3484
	return count($delp1ids) + count($delp2ids);
3485

    
3486
}
3487

    
3488
?>
(26-26/61)