Project

General

Profile

Download (109 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-2022 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
	global $config;
272

    
273
	if (!isset($config['ipsec']) || !is_array($config['ipsec'])) {
274
		return false;
275
	}
276

    
277
	/* Check if we have at least one phase 1 entry. */
278
	if (!isset($config['ipsec']['phase1']) ||
279
	    !is_array($config['ipsec']['phase1']) ||
280
	    empty($config['ipsec']['phase1'])) {
281
		return false;
282
	}
283
	/* Check if at least one phase 1 entry is enabled. */
284
	foreach ($config['ipsec']['phase1'] as $phase1) {
285
		if (!isset($phase1['disabled'])) {
286
			return true;
287
		}
288
	}
289

    
290
	return false;
291
}
292

    
293
/*
294
 * ikeid management functions
295
 */
296

    
297
function ipsec_ikeid_used($ikeid) {
298
	global $config;
299
	init_config_arr(array('ipsec', 'phase1'));
300
	foreach ($config['ipsec']['phase1'] as $ph1ent) {
301
		if ($ikeid == $ph1ent['ikeid']) {
302
			return true;
303
		}
304
	}
305
	return false;
306
}
307

    
308
function ipsec_ikeid_next() {
309
	global $config;
310
	$ikeid = 1;
311
	init_config_arr(array('ipsec', 'phase1'));
312
	$ipsecikeid = lock('ipsecikeids', LOCK_EX);
313
	$ikeids = array();
314

    
315
	foreach ($config['ipsec']['phase1'] as $ph1) {
316
		$keyids[$ph1['ikeid']] = $ph1['ikeid'];
317
	}
318

    
319
	for ($i = 1; $i < 16000; $i++) {
320
		if (!isset($keyids[$i])) {
321
			$keyid = $i;
322
			break;
323
		}
324
	}
325

    
326
	unlock($ipsecreqid);
327
	return $keyid;
328
}
329

    
330
function ipsec_new_reqid() {
331
	global $config;
332
	init_config_arr(array('ipsec', 'phase2'));
333
	$ipsecreqid = lock('ipsecreqids', LOCK_EX);
334
	$keyids = array();
335
	$keyid = 1;
336

    
337
	foreach ($config['ipsec']['phase2'] as $ph2) {
338
		$keyids[$ph2['reqid']] = $ph2['reqid'];
339
	}
340

    
341
	for ($i = 1; $i < 16000; $i++) {
342
		if (!isset($keyids[$i])) {
343
			$keyid = $i;
344
			break;
345
		}
346
	}
347

    
348
	unlock($ipsecreqid);
349
	return $keyid;
350
}
351

    
352
function ipsec_get_phase1($ikeid) {
353
	global $config;
354
	init_config_arr(array('ipsec', 'phase1'));
355

    
356
	foreach ($config['ipsec']['phase1'] as $p1) {
357
		if ($p1['ikeid'] == $ikeid) {
358
			return $p1;
359
		}
360
	}
361
}
362

    
363
function ipsec_get_p1_descr($ikeid) {
364
	global $config;
365
	init_config_arr(array('ipsec', 'phase1'));
366
	$p1 = ipsec_get_phase1($ikeid);
367
	return (!empty($p1['descr'])) ? $p1['descr'] : '';
368
}
369

    
370
/*
371
 * Return phase1 by reference for a given phase2
372
 */
373
function ipsec_lookup_phase1($p2, &$p1ref) {
374
	global $config;
375
	init_config_arr(array('ipsec', 'phase1'));
376

    
377
	foreach ($config['ipsec']['phase1'] as & $p1) {
378
		if ($p1['ikeid'] == $p2['ikeid']) {
379
			$p1ref = $p1;
380
			return true;
381
		}
382
	}
383

    
384
	return false;
385
}
386

    
387
function ipsec_get_phase2($reqid) {
388
	global $config;
389
	init_config_arr(array('ipsec', 'phase2'));
390

    
391
	foreach ($config['ipsec']['phase2'] as $p2) {
392
		if ($p2['reqid'] == $reqid) {
393
			return $p2;
394
		}
395
	}
396
}
397

    
398
function ipsec_get_phase2_by_ikeid($ikeid) {
399
	global $config;
400
	init_config_arr(array('ipsec', 'phase2'));
401
	$results = array();
402
	foreach ($config['ipsec']['phase2'] as $p2) {
403
		if ($p2['ikeid'] == $ikeid) {
404
			$results[] = $p2;
405
		}
406
	}
407
	return $results;
408
}
409

    
410
function ipsec_get_number_of_phase2($ikeid) {
411
	global $config;
412
	return count(ipsec_get_phase2_by_ikeid($ikeid));
413
}
414

    
415
/* Return a nested hash with P1 indexed by ikeid and P2 indexed by reqid */
416
function ipsec_map_config_by_id() {
417
	global $config;
418
	init_config_arr(array('ipsec', 'phase1'));
419
	init_config_arr(array('ipsec', 'phase2'));
420
	$map = array();
421

    
422
	foreach ($config['ipsec']['phase1'] as $p1) {
423
		$map[$p1['ikeid']] = array();
424
		$map[$p1['ikeid']]['p1'] = $p1;
425
	}
426
	foreach ($config['ipsec']['phase2'] as $p2) {
427
		if (!is_array($map[$p2['ikeid']]['p2'])) {
428
			$map[$p2['ikeid']]['p2'] = array();
429
		}
430
		$map[$p2['ikeid']]['p2'][$p2['reqid']] = $p2;
431
	}
432

    
433
	return $map;
434
}
435

    
436
function ipsec_conid($p1 = null, $p2 = null) {
437
	global $ipsec_conid_prefix, $ipsec_conid_separator;
438

    
439
	$conid = NULL;
440
	$have_p1 = (!empty($p1) && is_array($p1));
441
	$have_p2 = (!empty($p2) && is_array($p2));
442

    
443
	if (!$have_p1 && $have_p2) {
444
		$p1 = ipsec_get_phase1($p2['ikeid']);
445
	}
446

    
447
	if (isset($p1['mobile'])) {
448
		return "{$ipsec_conid_prefix}-mobile";
449
	}
450

    
451
	$conid = "{$ipsec_conid_prefix}{$p1['ikeid']}";
452

    
453
	if ($have_p2) {
454
		/* IKEv1 or IKEv2+Split Connections use separate P2 entries
455
		 * so add reqid to ID */
456
		if (($p1['iketype'] != 'ikev2') || isset($p1['splitconn'])) {
457
			$conid .= "{$ipsec_conid_separator}{$p2['reqid']}";
458
		}
459
	}
460

    
461
	return $conid;
462
}
463

    
464
function ipsec_id_by_conid($conid) {
465
	global $config, $ipsec_conid_prefix, $ipsec_conid_separator;
466

    
467
	if ($conid == "{$ipsec_conid_prefix}-mobile") {
468
		init_config_arr(array('ipsec', 'phase1'));
469
		foreach ($config['ipsec']['phase1'] as $p1) {
470
			if (isset($p1['mobile'])) {
471
				$conid = "{$ipsec_conid_prefix}{$p1['ikeid']}";
472
			}
473
		}
474
	}
475

    
476
	if (substr($conid, 0, strlen($ipsec_conid_prefix)) == $ipsec_conid_prefix) {
477
		$conid = substr($conid, strlen($ipsec_conid_prefix));
478
	}
479
	$parts = explode($ipsec_conid_separator, $conid);
480
	if (count($parts) == 1) {
481
		$parts[1] = NULL;
482
	}
483
	return array($parts[0], $parts[1]);
484
}
485

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

    
499
	/* Fetch gateway status if not passed */
500
	if (!is_array($gateways_status)) {
501
		$gateways_status = return_gateways_status(true);
502
	}
503

    
504
	$ifips = array();
505
	if (in_array($ph1ent['protocol'], array('inet', 'both'))) {
506
		$ip4 = get_interface_ip($if, $gateways_status);
507
		if (!empty($ip4)) {
508
			$ifips[] = $ip4;
509
		}
510
	}
511

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

    
525
	return implode(',', $ifips);
526
}
527

    
528
/*
529
 * Return phase1 local address
530
 */
531
function ipsec_get_phase1_dst(& $ph1ent) {
532
	global $g;
533

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

    
552
	return $rg;
553
}
554

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
983
	return $spd;
984
}
985

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

    
1036
	return $sad;
1037
}
1038

    
1039
/*
1040
 * Return dump of mobile user list
1041
 */
1042
function ipsec_dump_mobile() {
1043
	global $g, $config;
1044

    
1045
	if(!isset($config['ipsec']['client']['enable'])) {
1046
		return array();
1047
	}
1048

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

    
1052
	if ($rc != 0) {
1053
		log_error(gettext("Unable to find IPsec daemon leases file. Could not display mobile user stats!"));
1054
		return array();
1055
	}
1056

    
1057
	$response = array(
1058
		'pool' => array(),
1059
	);
1060

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

    
1086
	unset($_gb, $output, $rc, $pool_regex);
1087

    
1088
	return $response;
1089
}
1090

    
1091
function ipsec_mobilekey_sort() {
1092
	global $config;
1093

    
1094
	function mobilekeycmp($a, $b) {
1095
		return strcmp($a['ident'][0], $b['ident'][0]);
1096
	}
1097

    
1098
	usort($config['ipsec']['mobilekey'], "mobilekeycmp");
1099
}
1100

    
1101
function ipsec_fixup_ip($ipaddr) {
1102
	if (is_ipaddrv6($ipaddr) || is_subnetv6($ipaddr)) {
1103
		return text_to_compressed_ip6($ipaddr);
1104
	} else {
1105
		return $ipaddr;
1106
	}
1107
}
1108

    
1109
function ipsec_find_id(& $ph1ent, $side = "local", $rgmap = array(), $gateways_status = false) {
1110
	if ($side == "local") {
1111
		$id_type = $ph1ent['myid_type'];
1112
		$id_data = $ph1ent['myid_data'];
1113

    
1114
		$addr = ipsec_get_phase1_src($ph1ent, $gateways_status);
1115
		if (!$addr) {
1116
			return array();
1117
		}
1118
		/* When automatically guessing, use the first address. */
1119
		$addr = explode(',', $addr);
1120
		$addr = $addr[0];
1121
	} elseif ($side == "peer") {
1122
		$id_type = $ph1ent['peerid_type'];
1123
		$id_data = $ph1ent['peerid_data'];
1124

    
1125
		if (isset($ph1ent['mobile'])) {
1126
			$addr = "%any";
1127
		} else {
1128
			$addr = $ph1ent['remote-gateway'];
1129
		}
1130
	} else {
1131
		return array();
1132
	}
1133

    
1134
	$thisid_type = $id_type;
1135
	switch ($thisid_type) {
1136
		case 'myaddress':
1137
			$thisid_type = 'address';
1138
			$thisid_data = $addr;
1139
			break;
1140
		case 'dyn_dns':
1141
			$thisid_type = 'dns';
1142
			$thisid_data = $id_data;
1143
			break;
1144
		case 'peeraddress':
1145
			$thisid_type = 'address';
1146
			$thisid_data = $rgmap[$ph1ent['remote-gateway']];
1147
			break;
1148
		case 'keyid tag':
1149
			$thisid_type = 'keyid';
1150
			$thisid_data = $id_data;
1151
			break;
1152
		case 'user_fqdn':
1153
			$thisid_type = 'userfqdn';
1154
			$thisid_data = $id_data;
1155
			break;
1156
		case 'any':
1157
			$thisid_data = '%any';
1158
			break;
1159
		case 'address':
1160
		case 'fqdn':
1161
		case 'asn1dn':
1162
		case 'auto':
1163
			$thisid_data = $id_data;
1164
			break;
1165
		default:
1166
			break;
1167
	}
1168
	return array($thisid_type, $thisid_data);
1169
}
1170

    
1171
/*
1172
 * Fixup ID type/data to include prefix when necessary, add quotes, etc.
1173
 */
1174
function ipsec_fixup_id($type, $data) {
1175
	/* List of types to pass through as-is without changes or adding prefix */
1176
	$auto_types = array('address', 'auto');
1177
	/* List of types which need the resulting string double quoted. */
1178
	$quote_types = array('keyid', 'asn1dn', 'auto');
1179

    
1180
	/* If the first character of asn1dn type data is not #, then rely on
1181
	 * automatic parsing https://redmine.pfsense.org/issues/4792 */
1182
	if (($type == 'asn1dn') && !empty($data) && ($data[0] != '#')) {
1183
		$auto_types[] = 'asn1dn';
1184
	}
1185

    
1186
	if ($type == 'any') {
1187
		$idstring = "%any";
1188
	} elseif (!in_array($type, $auto_types)) {
1189
		$idstring = "{$type}:{$data}";
1190
	} else {
1191
		$idstring = $data;
1192
	}
1193

    
1194
	if (in_array($type, $quote_types)) {
1195
		$idstring = "\"{$idstring}\"";
1196
	}
1197

    
1198
	return $idstring;
1199
}
1200

    
1201
function ipsec_fixup_network($network) {
1202
	if (substr($network, -3) == '|/0') {
1203
		$result = substr($network, 0, -3);
1204
	} else {
1205
		$tmp = explode('|', $network);
1206
		if (isset($tmp[1])) {
1207
			$result = $tmp[1];
1208
		} else {
1209
			$result = $tmp[0];
1210
		}
1211
		unset($tmp);
1212
	}
1213

    
1214
	return $result;
1215
}
1216

    
1217
function ipsec_get_loglevels() {
1218
	global $config, $ipsec_log_cats;
1219
	$def_loglevel = '1';
1220

    
1221
	$levels = array();
1222

    
1223
	foreach (array_keys($ipsec_log_cats) as $cat) {
1224
		if (isset($config['ipsec']['logging'][$cat])) {
1225
			$levels[$cat] = config_get_path("ipsec/logging/{$cat}");
1226
		} elseif (in_array($cat, array('ike', 'chd', 'cfg'))) {
1227
			$levels[$cat] = "2";
1228
		} else {
1229
			$levels[$cat] = $def_loglevel;
1230
		}
1231
	}
1232
	return $levels;
1233
}
1234

    
1235
function ipsec_vti($p1, $returnaddresses = false, $skipdisabled = true) {
1236
	global $config;
1237
	$is_vti = false;
1238
	$vtisubnet_spec = array();
1239

    
1240
	foreach(ipsec_get_phase2_by_ikeid($p1['ikeid']) as $p2) {
1241
		if ($p2['mode'] == 'vti') {
1242
			if ($skipdisabled && isset($p2['disabled'])) {
1243
				continue;
1244
			} else {
1245
				$is_vti = true;
1246
			}
1247
			if ($returnaddresses) {
1248
				$vtisubnet_spec[] = array(
1249
					'left' => ipsec_idinfo_to_cidr($p2['localid'], true, $p2['mode']),
1250
					'right' => ipsec_idinfo_to_cidr($p2['remoteid'], false, $p2['mode']),
1251
					'descr' => $p2['descr'],
1252
					'reqid' => $p2['reqid'],
1253
				);
1254
			}
1255
		}
1256
	}
1257
	return ($returnaddresses) ? $vtisubnet_spec : $is_vti;
1258
}
1259

    
1260
/* Called when IPsec is reloaded through rc.newipsecdns and similar, gives
1261
 * packages an opportunity to execute custom code when IPsec reloads.
1262
 */
1263
function ipsec_reload_package_hook() {
1264
	global $g, $config;
1265

    
1266
	init_config_arr(array('installedpackages', 'package'));
1267
	foreach ($config['installedpackages']['package'] as $package) {
1268
		if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) {
1269
			continue;
1270
		}
1271

    
1272
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $package['configurationfile'], 'packagegui');
1273
		if (!empty($pkg_config['include_file']) &&
1274
		    file_exists($pkg_config['include_file'])) {
1275
			require_once($pkg_config['include_file']);
1276
		}
1277
		if (empty($pkg_config['ipsec_reload_function'])) {
1278
			continue;
1279
		}
1280
		$pkg_ipsec_reload = $pkg_config['ipsec_reload_function'];
1281
		if (!function_exists($pkg_ipsec_reload)) {
1282
			continue;
1283
		}
1284
		$pkg_ipsec_reload();
1285
	}
1286
}
1287

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

    
1323
	return $modpmap[$index];
1324
}
1325

    
1326
/*
1327
 * Forcefully restart IPsec
1328
 * This is required for when dynamic interfaces reload
1329
 * For all other occasions the normal ipsec_configure()
1330
 * will gracefully reload the settings without restarting
1331
 */
1332
function ipsec_force_reload($interface = "", $protocol = "") {
1333
	global $g, $config;
1334

    
1335
	if (!ipsec_enabled()) {
1336
		return;
1337
	}
1338

    
1339
	$ipseccfg = config_get_path('ipsec');
1340

    
1341
	if (!empty($interface) && is_array($ipseccfg['phase1'])) {
1342
		$found = false;
1343
		$gateways_status = null;
1344
		$realif = array();
1345
		$ifcache = array();
1346
		foreach ($ipseccfg['phase1'] as $ipsec) {
1347
			if (isset($ipsec['disabled'])) {
1348
				continue;
1349
			}
1350
			if (!empty($protocol) &&
1351
			    ($ipsec['protocol'] != $protocol) &&
1352
			    ($ipsec['protocol'] != 'both')) {
1353
				continue;
1354
			}
1355
			if ($ipsec['interface'] == $interface) {
1356
				$found = true;
1357
				break;
1358
			} else {
1359
				/* Fetch gateway status just once, and only if needed */
1360
				if (!is_array($gateways_status)) {
1361
					$gateways_status = return_gateways_status(true);
1362
				}
1363
				$fam = (!empty($ipsec['protocol'])) ? $ipsec['protocol'] : 'inet';
1364

    
1365
				/* Find the real active passed interface for this address family, cache the result */
1366
				if (empty($realif) ||
1367
				    empty($realif[$fam])) {
1368
					$realif[$fam] = get_failover_interface($interface, $fam, $gateways_status);
1369
				}
1370

    
1371
				/* Find the real active tunnel interface for this address family, cache the result */
1372
				if (empty($ifcache) ||
1373
				    empty($ifcache[$ipsec['interface']]) ||
1374
				    empty($ifcache[$ipsec['interface']][$fam])) {
1375
					if (!is_array($ifcache[$ipsec['interface']])) {
1376
						$ifcache[$ipsec['interface']] = array();
1377
					}
1378
					$ifcache[$ipsec['interface']][$fam] = get_failover_interface($ipsec['interface'], $fam, $gateways_status);
1379
				}
1380

    
1381
				if ($realif[$fam] == $ifcache[$ipsec['interface']][$fam]) {
1382
					$found = true;
1383
					break;
1384
				}
1385
			}
1386
		}
1387
		if (!$found) {
1388
			log_error(sprintf(gettext("Ignoring IPsec reload since there are no tunnels on interface %s"), $interface));
1389
			return;
1390
		}
1391
	}
1392

    
1393
	/* If we get this far then we need to take action. */
1394
	log_error(gettext("Forcefully reloading IPsec"));
1395
	ipsec_configure();
1396
}
1397

    
1398
/****f* ipsec/ipsec_strongswan_confgen
1399
 * NAME
1400
 *   ipsec_strongswan_confgen - Generate strongswan.conf style formatted output
1401
 *                              From a multi-dimensional array.
1402
 * INPUTS
1403
 *   $confarr: An array of key=value pairs or key=array entries for subsections.
1404
 *   $indent : A string of tabs used when indenting lines.
1405
 * RESULT
1406
 *   Returns a string of key=value pairs with proper indenting, and with named
1407
 *   subsections enclosed in braces.
1408
 * NOTES
1409
 *   Values starting with a "#" (comments) or "include " will be printed
1410
 *   directly without their associated key name.
1411
 ******/
1412
function ipsec_strongswan_confgen($confarr, $indent = "") {
1413
	$confstr = "";
1414
	/* Only process the contents if it's an array. */
1415
	if (is_array($confarr)) {
1416
		foreach ($confarr as $key => $value) {
1417
			if (is_array($value)) {
1418
				/* If the array is empty, do not print anything */
1419
				if (empty($value)) {
1420
					continue;
1421
				}
1422
				/* If this is an array, setup the subsection name
1423
				 * and structure, then call this function
1424
				 * recursively to walk the lower array. */
1425
				$confstr .= "{$indent}{$key} {\n";
1426
				$confstr .= ipsec_strongswan_confgen($value, $indent . "\t");
1427
				$confstr .= "{$indent}}\n";
1428
			} else {
1429
				$confstr .= "{$indent}";
1430
				if ((substr($value, 0 , 1) != '#') &&
1431
				    (substr($value, 0 , 8) != 'include ')) {
1432
					/* Not a comment or include, print the key */
1433
					$confstr .= "{$key} = ";
1434
				}
1435
				$confstr .= "{$value}\n";
1436
			}
1437
		}
1438
	}
1439
	return $confstr;
1440
}
1441

    
1442
global $g, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs;
1443
$ipsec_swanctl_basedir = "{$g['varetc_path']}/ipsec";
1444
$ipsec_swanctl_dirs = array(
1445
	'conf'     => "{$ipsec_swanctl_basedir}/conf.d",
1446
	'certpath' => "{$ipsec_swanctl_basedir}/x509",
1447
	'capath'   => "{$ipsec_swanctl_basedir}/x509ca",
1448
	'aapath'   => "{$ipsec_swanctl_basedir}/x509aa",
1449
	'ocsppath' => "{$ipsec_swanctl_basedir}/x509ocsp",
1450
	'crlpath'  => "{$ipsec_swanctl_basedir}/x509crl",
1451
	'acpath'   => "{$ipsec_swanctl_basedir}/x509ac",
1452
	'rsakeys'  => "{$ipsec_swanctl_basedir}/rsa",
1453
	'eckeys'   => "{$ipsec_swanctl_basedir}/ecdsa",
1454
	'keypath'  => "{$ipsec_swanctl_basedir}/private",
1455
	'pubkpath' => "{$ipsec_swanctl_basedir}/pubkey",
1456
	'blisspath' => "{$ipsec_swanctl_basedir}/bliss",
1457
	'pkcs8path' => "{$ipsec_swanctl_basedir}/pkcs8",
1458
	'pkcs12path' => "{$ipsec_swanctl_basedir}/pkcs12",
1459
);
1460

    
1461
/****f* ipsec/ipsec_create_dirs
1462
 * NAME
1463
 *   ipsec_create_dirs - Create default set of strongSwan configuration directories.
1464
 * INPUTS
1465
 *   None
1466
 * RESULT
1467
 *   Cleans up and creates strongSwan configuration directories and links.
1468
 ******/
1469
function ipsec_create_dirs() {
1470
	global $config, $g, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs;
1471

    
1472
	/* Cleanup base paths to ensure old files are not left behind (#5238) */
1473
	if (!empty($ipsec_swanctl_basedir)) {
1474
		@rmdir_recursive($ipsec_swanctl_basedir, false);
1475
	}
1476
	/* Create directory structure */
1477
	array_map('safe_mkdir', array_values($ipsec_swanctl_dirs));
1478

    
1479
	/* Create and link strongSwan config */
1480
	$ssdpath = "{$g['varetc_path']}/ipsec/strongswan.d";
1481
	if (!file_exists($ssdpath) || !is_link($ssdpath)) {
1482
		if (is_dir($ssdpath) && !is_link($ssdpath)) {
1483
			@rmdir_recursive($ssdpath, false);
1484
		} else {
1485
			@unlink($ssdpath);
1486
		}
1487
		@symlink("/usr/local/etc/strongswan.d",
1488
		    $ssdpath);
1489
	}
1490
	if (!file_exists("/usr/local/etc/strongswan.conf") ||
1491
	    !is_link("/usr/local/etc/strongswan.conf")) {
1492
		@unlink("/usr/local/etc/strongswan.conf");
1493
		@symlink("{$g['varetc_path']}/ipsec/strongswan.conf",
1494
		    "/usr/local/etc/strongswan.conf");
1495
	}
1496
}
1497

    
1498
/****f* ipsec/ipsec_setup_gwifs
1499
 * NAME
1500
 *   ipsec_setup_gwifs - Setup IPsec-related interfaces and gateways
1501
 * INPUTS
1502
 *   None
1503
 * RESULT
1504
 *   Sets up VTI interfaces and gateways, populates ipsecpinghosts
1505
 ******/
1506
function ipsec_setup_gwifs($gateways_status = false) {
1507
	global $g, $a_phase1, $a_phase2, $rgmap, $filterdns_list,
1508
		$aggressive_mode_psk, $mobile_ipsec_auth, $ifacesuse,
1509
		$ipsecpinghostsactive;
1510
	/* resolve all local, peer addresses and setup pings */
1511
	/* Fetch gateway status if not passed */
1512
	if (!is_array($gateways_status)) {
1513
		$gateways_status = return_gateways_status(true);
1514
	}
1515

    
1516
	unset($iflist);
1517
	if (is_array($a_phase1) && count($a_phase1)) {
1518
		$ipsecpinghosts = array();
1519
		/* step through each phase1 entry */
1520
		foreach ($a_phase1 as $ph1ent) {
1521
			if (isset($ph1ent['disabled'])) {
1522
				continue;
1523
			}
1524
			if (substr($ph1ent['interface'], 0, 4) == "_vip") {
1525
				$vpninterface = get_configured_vip_interface($ph1ent['interface']);
1526
				$ifacesuse[] = get_real_interface($vpninterface);
1527
			} else {
1528
				$vpninterface = get_failover_interface($ph1ent['interface'], 'all', $gateways_status);
1529
				if (substr($vpninterface, 0, 4) == "_vip") {
1530
					$vpninterface = get_configured_vip_interface($vpninterface);
1531
					$ifacesuse[] = get_real_interface($vpninterface);
1532
				} elseif (!empty($vpninterface)) {
1533
					$ifacesuse[] = $vpninterface;
1534
				}
1535
			}
1536
			if ($ph1ent['mode'] == "aggressive" && ($ph1ent['authentication_method'] == "pre_shared_key" || $ph1ent['authentication_method'] == "xauth_psk_server")) {
1537
				$aggressive_mode_psk = true;
1538
			}
1539

    
1540
			$ikeid = $ph1ent['ikeid'];
1541

    
1542
			$local_spec = ipsec_get_phase1_src($ph1ent, $gateways_status);
1543
			/* When automatically guessing, use the first address. */
1544
			$local_spec  = explode(',', $local_spec);
1545
			$local_spec  = $local_spec[0];
1546
			if (!is_ipaddr($local_spec)) {
1547
				log_error(sprintf(gettext("IPsec ERROR: Could not find phase 1 source for connection %s. Omitting from configuration file."), $ph1ent['descr']));
1548
				continue;
1549
			}
1550

    
1551
			if (isset($ph1ent['mobile'])) {
1552
				$mobile_ipsec_auth = $ph1ent['authentication_method'];
1553
				continue;
1554
			}
1555

    
1556
			/* see if this tunnel has a hostname for the remote-gateway. If so,
1557
			   try to resolve it now and add it to the list for filterdns */
1558
			$rg = $ph1ent['remote-gateway'];
1559
			if (!is_ipaddr($rg)) {
1560
				$filterdns_list[] = "{$rg}";
1561
				if (!platform_booting()) {
1562
					$rg = resolve_retry($rg, $ph1ent['protocol']);
1563
				}
1564
				if (!is_ipaddr($rg)) {
1565
					continue;
1566
				}
1567
			}
1568
			if (!isset($ph1ent['gw_duplicates']) && array_search($rg, $rgmap)) {
1569
				log_error(sprintf(gettext("The remote gateway %s already exists on another phase 1 entry"), $rg));
1570
				continue;
1571
			}
1572
			$rgmap[$ph1ent['remote-gateway']] = $rg;
1573

    
1574
			$is_vti = false;
1575
			if (is_array($a_phase2)) {
1576
				/* step through each phase2 entry */
1577
				foreach ($a_phase2 as $ph2ent) {
1578
					if ($ph2ent['ikeid'] != $ph1ent['ikeid']) {
1579
						continue;
1580
					}
1581
					if ($ph2ent['mode'] == 'vti') {
1582
						$is_vti = true;
1583
					}
1584
					if (isset($ph2ent['disabled']) || ($ikeid != $ph2ent['ikeid'])) {
1585
						continue;
1586
					}
1587

    
1588
					/* add an ipsec pinghosts entry */
1589
					if ($ph2ent['pinghost']) {
1590
						if (!is_array($iflist)) {
1591
							$iflist = get_configured_interface_list();
1592
						}
1593
						$srcip = null;
1594
						$local_subnet = ipsec_idinfo_to_cidr($ph2ent['localid'], true, $ph2ent['mode']);
1595
						if (is_ipaddrv6($ph2ent['pinghost'])) {
1596
							foreach ($iflist as $ifent => $ifname) {
1597
								$interface_ip = get_interface_ipv6($ifent);
1598
								if (!is_ipaddrv6($interface_ip)) {
1599
									continue;
1600
								}
1601
								if (ip_in_subnet($interface_ip, $local_subnet)) {
1602
									$srcip = $interface_ip;
1603
									break;
1604
								}
1605
							}
1606
						} else {
1607
							foreach ($iflist as $ifent => $ifname) {
1608
								$interface_ip = get_interface_ip($ifent, $gateways_status);
1609
								if (!is_ipaddrv4($interface_ip)) {
1610
									continue;
1611
								}
1612
								if ($local_subnet == "0.0.0.0/0" || ip_in_subnet($interface_ip, $local_subnet)) {
1613
									$srcip = $interface_ip;
1614
									break;
1615
								}
1616
							}
1617
						}
1618
						/* if no valid src IP was found in configured interfaces, try the vips */
1619
						if (is_null($srcip)) {
1620
							$viplist = get_configured_vip_list();
1621
							foreach ($viplist as $vip => $address) {
1622
								if (ip_in_subnet($address, $local_subnet)) {
1623
									$srcip = $address;
1624
									break;
1625
								}
1626
							}
1627
						}
1628
						$dstip = $ph2ent['pinghost'];
1629
						$family = "inet" . (is_ipaddrv6($dstip)) ? "6" : "";
1630
						if (is_ipaddr($srcip)) {
1631
							$ipsecpinghosts[] = "{$srcip}|{$dstip}|3|||||{$family}|\n";
1632
							$ipsecpinghostsactive = true;
1633
						}
1634
					}
1635
				}
1636
			}
1637
			if ($is_vti) {
1638
				interface_ipsec_vti_configure($ph1ent, $gateways_status);
1639
			}
1640
		}
1641
		@file_put_contents("{$g['vardb_path']}/ipsecpinghosts", $ipsecpinghosts);
1642
		unset($ipsecpinghosts);
1643
	}
1644
	unset($iflist);
1645
}
1646

    
1647
/****f* ipsec/ipsec_logging_config
1648
 * NAME
1649
 *   ipsec_logging_config - Generate an array containing strongSwan logging
1650
 *                          values.
1651
 * INPUTS
1652
 *   None
1653
 * RESULT
1654
 *   Returns an array which can be merged into the strongswan daemon logging
1655
 *   values so that each logging category is represented, even those not listed
1656
 *   in config.xml.
1657
 ******/
1658
function ipsec_logging_config() {
1659
	global $config, $ipsec_log_cats, $ipsec_log_sevs;
1660
	$logcfg = array();
1661
	$log_levels = ipsec_get_loglevels();
1662
	foreach (array_keys($ipsec_log_cats) as $cat) {
1663
		if (is_numeric($log_levels[$cat]) &&
1664
		    in_array(intval($log_levels[$cat]), array_keys($ipsec_log_sevs), true)) {
1665
			$logcfg[$cat] = $log_levels[$cat];
1666
		}
1667
	}
1668
	return $logcfg;
1669
}
1670

    
1671
/****f* ipsec/ipsec_setup_strongswan
1672
 * NAME
1673
 *   ipsec_setup_strongswan - Generate the strongswan.conf configuration file.
1674
 * INPUTS
1675
 *   None
1676
 * RESULT
1677
 *   Determines strongswan.conf values and crafts the configuration file in the
1678
 *   appropriate format.
1679
 * NOTES
1680
 *   Called from ipsec_configure()
1681
 ******/
1682
function ipsec_setup_strongswan() {
1683
	global $config, $g, $mobile_ipsec_auth, $a_client, $a_phase2, $aggressive_mode_psk, $ifacesuse;
1684

    
1685
	/* strongswan.conf array */
1686
	$ssconf = array();
1687
	$ssconf[] = "# Automatically generated config file - DO NOT MODIFY. Changes will be overwritten.";
1688
	$ssconf['starter'] = array();
1689
	$ssconf['starter']['load_warning'] = "no";
1690

    
1691
	$ssconf['charon'] = array();
1692
	$ssconf['charon'][] = '# number of worker threads in charon';
1693
	$ssconf['charon']['threads'] = '16';
1694
	$ssconf['charon']['ikesa_table_size'] = '32';
1695
	$ssconf['charon']['ikesa_table_segments'] = '4';
1696
	$ssconf['charon']['init_limit_half_open'] = '1000';
1697
	$ssconf['charon']['install_routes'] = 'no';
1698
	$ssconf['charon']['load_modular'] = 'yes';
1699
	$ssconf['charon']['ignore_acquire_ts'] = 'yes';
1700

    
1701
	if ($aggressive_mode_psk) {
1702
		$stronconf = '';
1703
		if (file_exists("{$g['varetc_path']}/ipsec/strongswan.conf")) {
1704
			$stronconf = file_get_contents("{$g['varetc_path']}/ipsec/strongswan.conf");
1705
		}
1706
		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.");
1707
		$restart = (!empty($stronconf) && strpos($stronconf, 'i_dont_care_about_security_and_use_aggressive_mode_psk') === FALSE);
1708
		unset($stronconf);
1709
		$ssconf['charon']['i_dont_care_about_security_and_use_aggressive_mode_psk'] = "yes";
1710
	}
1711

    
1712
	if (isset($config['ipsec']['acceptunencryptedmainmode'])) {
1713
		$ssconf['charon']['accept_unencrypted_mainmode_messages'] = "yes";
1714
	}
1715

    
1716
	if (isset($config['ipsec']['maxexchange'])) {
1717
		$ssconf['charon']['max_ikev1_exchanges'] = config_get_path('ipsec/maxexchange');
1718
	}
1719

    
1720
	$unity_enabled = isset($config['ipsec']['unityplugin']) ? 'yes' : 'no';
1721
	$ssconf['charon']['cisco_unity'] = $unity_enabled;
1722

    
1723
	if (isset($config['ipsec']['enableinterfacesuse']) && !empty($ifacesuse)) {
1724
		$ssconf['charon']['interfaces_use'] = implode(',', array_unique($ifacesuse));
1725
	}
1726

    
1727
	if (isset($config['ipsec']['makebeforebreak'])) {
1728
		$ssconf['charon']['make_before_break'] = "yes";
1729
	}
1730

    
1731
	if (isset($config['ipsec']['port'])) {
1732
		$ssconf['charon']['port'] = config_get_path('ipsec/port');
1733
	}
1734

    
1735
	if (isset($config['ipsec']['port_nat_t'])) {
1736
		$ssconf['charon']['port_nat_t'] = config_get_path('ipsec/port_nat_t');
1737
	}
1738

    
1739
	/* IKEv2 retransmit options, see https://redmine.pfsense.org/issues/12184 */
1740
	if (isset($config['ipsec']['ikev2_retransmit_tries'])) {
1741
		$ssconf['charon']['retransmit_tries'] = config_get_path('ipsec/ikev2_retransmit_tries');
1742
	}
1743
	if (isset($config['ipsec']['ikev2_retransmit_timeout'])) {
1744
		$ssconf['charon']['retransmit_timeout'] = config_get_path('ipsec/ikev2_retransmit_timeout');
1745
	}
1746
	if (isset($config['ipsec']['ikev2_retransmit_base'])) {
1747
		$ssconf['charon']['retransmit_base'] = config_get_path('ipsec/ikev2_retransmit_base');
1748
	}
1749
	if (isset($config['ipsec']['ikev2_retransmit_jitter'])) {
1750
		$ssconf['charon']['retransmit_jitter'] = config_get_path('ipsec/ikev2_retransmit_jitter');
1751
	}
1752
	if (isset($config['ipsec']['ikev2_retransmit_limit'])) {
1753
		$ssconf['charon']['retransmit_limit'] = config_get_path('ipsec/ikev2_retransmit_limit');
1754
	}
1755

    
1756
	$ssconf['charon']['syslog'] = array();
1757
	$ssconf['charon']['syslog']['identifier'] = 'charon';
1758
	$ssconf['charon']['syslog'][] = "# log everything under daemon since it ends up in the same place regardless with our syslog.conf";
1759
	$ssconf['charon']['syslog']['daemon'] = array();
1760
	$ssconf['charon']['syslog']['daemon']['ike_name'] = 'yes';
1761
	$ssconf['charon']['syslog']['daemon'] = array_merge($ssconf['charon']['syslog']['daemon'], ipsec_logging_config());
1762
	$ssconf['charon']['syslog'][] = '# disable logging under auth so logs aren\'t duplicated';
1763
	$ssconf['charon']['syslog']['auth'] = array();
1764
	$ssconf['charon']['syslog']['auth']['default'] = -1;
1765

    
1766
	$ssconf['charon']['plugins'] = array();
1767
	$ssconf['charon']['plugins'][] = "# Load defaults";
1768
	$ssconf['charon']['plugins'][] = "include {$g['varetc_path']}/ipsec/strongswan.d/charon/*.conf";
1769
	$ssconf['charon']['plugins']['unity'] = array('load' => $unity_enabled);
1770
	$ssconf['charon']['plugins']['curve25519'] = array('load' => "yes");
1771

    
1772
	if (isset($config['ipsec']['pkcs11support'])) {
1773
		$ssconf['charon']['plugins']['pkcs11'] = array();
1774
		$ssconf['charon']['plugins']['pkcs11']['load'] = "yes";
1775
		$ssconf['charon']['plugins']['pkcs11']['modules'] = array();
1776
		$ssconf['charon']['plugins']['pkcs11']['modules']['opensc'] = array();
1777
		$ssconf['charon']['plugins']['pkcs11']['modules']['opensc']['load_certs'] = "yes";
1778
		$ssconf['charon']['plugins']['pkcs11']['modules']['opensc']['path'] = "/usr/local/lib/opensc-pkcs11.so";
1779
	}
1780

    
1781
	/* Find RADIUS servers designated for Mobile IPsec user auth */
1782
	$radius_servers = array();
1783
	foreach (explode(',', $config['ipsec']['client']['user_source']) as $user_source) {
1784
		$auth_server = auth_get_authserver($user_source);
1785
		$nice_user_source = strtolower(preg_replace('/[\s\.]+/', '_', $user_source));
1786
		if ($auth_server && ($auth_server['type'] === 'radius')) {
1787
			$radius_servers[$nice_user_source] = array();
1788
			$radius_servers[$nice_user_source]['address'] = $auth_server['host'];
1789
			$radius_servers[$nice_user_source]['secret'] = "\"{$auth_server['radius_secret']}\"";
1790
			$radius_servers[$nice_user_source]['auth_port'] = empty($auth_server['radius_auth_port']) ? 1812 : $auth_server['radius_auth_port'];;
1791
			$radius_servers[$nice_user_source]['acct_port'] = empty($auth_server['radius_acct_port']) ? 1813 : $auth_server['radius_acct_port'];
1792
		}
1793
	}
1794

    
1795
	/* Generate an eap-radius config section if appropriate */
1796
	if (count($radius_servers) && ($mobile_ipsec_auth === "eap-radius")) {
1797
		$ssconf['charon']['plugins']['eap-radius'] = array();
1798
		$ssconf['charon']['plugins']['eap-radius']['load'] = "2";
1799
		$ssconf['charon']['plugins']['eap-radius']['class_group'] = "yes";
1800
		$ssconf['charon']['plugins']['eap-radius']['eap_start'] = "no";
1801
		/* Activate RADIUS accounting only if it was selected on the IPsec Mobile Clients tab */
1802
		if ($auth_server && isset($auth_server['radius_acct_port']) &&
1803
		    (is_array($a_client) && ($a_client['radiusaccounting'] == "enabled"))) {
1804
			$ssconf['charon']['plugins']['eap-radius']['accounting'] = "yes";
1805
			$ssconf['charon']['plugins']['eap-radius']['accounting_close_on_timeout'] = "no";
1806
		}
1807
		/* advanced parameters, see https://redmine.pfsense.org/issues/11211 */
1808
		if (isset($a_client['radius_retransmit_base'])) {
1809
			$ssconf['charon']['plugins']['eap-radius']['retransmit_base'] = $a_client['radius_retransmit_base'];
1810
		}
1811
		if (isset($a_client['radius_retransmit_timeout'])) {
1812
			$ssconf['charon']['plugins']['eap-radius']['retransmit_timeout'] = $a_client['radius_retransmit_timeout'];
1813
		}
1814
		if (isset($a_client['radius_retransmit_tries'])) {
1815
			$ssconf['charon']['plugins']['eap-radius']['retransmit_tries'] = $a_client['radius_retransmit_tries'];
1816
		}
1817
		if (isset($a_client['radius_sockets'])) {
1818
			$ssconf['charon']['plugins']['eap-radius']['sockets'] = $a_client['radius_sockets'];
1819
		}
1820
		$ssconf['charon']['plugins']['eap-radius']['servers'] = $radius_servers;
1821
	}
1822

    
1823
	if (is_array($a_client) && isset($a_client['enable'])) {
1824
		if ($a_client['user_source'] != "none") {
1825
			$ssconf['charon']['plugins']['xauth-generic'] = array();
1826
			$ssconf['charon']['plugins']['xauth-generic']['script'] = "/etc/inc/ipsec.auth-user.php";
1827
			$authcfgs = array();
1828
			foreach (explode(",", $a_client['user_source']) as $authcfg) {
1829
				$authcfgs[] = ($authcfg == "system") ? "Local Database" : $authcfg;
1830
			}
1831
			$ssconf['charon']['plugins']['xauth-generic']['authcfg'] = implode(",", $authcfgs);
1832
		}
1833

    
1834
		$ssconf['charon']['plugins']['attr'] = array();
1835

    
1836
		$rightdnsservers = array();
1837
		if (!empty($a_client['dns_server1'])) {
1838
			$rightdnsservers[] = $a_client['dns_server1'];
1839
		}
1840
		if (!empty($a_client['dns_server2'])) {
1841
			$rightdnsservers[] = $a_client['dns_server2'];
1842
		}
1843
		if (!empty($a_client['dns_server3'])) {
1844
			$rightdnsservers[] = $a_client['dns_server3'];
1845
		}
1846
		if (!empty($a_client['dns_server4'])) {
1847
			$rightdnsservers[] = $a_client['dns_server4'];
1848
		}
1849
		if (count($rightdnsservers)) {
1850
			$ssconf['charon']['plugins']['attr']['dns'] = implode(',', $rightdnsservers);
1851
		}
1852

    
1853
		$cfgservers = array();
1854
		if (!empty($a_client['wins_server1'])) {
1855
			$cfgservers[] = $a_client['wins_server1'];
1856
		}
1857
		if (!empty($a_client['wins_server2'])) {
1858
			$cfgservers[] = $a_client['wins_server2'];
1859
		}
1860
		if (!empty($cfgservers)) {
1861
			$ssconf['charon']['plugins']['attr']['nbns'] = implode(',', $cfgservers);
1862
		}
1863
		unset($cfgservers);
1864

    
1865
		$net_list = array();
1866
		if (isset($a_client['net_list']) && is_array($a_phase2)) {
1867
			foreach ($a_phase2 as $ph2ent) {
1868
				if (isset($ph2ent['disabled']) ||
1869
				    !isset($ph2ent['mobile'])) {
1870
					continue;
1871
				}
1872
				$nlent = ipsec_idinfo_to_cidr($ph2ent['localid'], true, $ph2ent['mode']);
1873
				if (is_subnet($nlent)) {
1874
					$net_list[] = $nlent;
1875
				}
1876
				unset($nlent);
1877
			}
1878
		}
1879

    
1880
		if (!empty($net_list)) {
1881
			$ssconf['charon']['plugins']['attr']['subnet'] = implode(',', $net_list);
1882
			$ssconf['charon']['plugins']['attr']['split-include'] = implode(',', $net_list);
1883
		}
1884

    
1885
		if (!empty($a_client['dns_domain'])) {
1886
			$ssconf['charon']['plugins']['attr'][] = "# Search domain and default domain";
1887
			$ssconf['charon']['plugins']['attr']['27674'] = $a_client['dns_domain'];
1888
			if (empty($a_client['dns_split'])) {
1889
				$ssconf['charon']['plugins']['attr']['27675'] = $a_client['dns_domain'];
1890
			}
1891
		}
1892

    
1893
		if (!empty($a_client['dns_split'])) {
1894
			$ssconf['charon']['plugins']['attr'][] = "# Split DNS (UNITY_SPLITDNS_NAME)";
1895
			$ssconf['charon']['plugins']['attr']['28675'] = $a_client['dns_split'];
1896
			$ssconf['charon']['plugins']['attr'][] = "# Split DNS (INTERNAL_DNS_DOMAIN, RFC 8598)";
1897
			$ssconf['charon']['plugins']['attr']['25'] = str_replace(' ', ',', $a_client['dns_split']);
1898
		}
1899

    
1900
		if (!empty($a_client['login_banner'])) {
1901
			$ssconf['charon']['plugins']['attr'][] = "# Login Banner";
1902
			$ssconf['charon']['plugins']['attr']['28672'] = $a_client['login_banner'];
1903
		}
1904

    
1905
		if (isset($a_client['save_passwd'])) {
1906
			$ssconf['charon']['plugins']['attr'][] = "# Save Xauth Password";
1907
			$ssconf['charon']['plugins']['attr']['28673'] = "1";
1908
		}
1909

    
1910
		if ($a_client['pfs_group']) {
1911
			$ssconf['charon']['plugins']['attr'][] = "# Phase2 PFS Group";
1912
			$ssconf['charon']['plugins']['attr']['28679'] = $a_client['pfs_group'];
1913
		}
1914
	}
1915

    
1916
	@file_put_contents("{$g['varetc_path']}/ipsec/strongswan.conf", ipsec_strongswan_confgen($ssconf));
1917
}
1918

    
1919
/****f* ipsec/ipsec_setup_pools
1920
 * NAME
1921
 *   ipsec_setup_pools - Generate primary mobile pool configuration for swanctl
1922
  * INPUTS
1923
 *   None
1924
 * RESULT
1925
 *   Adds configured mobile pool settings to $scconf
1926
 * NOTES
1927
 *   These values were formerly in strongswan.conf but may now be configured in
1928
 *   pools, making future expansion to support multiple pools possible.
1929
 ******/
1930
function ipsec_setup_pools() {
1931
	global $config, $g, $mobile_ipsec_auth, $a_client, $a_phase2, $scconf;
1932
	if (!is_array($a_client) || !isset($a_client['enable'])) {
1933
		return;
1934
	}
1935
	if (($mobile_ipsec_auth == "eap-radius") && empty($a_client['pool_address']) &&
1936
	    empty($a_client['pool_address_v6'])) {
1937
		return;
1938
	}
1939

    
1940
	/* Keep the comment in the array to ensure the entry makes it into the
1941
	 * config in all cases.
1942
	 * See https://redmine.pfsense.org/issues/11891 */
1943
	$scconf['mobile-pool'] = array("# Mobile pool settings template");
1944
	$scconf['pools'] = array();
1945
	$pool_common =& $scconf['mobile-pool'];
1946

    
1947
	if (!empty($a_client['pool_address'])) {
1948
		$scconf['pools']['mobile-pool-v4 : mobile-pool'] = array();
1949
		$v4pool =& $scconf['pools']['mobile-pool-v4 : mobile-pool'];
1950
		$v4pool['addrs'] = "{$a_client['pool_address']}/{$a_client['pool_netbits']}";
1951
	}
1952
	if (!empty($a_client['pool_address_v6'])) {
1953
		$scconf['pools']['mobile-pool-v6 : mobile-pool'] = array();
1954
		$v6pool =& $scconf['pools']['mobile-pool-v6 : mobile-pool'];
1955
		$v6pool['addrs'] = "{$a_client['pool_address_v6']}/{$a_client['pool_netbits_v6']}";
1956
	}
1957

    
1958
	return;
1959
}
1960

    
1961
/****f* ipsec/ipsec_setup_userpools
1962
 * NAME
1963
 *   ipsec_setup_userpools - Generate per-user custom pool settings for swanctl
1964
  * INPUTS
1965
 *   None
1966
 * RESULT
1967
 *   Adds configured per-user pool settings to $scconf using the primary mobile
1968
 *   pool as a base configuration.
1969
 * NOTES
1970
 *   Given this new flexible format, it is now possible to override any valid
1971
 *   pool setting, so future expansion of per-user settings is possible.
1972
 ******/
1973
function ipsec_setup_userpools() {
1974
	global $config, $scconf;
1975
	$a_mobilekey = config_get_path('ipsec/mobilekey');
1976

    
1977
	/* Do not waste time if we do not have all the necessary information. */
1978
	if (!is_array($a_mobilekey) ||
1979
	    empty($a_mobilekey) ||
1980
	    !is_array($scconf['connections']) ||
1981
	    !is_array($scconf['con-mobile-defaults']) ||
1982
	    !is_array($scconf['pools']) ||
1983
	    !is_array($scconf['mobile-pool'])) {
1984
		return;
1985
	}
1986

    
1987
	$suffix = 1;
1988
	foreach ($a_mobilekey as $mkent) {
1989
		if (($mkent['type'] != "EAP") ||
1990
		    !isset($mkent['ident_type']) ||
1991
		    !isset($mkent['pool_address']) ||
1992
		    !isset($mkent['pool_netbits']) ||
1993
		    (strlen($mkent['pool_address']) < 1) ||
1994
		    !is_ipaddr($mkent['pool_address'])) {
1995
			continue;
1996
		}
1997
		/* The name of just this pool */
1998
		$upbase = "mobile-userpool-{$suffix}";
1999
		/* The full connection name including a reference to the primary
2000
		 * mobile connection */
2001
		$upconn = "con-{$upbase} : con-mobile-defaults";
2002
		/* The full pool name including a reference to the primary
2003
		 * mobile pool */
2004
		$upname = "{$upbase} : mobile-pool";
2005

    
2006
		$scconf['connections'][$upconn] = array();
2007
		$scconf['pools'][$upname] = array();
2008

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

    
2012
		/* Craft a cloned connection with the ID information to match */
2013
		$scconf['connections'][$upconn]['remote'] = array();
2014
		$scconf['connections'][$upconn]['remote']['id'] = $clientid;
2015
		$scconf['connections'][$upconn]['remote']['eap_id'] = $clienteapid;
2016
		$scconf['connections'][$upconn]['pools'] = $upbase;
2017

    
2018
		/* Craft a cloned pool with pool settings to override for this user */
2019
		$scconf['pools'][$upname]['addrs'] = "{$mkent['pool_address']}/{$mkent['pool_netbits']}";
2020
		if (isset($mkent['dns_address']) && strlen($mkent['dns_address']) > 0 && is_ipaddr($mkent['dns_address'])) {
2021
			$scconf['pools'][$upname]['dns'] = $mkent['dns_address'];
2022
		}
2023
		$suffix++;
2024
	}
2025
	return;
2026
}
2027

    
2028
/****f* ipsec/ipsec_setup_bypass
2029
 * NAME
2030
 *   ipsec_setup_bypass - Generate a bypass connection for LAN-LAN and custom rules traffic
2031
 * INPUTS
2032
 *   None
2033
 * RESULT
2034
 *   Sets up a bypass connection to prevent local traffic from being caught by
2035
 *   IPsec policies.
2036
 ******/
2037
function ipsec_setup_bypass($gateways_status) {
2038
	global $config, $scconf;
2039

    
2040
	$scconf['connections']['bypass'] = array();
2041
	/* Prevents the connection from being considered for remote peers */
2042
	$scconf['connections']['bypass']['remote_addrs'] = "127.0.0.1";
2043
	$scconf['connections']['bypass']['children'] = array();
2044

    
2045
	/* Locate the LAN IPv4 and IPv6 subnets */
2046
	$bypassnets = array();
2047
	if ($config['interfaces']['lan']) {
2048
		$lanip = get_interface_ip("lan", $gateways_status);
2049
		if (!empty($lanip) && is_ipaddrv4($lanip)) {
2050
			$lansn = get_interface_subnet("lan");
2051
			$lansa = gen_subnetv4($lanip, $lansn);
2052
			if (!empty($lansa) && !empty($lansn)) {
2053
				$bypassnets[] = "{$lansa}/{$lansn}";
2054
			}
2055
		}
2056
		$lanip6 = get_interface_ipv6("lan", $gateways_status);
2057
		if (!empty($lanip6) && is_ipaddrv6($lanip6)) {
2058
			$lansn6 = get_interface_subnetv6("lan");
2059
			$lansa6 = gen_subnetv6($lanip6, $lansn6);
2060
			if (!empty($lansa6) && !empty($lansn6)) {
2061
				$bypassnets[] = "{$lansa6}/{$lansn6}";
2062
			}
2063
		}
2064
	}
2065
	/* If we have viable targets, setup a bypass LAN connection */
2066
	if (!empty($bypassnets) && !isset($config['ipsec']['noshuntlaninterfaces'])) {
2067
		$bypass = implode(',', $bypassnets);
2068
		$scconf['connections']['bypass']['children']['bypasslan'] = array();
2069
		$scconf['connections']['bypass']['children']['bypasslan']['local_ts'] = $bypass;
2070
		$scconf['connections']['bypass']['children']['bypasslan']['remote_ts'] = $bypass;
2071
		$scconf['connections']['bypass']['children']['bypasslan']['mode'] = "pass";
2072
		$scconf['connections']['bypass']['children']['bypasslan']['start_action'] = "trap";
2073
	}
2074

    
2075
	/* Setup custom bypass rules */
2076
	if ((isset($config['ipsec']['ipsecbypass']) && $config['ipsec']['bypassrules']) &&
2077
	    is_array($config['ipsec']['bypassrules'])) {
2078
		foreach ($config['ipsec']['bypassrules']['rule'] as $id => $rule) {
2079
			$scconf['connections']['bypass']['children']["rule{$id}"] = array();
2080
			$scconf['connections']['bypass']['children']["rule{$id}"]["local_ts"] = $rule['source'] .
2081
			       	"/" . $rule['srcmask'];
2082
			$scconf['connections']['bypass']['children']["rule{$id}"]["remote_ts"] = $rule['destination'] .
2083
			       	"/" . $rule['dstmask'];
2084
			$scconf['connections']['bypass']['children']["rule{$id}"]["mode"] = "pass";
2085
			$scconf['connections']['bypass']['children']["rule{$id}"]["start_action"] = "trap";
2086
		}
2087
	}
2088

    
2089
	return;
2090
}
2091

    
2092
/****f* ipsec/ipsec_setup_routes
2093
 * NAME
2094
 *   ipsec_setup_routes - Setup OS routing table static routes for remote peers.
2095
 *                        This ensures that IPsec connections are routed out of
2096
 *                        the expected interface on egress.
2097
 * INPUTS
2098
 *   $interface : The interface upon which routes will be configured.
2099
 *   $family    : The address family ('inet' or 'inet6')
2100
 *   $sourcehost: The local source address used for initiating connections.
2101
 *   $duplicates: If the ipsec tunnel allows duplicates remote peers
2102
 * RESULT
2103
 *   Static routes in the OS routing table for IPsec peers
2104
 ******/
2105
function ipsec_setup_routes($interface, $family, $sourcehost, $duplicates) {
2106
	if (substr($interface, 0, 4) == "_vip") {
2107
		$vpninterface = get_configured_vip_interface($interface);
2108
		if (substr($vpninterface, 0, 4) == "_vip") {
2109
			// vips are nested if its a ipalias with a carp parent
2110
			$vpninterface = get_configured_vip_interface($vpninterface);
2111
		}
2112
		$ifacesuse = get_real_interface($vpninterface);
2113
	} else {
2114
		$ifacesuse = get_failover_interface($interface);
2115
		if (substr($ifacesuse, 0, 4) == "_vip") {
2116
			$vpninterface = get_configured_vip_interface($ifacesuse);
2117
			$ifacesuse = get_real_interface($vpninterface);
2118
		} else {
2119
			$vpninterface = convert_real_interface_to_friendly_interface_name($ifacesuse);
2120
		}
2121
	}
2122
	if ($family == 'inet' && !empty($ifacesuse) &&
2123
	    interface_has_gateway($vpninterface)) {
2124
		$gatewayip = get_interface_gateway($vpninterface);
2125
		$interfaceip = get_interface_ip($vpninterface);
2126
		$subnet_bits = get_interface_subnet($vpninterface);
2127
		$subnet_ip = gen_subnetv4($interfaceip, $subnet_bits);
2128
		/*
2129
		 * if the remote gateway is in the local subnet, then don't add
2130
		 * a route
2131
		 */
2132
		if (is_ipaddrv4($sourcehost) &&
2133
		    !ip_in_subnet($sourcehost, "{$subnet_ip}/{$subnet_bits}") &&
2134
		    is_ipaddrv4($gatewayip) && !$duplicates) {
2135
			route_add_or_change($sourcehost, $gatewayip);
2136
		}
2137
	} else if ($family == 'inet6' && !empty($ifacesuse) &&
2138
	    interface_has_gatewayv6($vpninterface)) {
2139
		$gatewayip = get_interface_gateway_v6($vpninterface);
2140
		$interfaceip = get_interface_ipv6($vpninterface);
2141
		$subnet_bits = get_interface_subnetv6($vpninterface);
2142
		$subnet_ip = gen_subnetv6($interfaceip, $subnet_bits);
2143
		/*
2144
		 * if the remote gateway is in the local subnet, then don't add
2145
		 * a route
2146
		 */
2147
		if (is_ipaddrv6($sourcehost) &&
2148
		    !ip_in_subnet($sourcehost, "{$subnet_ip}/{$subnet_bits}") &&
2149
		    is_ipaddrv6($gatewayip) && !$duplicates) {
2150
			route_add_or_change($sourcehost, $gatewayip);
2151
		}
2152
	}
2153
	return $ifacesuse;
2154
}
2155

    
2156
/****f* ipsec/ipsec_setup_authentication
2157
 * NAME
2158
 *   ipsec_setup_authentication - Generate an array with local/remote
2159
 *                                authentication information for a given IPsec
2160
 *                                Phase 1.
2161
 * INPUTS
2162
 *   $ph1ent: An IPsec Phase 1 configuration
2163
 *   $conn  : A swanctl connection array corresponding to the IPsec Phase 1.
2164
 * RESULT
2165
 *   Populates $conn with local and remote arrays containing authentication
2166
 *   details.
2167
 ******/
2168
function ipsec_setup_authentication(& $ph1ent, & $conn, $gateways_status = false) {
2169
	global $rgmap, $ipsec_swanctl_dirs, $config;
2170
	$local = array();
2171
	$remote = array();
2172
	$remote2 = array();
2173

    
2174
	/* Fetch gateway status if not passed */
2175
	if (!is_array($gateways_status)) {
2176
		$gateways_status = return_gateways_status(true);
2177
	}
2178

    
2179
	list($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local', array(), $gateways_status);
2180
	$localid = ipsec_fixup_id($myid_type, $myid_data);
2181
	if (!empty($localid)) {
2182
		$local['id'] = $localid;
2183
	}
2184

    
2185
	// Only specify peer ID if we are not dealing with mobile PSK
2186
	if (!(isset($ph1ent['mobile']) &&
2187
	    in_array($ph1ent['authentication_method'], array("pre_shared_key", "xauth_psk_server")))) {
2188
		list ($remoteid_type, $remoteid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap, $gateways_status);
2189
		$remoteid = ipsec_fixup_id($remoteid_type, $remoteid_data);
2190
	}
2191
	if (!empty($remoteid)) {
2192
		$remote['id'] = $remoteid;
2193
	}
2194

    
2195
	if (!empty($ph1ent['caref'])) {
2196
		$ca = lookup_ca($ph1ent['caref']);
2197
		if ($ca) {
2198
			/* Get hash value to use for filename */
2199
			$ca_details = openssl_x509_parse(base64_decode($ca['crt']));
2200
			$cafn = "{$ipsec_swanctl_dirs['capath']}/{$ca_details['hash']}.0";
2201
		}
2202
	}
2203

    
2204
	$authentication = "";
2205
	switch ($ph1ent['authentication_method']) {
2206
		case 'eap-mschapv2':
2207
			if (isset($ph1ent['mobile'])) {
2208
				$local['auth'] = "pubkey";
2209
				$remote['eap_id'] = "%any";
2210
				$remote['auth'] = "eap-mschapv2";
2211
			}
2212
			break;
2213
		case 'eap-tls':
2214
			if (isset($ph1ent['mobile'])) {
2215
				$local['auth'] = "pubkey";
2216
				$remote['eap_id'] = "%any";
2217
			} else {
2218
				$local['auth'] = "eap-tls";
2219
			}
2220
			$remote['auth'] = "eap-tls";
2221
			break;
2222
		case 'eap-radius':
2223
			if (isset($ph1ent['mobile'])) {
2224
				$local['auth'] = "pubkey";
2225
				$remote['eap_id'] = "%any";
2226
			} else {
2227
				$local['auth'] = "eap-radius";
2228
			}
2229
			if (($config['ipsec']['client']['group_source'] == 'enabled') &&
2230
			    !empty($config['ipsec']['client']['auth_groups'])) {
2231
				$remote['groups'] = config_get_path('ipsec/client/auth_groups');
2232
			}
2233
			$remote['auth'] = "eap-radius";
2234
			break;
2235
		case 'xauth_cert_server':
2236
			$local['auth'] = "pubkey";
2237
			$remote['auth'] = "pubkey";
2238
			$remote2['auth'] = "xauth-generic";
2239
			break;
2240
		case 'xauth_psk_server':
2241
			$local['auth'] = "psk";
2242
			$remote['auth'] = "psk";
2243
			$remote2['auth'] = "xauth-generic";
2244
			break;
2245
		case 'pre_shared_key':
2246
			$local['auth'] = "psk";
2247
			$remote['auth'] = "psk";
2248
			break;
2249
		case 'cert':
2250
		case 'pkcs11':
2251
			$local['auth'] = "pubkey";
2252
			$remote['auth'] = "pubkey";
2253
			break;
2254
		case 'hybrid_cert_server':
2255
			$local['auth'] = "pubkey";
2256
			$remote['auth'] = "xauth-generic";
2257
			break;
2258
	}
2259
	if (in_array($ph1ent['authentication_method'], array('eap-mschapv2', 'eap-tls', 'eap-radius', 'xauth_cert_server', 'cert', 'hybrid_cert_server')) &&
2260
	    !empty($ph1ent['certref'])) {
2261
		$local['cert'] = array('file' => "{$ipsec_swanctl_dirs['certpath']}/cert-{$ph1ent['ikeid']}.crt");
2262
		$conn['send_cert'] = "always";
2263
	}
2264
	if ($ph1ent['authentication_method'] == 'pkcs11') {
2265
		$local['cert'] = array('handle' => "{$ph1ent['pkcs11certref']}");
2266
		$conn['send_cert'] = "always";
2267
	}
2268
	if (in_array($ph1ent['authentication_method'], array('eap-tls', 'xauth_cert_server', 'cert', 'pkcs11')) &&
2269
	    isset($cafn)) {
2270
		$remote['cacerts'] = $cafn;
2271
		if (isset($config['ipsec']['strictcrlpolicy'])) {
2272
			$remote['revocation'] = "strict";
2273
		}
2274
	}
2275

    
2276
	$conn['local'] = $local;
2277
	/* If there is data for a second remote auth round, setup two remote
2278
	 * arrays with unique names, otherwise setup a single remote. */
2279
	if (empty($remote2)) {
2280
		$conn['remote'] = $remote;
2281
	} else {
2282
		$conn['remote-1'] = $remote;
2283
		$conn['remote-2'] = $remote2;
2284
	}
2285
}
2286

    
2287
/****f* ipsec/ipsec_setup_proposal_algo
2288
 * NAME
2289
 *   ipsec_setup_proposal_algo - Form a single proposal algorithm string
2290
 * INPUTS
2291
 *   $ealg_id: Encryption algorithm ID
2292
 *   $keylen : Key length for the encryption algorithm
2293
 *   $halgo  : Hash algorithm
2294
 *   $modp   : DH Group number
2295
 * RESULT
2296
 *   Returns a string using the available information to form a single proposal
2297
 *   algorithm definition.
2298
 * NOTES
2299
 *   Values left empty will not be added to the string. Some combinations may
2300
 *   require one or more parts to be omitted.
2301
 ******/
2302
function ipsec_setup_proposal_algo($ealg_id, $keylen, $halgo, $prfalgo, $modp) {
2303
	$palgo = "";
2304

    
2305
	/* Add the encryption algorithm (if present) */
2306
	if (!empty($ealg_id)) {
2307
		$palgo .= "{$ealg_id}";
2308
	}
2309

    
2310
	/* Add the key length (if present) */
2311
	if (!empty($keylen)) {
2312
		$palgo .= "{$keylen}";
2313
	}
2314

    
2315
	/* Add the hash algorithm (if present) */
2316
	if (!empty($halgo)) {
2317
		/* If there is some content in the proposal already, add a
2318
		 * separator */
2319
		if (!empty($palgo)) {
2320
			$palgo .= "-";
2321
		}
2322
		$halgo = str_replace('hmac_', '', $halgo);
2323
		$palgo .= "{$halgo}";
2324
	}
2325

    
2326
	if (!empty($prfalgo)) {
2327
		$palgo .= "-prf{$prfalgo}";
2328
	}
2329

    
2330
	/* Convert the DH group to its keyword and add (if present) */
2331
	$modp = ipsec_convert_to_modp($modp);
2332
	if (!empty($modp)) {
2333
		$palgo .= "-{$modp}";
2334
	}
2335

    
2336
	return $palgo;
2337
}
2338

    
2339
/****f* ipsec/ipsec_setup_proposal_entry
2340
 * NAME
2341
 *   ipsec_setup_proposal_entry - Generate a full proposal string for an IPsec
2342
 *                                Phase 2 configuration.
2343
 * INPUTS
2344
 *   $ph2ent  : An IPsec Phase 2 configuration
2345
 *   $algo_arr: An array in which all proposal algorithms are collected
2346
 *   $ealg_id : Encryption algorithm ID
2347
 *   $keylen  : Key length for the encryption algorithm
2348
 * RESULT
2349
 *   Returns a string containing all proposal elements, and merges the entries
2350
 *   to $algo_arr as well.
2351
 ******/
2352
function ipsec_setup_proposal_entry(& $ph2ent, & $algo_arr, $ealg_id, $keylen) {
2353
	global $config, $p2_ealgos;
2354
	$proposal = array();
2355

    
2356
	/* If multiple hash algorithms are present, loop through and add them all. */
2357
	if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option']) 
2358
	    && !(strpos($ealg_id, "gcm") || $ealg_id == "chacha20poly1305")) {
2359
		foreach ($ph2ent['hash-algorithm-option'] as $halgo) {
2360
			$proposal[] = ipsec_setup_proposal_algo($ealg_id, $keylen, $halgo, false, $ph2ent['pfsgroup']);
2361
		}
2362
	} else {
2363
		$proposal[] = ipsec_setup_proposal_algo($ealg_id, $keylen, false, false, $ph2ent['pfsgroup']);
2364
	}
2365

    
2366
	/* Add to master list */
2367
	$algo_arr = array_merge($algo_arr, $proposal);
2368
	return implode(',', $proposal);
2369
}
2370

    
2371
function ipsec_get_ifname($p1, $reqid = 0) {
2372
	global $config;
2373
	$ifname = "ipsec";
2374
	if ($reqid > 0) {
2375
		$ifname .= "{$reqid}";
2376
	} else {
2377
		$p2s = ipsec_get_phase2_by_ikeid($p1['ikeid']);
2378
		if (!empty($p2s)) {
2379
			$ifname .= $p2s[0]['reqid'];
2380
		}
2381
	}
2382
	return $ifname;
2383
}
2384

    
2385
function ipsec_get_ifname_by_conf($p1 = array(), $p2 = array()) {
2386
	global $config;
2387
	$have_p1 = (!empty($p1) && is_array($p1));
2388
	$have_p2 = (!empty($p2) && is_array($p2));
2389
	$reqid = null;
2390

    
2391
	if (!$have_p1 && $have_p2) {
2392
		$p1 = ipsec_get_phase1($p2['ikeid']);
2393
	}
2394

    
2395
	if ($have_p2) {
2396
		$reqid = $p2['reqid'];
2397
	}
2398

    
2399
	return ipsec_get_ifname($p1, $reqid);
2400
}
2401

    
2402
/****f* ipsec/ipsec_setup_vtireq
2403
 * NAME
2404
 *   ipsec_setup_vtireq - Setup a VTI type IPsec request
2405
 * INPUTS
2406
 *   $child                : A swanctl child array
2407
 *   $ipsec_vti_cleanup_ifs: An array of VTI interface names for later cleanup
2408
 *   $p1                   : The IKE config under which this child exists
2409
 *   $reqid                : The reqid of the P2 if known, otherwise the first P2 reqid for this P1 is looked up.
2410
 * RESULT
2411
 *   Sets up VTI-specific values for a request.
2412
 ******/
2413
function ipsec_setup_vtireq(& $child, & $ipsec_vti_cleanup_ifs, $p1, $reqid = 0) {
2414
	global $ipsec_reqid_base;
2415
	if ($reqid > 0) {
2416
		$child['reqid'] = $ipsec_reqid_base + $reqid;
2417
	} else {
2418
		$p2s = ipsec_get_phase2_by_ikeid($p1['ikeid']);
2419
		if (!empty($p2s)) {
2420
			$reqid = $p2s[0]['reqid'];
2421
			$child['reqid'] = $ipsec_reqid_base + $reqid;
2422
		} else {
2423
			return false;
2424
		}
2425
	}
2426
	/* This interface will be a valid IPsec interface, so remove it from the cleanup list. */
2427
	$ipsec_vti_cleanup_ifs = array_diff($ipsec_vti_cleanup_ifs, array(ipsec_get_ifname($p1, $reqid)));
2428
	$child['local_ts'] .= ",0.0.0.0/0,::/0";
2429
	$child['remote_ts'] .= ",0.0.0.0/0,::/0";
2430
	return true;
2431
}
2432

    
2433
/****f* ipsec/ipsec_setup_tunnels
2434
 * NAME
2435
 *   ipsec_setup_tunnels - Configure all P1/P2 entries as swanctl connections
2436
 * INPUTS
2437
 *   None
2438
 * RESULT
2439
 *   Sets up a swanctl array for all connections in the configuration along with
2440
 *   their children, authentication, etc.
2441
 ******/
2442
function ipsec_setup_tunnels($gateways_status = false) {
2443
	global $aggressive_mode_psk, $a_client, $config,
2444
		$filterdns_list, $g, $ifacesuse, $ipsec_idhandling, $ipsec_log_cats,
2445
		$ipsec_log_sevs, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs,
2446
		$ipseccfg, $mobile_ipsec_auth, $natfilterrules, $p1_ealgos,
2447
		$p2_ealgos, $rgmap, $sa, $sn, $scconf, $conn, $tunnels,
2448
		$ipsec_vti_cleanup_ifs, $conn_defaults;
2449

    
2450
	/* Fetch gateway status if not passed */
2451
	if (!is_array($gateways_status)) {
2452
		$gateways_status = return_gateways_status(true);
2453
	}
2454

    
2455
	$a_groups = return_gateway_groups_array(true);
2456

    
2457
	foreach ($tunnels as $ph1ent) {
2458
		/* Skip disabled entries */
2459
		if (isset($ph1ent['disabled'])) {
2460
			continue;
2461
		}
2462
		/* If the local source is invalid, skip this entry. */
2463
		$local_spec = ipsec_get_phase1_src($ph1ent, $gateways_status);
2464
		if (!$local_spec) {
2465
			continue;
2466
		}
2467

    
2468
		/* Determine the name of this connection, either con-mobile for
2469
		 * mobile clients, or a name based on the IKE ID otherwise. */
2470
		if (isset($ph1ent['mobile'])) {
2471
			$cname = "con-mobile";
2472
			/* Start with common default values */
2473
			$scconf["{$cname}-defaults"] = $conn_defaults;
2474
			/* Array reference to make things easier */
2475
			$conn =& $scconf["{$cname}-defaults"];
2476
			$scconf['connections']["{$cname} : {$cname}-defaults"] = array("# Stub to load con-mobile-defaults");
2477
		} else {
2478
			$cname = ipsec_conid($ph1ent);
2479
			/* Start with common default values */
2480
			$scconf['connections'][$cname] = $conn_defaults;
2481
			$descr = "# P1 (ikeid {$ph1ent['ikeid']})";
2482
			if (!empty($ph1ent['descr'])) {
2483
				$descr .= ": {$ph1ent['descr']}";
2484
			}
2485
			array_unshift($scconf['connections'][$cname], $descr);
2486
			/* Array reference to make things easier */
2487
			$conn =& $scconf['connections'][$cname];
2488
		}
2489

    
2490
		/* Common parameters for all children */
2491
		$child_params = array();
2492
		if (!empty($ph1ent['closeaction'])) {
2493
			$child_params['close_action'] = $ph1ent['closeaction'];
2494
		}
2495

    
2496
		$ikeid = $ph1ent['ikeid'];
2497

    
2498
		$carpbackup = false;
2499
		if (((substr($ph1ent['interface'], 0, 4) == "_vip") &&
2500
		    in_array(get_carp_bind_status($ph1ent['interface']), array('BACKUP', 'INIT'))) ||
2501
		    (is_array($a_groups[$ph1ent['interface']]) &&
2502
		    !empty($a_groups[$ph1ent['interface']][0]['vip']) &&
2503
		    in_array(get_carp_bind_status($a_groups[$ph1ent['interface']][0]['vip']), array('BACKUP', 'INIT')))) {
2504
			$carpbackup = true;
2505
		}
2506

    
2507
		/* "trap" adds policies to start a tunnel when interesting
2508
		 * traffic is observed by the host. */
2509
		$start_action = "trap";
2510
		if ($carpbackup) {
2511
			$start_action = "none";
2512
		}
2513

    
2514
		/* Set the IKE version appropriately (empty = IKEv1) */
2515
		switch ($ph1ent['iketype']) {
2516
			case 'auto':
2517
				$ikeversion = 0;
2518
				break;
2519
			case 'ikev2':
2520
				$ikeversion = 2;
2521
				break;
2522
			case 'ikev1':
2523
			default:
2524
				$ikeversion = 1;
2525
				break;
2526
		}
2527
		$conn['version'] = $ikeversion;
2528

    
2529
		/* For IKEv1 or auto, setup aggressive mode if configured */
2530
		if ($ikeversion != 2){
2531
			$conn['aggressive'] = ($ph1ent['mode'] == "aggressive") ? "yes" : "no";
2532
		}
2533

    
2534
		if (isset($ph1ent['mobile'])) {
2535
			/* Mobile tunnels allow 'any' as a peer */
2536
			$remote_spec = "0.0.0.0/0,::/0";
2537
			/* Mobile tunnels cannot start automatically */
2538
			$start_action = 'none';
2539
		} else {
2540
			$remote_spec = $ph1ent['remote-gateway'];
2541
			$sourcehost = (is_ipaddr($remote_spec)) ? $remote_spec : $rgmap[$remote_spec];
2542
			$ifacesuse = ipsec_setup_routes($ph1ent['interface'], $ph1ent['protocol'], $sourcehost, isset($ph1ent['gw_duplicates']));
2543
		}
2544

    
2545
		/* Setup IKE proposals */
2546
		if (is_array($ph1ent['encryption']['item'])) {
2547
			$ciphers = array();
2548
			foreach($ph1ent['encryption']['item'] as $p1enc) {
2549
				if (!is_array($p1enc['encryption-algorithm']) ||
2550
						empty($p1enc['encryption-algorithm']['name']) ||
2551
						empty($p1enc['hash-algorithm'])) {
2552
					continue;
2553
				}
2554
				if ($ph1ent['prfselect_enable'] != 'yes') {
2555
					$p1enc['prf-algorithm'] = false;
2556
				}
2557
				$ciphers[] = ipsec_setup_proposal_algo($p1enc['encryption-algorithm']['name'],
2558
									$p1enc['encryption-algorithm']['keylen'],
2559
									$p1enc['hash-algorithm'],
2560
									$p1enc['prf-algorithm'],
2561
									$p1enc['dhgroup']);
2562
			}
2563
			$conn['proposals'] = implode(",", $ciphers);
2564
		}
2565

    
2566
		/* Configure DPD values. The DPD action is a per-child parameter,
2567
		 * not per-connection like the delay and timeout. */
2568
		if ($ph1ent['dpd_delay'] && $ph1ent['dpd_maxfail']) {
2569
			if ($start_action == "trap") {
2570
				$child_params['dpd_action'] = "trap";
2571
			} else {
2572
				$child_params['dpd_action'] = "clear";
2573
			}
2574
			$conn['dpd_delay'] = "{$ph1ent['dpd_delay']}s";
2575
			if ($ph1ent['iketype'] == 'ikev1') {
2576
				/* in IKEv2 the default retransmission timeout applies,
2577
				 * see https://redmine.pfsense.org/issues/12184 */
2578
				$conn['dpd_timeout'] =  $ph1ent['dpd_delay'] * ($ph1ent['dpd_maxfail'] + 1) . "s";
2579
			}
2580

    
2581
			/* Adjust dpd_action if the close_action is explicitly set */
2582
			if (!empty($child_params['close_action'])) {
2583
				switch ($ph1ent['closeaction']) {
2584
					case 'trap':
2585
						$child_params['dpd_action'] = 'trap';
2586
						break;
2587
					case 'start':
2588
						$child_params['dpd_action'] = 'restart';
2589
						break;
2590
					case 'none':
2591
					default:
2592
						$child_params['dpd_action'] = 'clear';
2593
				}
2594
			}
2595
		} else {
2596
			$child_params['dpd_action'] = "clear";
2597
		}
2598

    
2599
		/* Rekey (IKEv2 or Auto only, not supported by IKEv1) */
2600
		if (($ikeversion == 0) || ($ikeversion == 2)) {
2601
			$conn['rekey_time'] = ipsec_get_rekey_time($ph1ent) . "s";
2602
		}
2603

    
2604
		/* Reauth (Any) */
2605
		$conn['reauth_time'] = ipsec_get_reauth_time($ph1ent) . "s";
2606

    
2607
		/* Over Time */
2608
		$conn['over_time'] = ipsec_get_over_time($ph1ent) . "s";
2609

    
2610
		/* Rand Time */
2611
		$conn['rand_time'] = ipsec_get_rand_time($ph1ent) . "s";
2612

    
2613
		/* NAT Traversal */
2614
		$conn['encap'] = ($ph1ent['nat_traversal'] == 'force') ? "yes" : "no";
2615

    
2616
		/* MOBIKE support */
2617
		$conn['mobike'] = ($ph1ent['mobike'] == 'on') ? "yes" : "no";
2618

    
2619
		/* TFC Padding */
2620
		if (isset($ph1ent['tfc_enable'])) {
2621
			$conn['tfc_padding'] = (isset($ph1ent['tfc_bytes']) && is_numericint($ph1ent['tfc_bytes'])) ? $ph1ent['tfc_bytes'] : "mtu";
2622
		}
2623

    
2624
		/* Custom Ports */
2625
		if (isset($ph1ent['ikeport'])) {
2626
			/* For a connection without encapsulation, do not set local_port */
2627
			$conn['remote_port'] = $ph1ent['ikeport'];
2628
		} elseif (isset($ph1ent['nattport'])) {
2629
			/* For an encapsulated connection,
2630
			 * set local_port to the server NAT-T port value or default (4500) */
2631
			$conn['local_port'] = isset($config['ipsec']['port_nat_t']) ? $config['ipsec']['port_nat_t'] : 4500;
2632
			$conn['remote_port'] = $ph1ent['nattport'];
2633
		}
2634

    
2635
		/* Arrays for P2s/children */
2636
		$children = array();
2637
		$remote_ts_spec = array();
2638
		$local_ts_spec = array();
2639
		$reqids = array();
2640
		$has_vti = false;
2641
		$ealgoAHsp2arr = array();
2642
		$ealgoESPsp2arr = array();
2643

    
2644
		foreach ($ph1ent['p2'] as $ph2ent) {
2645
			/* If this entry is disabled, or cannot be configured, skip. */
2646
			if (isset($ph2ent['disabled']) ||
2647
			    (isset($ph2ent['mobile']) && !isset($a_client['enable']))) {
2648
				continue;
2649
			}
2650
			$child = array();
2651
			$local_ts = "";
2652
			$remote_ts = "";
2653
			if (($ph2ent['mode'] == 'tunnel') or ($ph2ent['mode'] == 'tunnel6')) {
2654
				/* Normal tunnel child config */
2655
				$child['mode'] = "tunnel";
2656
				$child['policies'] = "yes";
2657

    
2658
				$localid_type = $ph2ent['localid']['type'];
2659
				$localsubnet_data = ipsec_idinfo_to_cidr($ph2ent['localid'], false, $ph2ent['mode']);
2660

    
2661
				/* Do not print localid in some cases, such as a pure-psk or psk/xauth single phase2 mobile tunnel */
2662
				if (($localid_type == "none" || $localid_type == "mobile") &&
2663
				    isset($ph1ent['mobile']) && (ipsec_get_number_of_phase2($ikeid) == 1)) {
2664
					$local_spec = '0.0.0.0/0,::/0';
2665
				} else {
2666
					if ($localid_type != "address") {
2667
						$localid_type = "subnet";
2668
					}
2669
					// Don't let an empty subnet into config, it can cause parse errors. Ticket #2201.
2670
					if (!is_ipaddr($localsubnet_data) && !is_subnet($localsubnet_data) && ($localsubnet_data != "0.0.0.0/0")) {
2671
						log_error("Invalid IPsec Phase 2 \"{$ph2ent['descr']}\" - {$ph2ent['localid']['type']} has no subnet.");
2672
						continue;
2673
					}
2674
					if (!empty($ph2ent['natlocalid'])) {
2675
						$natlocalsubnet_data = ipsec_idinfo_to_cidr($ph2ent['natlocalid'], false, $ph2ent['mode']);
2676
						if ($ph2ent['natlocalid']['type'] != "address") {
2677
							if (is_subnet($natlocalsubnet_data)) {
2678
								$localsubnet_data = "{$natlocalsubnet_data}|{$localsubnet_data}";
2679
							}
2680
						} else {
2681
							if (is_ipaddr($natlocalsubnet_data)) {
2682
								$localsubnet_data = "{$natlocalsubnet_data}|{$localsubnet_data}";
2683
							}
2684
						}
2685
						$natfilterrules = true;
2686
					}
2687
				}
2688

    
2689
				$local_ts = $localsubnet_data;
2690

    
2691
				if (!isset($ph2ent['mobile'])) {
2692
					$remote_ts = ipsec_idinfo_to_cidr($ph2ent['remoteid'], false, $ph2ent['mode']);
2693
				} else if (!empty($a_client['pool_address'])) {
2694
					$remote_ts = "{$a_client['pool_address']}/{$a_client['pool_netbits']}";
2695
				}
2696

    
2697
			} elseif ($ph2ent['mode'] == 'vti') {
2698
				/* VTI-specific child config */
2699
				$child['policies'] = "no";
2700
				/* VTI cannot use trap policies, so start automatically instead */
2701
				$start_action = 'start';
2702
				if ($carpbackup) {
2703
					$start_action = "none";
2704
				}
2705
				if ($child_params['dpd_action'] == "trap") {
2706
					$child_params['dpd_action'] = "restart";
2707
				}
2708
				$localid_type = $ph2ent['localid']['type'];
2709
				$localsubnet_data = ipsec_idinfo_to_cidr($ph2ent['localid'], false, $ph2ent['mode']);
2710
				$local_ts = $localsubnet_data;
2711
				$remote_ts = ipsec_idinfo_to_cidr($ph2ent['remoteid'], false, $ph2ent['mode']);
2712
				$has_vti = true;
2713
			} else {
2714
				/* Transport mode child config */
2715
				$child['mode'] = "transport";
2716
				$child['policies'] = "yes";
2717

    
2718
				if ((($ph1ent['authentication_method'] == "xauth_psk_server") ||
2719
				    ($ph1ent['authentication_method'] == "pre_shared_key")) &&
2720
				    isset($ph1ent['mobile'])) {
2721
					$local_spec = "0.0.0.0/0,::/0";
2722
				} else {
2723
					$local_ts = ipsec_get_phase1_src($ph1ent, $gateways_status);
2724
				}
2725

    
2726
				if (!isset($ph2ent['mobile'])) {
2727
					$remote_ts = $remote_spec;
2728
				}
2729
			}
2730

    
2731
			if (!empty($local_ts)) {
2732
				$local_ts_spec[] = $local_ts;
2733
			}
2734
			if (!empty($remote_ts)) {
2735
				$remote_ts_spec[] = $remote_ts;
2736
			}
2737

    
2738
			/* If a PFS group is configured on the Mobile Clients tab,
2739
			 * and this is a mobile P2, use it here. */
2740
			if (isset($a_client['pfs_group']) && isset($ph2ent['mobile'])) {
2741
				$ph2ent['pfsgroup'] = $a_client['pfs_group'];
2742
			}
2743

    
2744
			$reqids[] = $ph2ent['reqid'];
2745

    
2746
			if (!empty($ph2ent['lifetime'])) {
2747
				$child['life_time'] = ipsec_get_life_time($ph2ent) . "s";
2748
				/* Rekey at 90% of lifetime */
2749
				$child['rekey_time'] = ipsec_get_rekey_time($ph2ent) . "s";
2750
				/* Random rekey fuzz time */
2751
				$child['rand_time'] = ipsec_get_rand_time($ph2ent) . "s";
2752
			}
2753

    
2754
			/* Set Child SA Start Action based on user preference, except
2755
			 * for invalid combinations such as 'trap' on VTI */
2756
			if (!isset($ph2ent['mobile']) && !$carpbackup &&
2757
			    !(($ph2ent['mode'] == 'vti') && ($ph1ent['startaction'] == 'trap'))) {
2758
				$start_action = !empty($ph1ent['startaction']) ? $ph1ent['startaction'] : $start_action;
2759
			}
2760
			$child['start_action'] = $start_action;
2761

    
2762
			/* Setup child SA proposals */
2763
			$proposal = array();
2764
			$ph2ent_ealgos = array();
2765
			$ph2ent_ealgos_aead = array();
2766
			if ($ph2ent['protocol'] == 'esp') {
2767
				if (is_array($ph2ent['encryption-algorithm-option'])) {
2768
					foreach ($ph2ent['encryption-algorithm-option'] as $ealg) {
2769
						if (strpos($ealg['name'], "gcm")) {
2770
							/* put AEAD ciphers on top, 
2771
							*  see https://redmine.pfsense.org/issues/11078 */
2772
							$ph2ent_ealgos_aead[] = $ealg;
2773
						} else {
2774
							$ph2ent_ealgos[] = $ealg;
2775
						}
2776
					}
2777
					$ph2ent_ealgos = array_merge(array_reverse($ph2ent_ealgos_aead), $ph2ent_ealgos);
2778
					foreach ($ph2ent_ealgos as $ealg) {
2779
						$ealg_id = $ealg['name'];
2780
						$ealg_kl = $ealg['keylen'];
2781

    
2782
						if (!empty($ealg_kl) && $ealg_kl == "auto") {
2783
							if (empty($p2_ealgos) || !is_array($p2_ealgos)) {
2784
								require_once("ipsec.inc");
2785
							}
2786
							$key_hi = $p2_ealgos[$ealg_id]['keysel']['hi'];
2787
							$key_lo = $p2_ealgos[$ealg_id]['keysel']['lo'];
2788
							$key_step = $p2_ealgos[$ealg_id]['keysel']['step'];
2789
							/* XXX: in some cases where include ordering is suspect these variables
2790
							 * are somehow 0 and we enter this loop forever and timeout after 900
2791
							 * seconds wrecking bootup */
2792
							if ($key_hi != 0 and $key_lo != 0 and $key_step != 0) {
2793
								for ($keylen = $key_hi; $keylen >= $key_lo; $keylen -= $key_step) {
2794
									$proposal[] = ipsec_setup_proposal_entry($ph2ent, $ealgoESPsp2arr, $ealg_id, $keylen);
2795
								}
2796
							}
2797
						} else {
2798
							$proposal[] = ipsec_setup_proposal_entry($ph2ent, $ealgoESPsp2arr, $ealg_id, $ealg_kl);
2799
						}
2800
					}
2801
				}
2802
			} else if ($ph2ent['protocol'] == 'ah') {
2803
				$proposal[] = ipsec_setup_proposal_entry($ph2ent, $ealgoAHsp2arr, '', '');
2804
			}
2805

    
2806
			/* Not mobile, and IKEv1 or Split Connections active */
2807
			if (!isset($ph1ent['mobile']) && (($ikeversion == 1) || isset($ph1ent['splitconn']))) {
2808
				if (!empty($remote_ts)) {
2809
					/* Setup child sub-connections using unique names */
2810
					$subconname = ipsec_conid($ph1ent, $ph2ent);
2811
					$children[$subconname] = $child;
2812

    
2813
					$descr = "# P2 (reqid {$ph2ent['reqid']})";
2814
					if (!empty($ph2ent['descr'])) {
2815
						$descr .= ": {$ph2ent['descr']}";
2816
					}
2817
					array_unshift($children[$subconname], $descr);
2818
					$children[$subconname]['local_ts'] = $local_ts;
2819
					$children[$subconname]['remote_ts'] = $remote_ts;
2820
					if ($has_vti) {
2821
						ipsec_setup_vtireq($children[$subconname], $ipsec_vti_cleanup_ifs, $ph1ent, $ph2ent['reqid']);
2822
					}
2823
					if (!empty($ph2ent['protocol']) && !empty($proposal)) {
2824
						$children[$subconname][$ph2ent['protocol'] . '_proposals'] = implode(',', $proposal);
2825
					}
2826
				} else {
2827
					log_error(sprintf(gettext("No phase2 specifications for tunnel with ikeid = %s"), $ikeid));
2828
				}
2829
			} else {
2830
				/* TODO: Fix this to nicely merge all P2 params for single child case */
2831
				if (!is_array($children[$cname])) {
2832
					$children[$cname] = array();
2833
				}
2834
				$children[$cname] = array_merge($children[$cname], $child);
2835

    
2836
				$descr = "# P2 (reqid {$ph2ent['reqid']})";
2837
				if (!empty($ph2ent['descr'])) {
2838
					$descr .= ": {$ph2ent['descr']}";
2839
				}
2840
				array_unshift($children[$cname], $descr);
2841
			}
2842
		}
2843

    
2844
		$conn['local_addrs'] = $local_spec;
2845
		$conn['remote_addrs'] = $remote_spec;
2846

    
2847
		$pools = array();
2848
		if (isset($ph1ent['mobile'])) {
2849
			if (($ph1ent['authentication_method'] == 'eap-radius') && 
2850
			    empty($a_client['pool_address']) && empty($a_client['pool_address_v6'])) {
2851
				$pools[] = "radius";
2852
			} else {
2853
				if (!empty($a_client['pool_address'])) {
2854
					$pools[] = "mobile-pool-v4";
2855
				}
2856
				if (!empty($a_client['pool_address_v6'])) {
2857
					$pools[] = "mobile-pool-v6";
2858
				}
2859
				if (isset($a_client['radius_ip_priority_enable'])) {
2860
					$pools[] .= "radius";
2861
				}
2862
			}
2863
		}
2864
		if (!empty($pools)) {
2865
			$conn['pools'] = implode(', ', $pools);
2866
		}
2867

    
2868
		/* For IKEv2 without Split Connections, setup combined sets of
2869
		 * local/remote traffic selectors and proposals */
2870
		if (!(!isset($ph1ent['mobile']) && (($ikeversion == 1) || isset($ph1ent['splitconn'])))) {
2871
			if (!isset($ph1ent['mobile']) && !empty($remote_ts_spec)) {
2872
				$children[$cname]['remote_ts'] = implode(",", $remote_ts_spec);
2873
			}
2874
			if (!empty($local_ts_spec)) {
2875
				$children[$cname]['local_ts'] = implode(",", $local_ts_spec);
2876
			}
2877
			if ($has_vti) {
2878
				ipsec_setup_vtireq($children[$cname], $ipsec_vti_cleanup_ifs, $ph1ent);
2879
			}
2880
			if (!empty($ealgoAHsp2arr)) {
2881
				$halgos = array();
2882
				$halgos_sha = array();
2883
				foreach ($ealgoAHsp2arr as $halg) {
2884
					if (strstr($halg, "sha")) {
2885
						/* put SHA ciphers on top, 
2886
						*  see https://redmine.pfsense.org/issues/12323 */
2887
						$halgos_sha[] = $halg;
2888
					} else {
2889
						$halgos[] = $halg;
2890
					}
2891
				}
2892
				$ealgoAHsp2arr = array_merge(array_reverse($halgos_sha), $halgos);
2893
				$children[$cname]['ah_proposals'] = implode(',', array_unique($ealgoAHsp2arr));
2894
			}
2895
			if (!empty($ealgoESPsp2arr)) {
2896
				$children[$cname]['esp_proposals'] = implode(',', array_unique($ealgoESPsp2arr));
2897
			}
2898
		}
2899

    
2900
		/* Setup connection authentication */
2901
		ipsec_setup_authentication($ph1ent, $conn, $gateways_status);
2902

    
2903
		/* Add children to this connection, including default child parameters */
2904
		if (count($children)) {
2905
			foreach($children as $name => $child) {
2906
				$conn['children'][$name] = array_merge($child, $child_params);
2907
			}
2908
		}
2909
		unset ($ph2ent);
2910
	}
2911

    
2912
	return;
2913
}
2914

    
2915
/****f* ipsec/ipsec_setup_secrets
2916
 * NAME
2917
 *   ipsec_setup_secrets - Setup swanctl authentication secrets
2918
 * INPUTS
2919
 *   None
2920
 * RESULT
2921
 *   Returns a swanctl array containing secrets (PSKs, certs, etc) and writes out
2922
 *   necessary CA, CRL, and certificate data.
2923
 ******/
2924
function ipsec_setup_secrets($gateways_status = false) {
2925
	global $config, $a_phase1, $ipsec_swanctl_dirs, $ipseccfg, $rgmap, $scconf;
2926

    
2927
	$suffix = 0;
2928

    
2929
	/* Fetch gateway status if not passed */
2930
	if (!is_array($gateways_status)) {
2931
		$gateways_status = return_gateways_status(true);
2932
	}
2933

    
2934
	$vpncas = array();
2935
	if (is_array($a_phase1) && count($a_phase1)) {
2936
		foreach ($a_phase1 as $ph1ent) {
2937
			if (isset($ph1ent['disabled'])) {
2938
				continue;
2939
			}
2940

    
2941
			if (isset($ph1ent['mobile'])) {
2942
				if ($ph1ent['authentication_method'] == 'pre_shared_key') {
2943
					$mobilepsk = true;
2944
				} elseif ($ph1ent['authentication_method'] == 'eap-mschapv2') {
2945
					$mobileeap = true;
2946
				}
2947
			}
2948

    
2949
			if (strstr($ph1ent['authentication_method'], 'cert') ||
2950
			    in_array($ph1ent['authentication_method'], array('eap-mschapv2', 'eap-tls', 'eap-radius'))) {
2951
				/* Write certificate and private key, point to private key */
2952
				$certline = '';
2953

    
2954
				$ikeid = $ph1ent['ikeid'];
2955
				$cert = lookup_cert($ph1ent['certref']);
2956

    
2957
				if (!$cert) {
2958
					log_error(sprintf(gettext("Error: Invalid phase1 certificate reference for %s"), $ph1ent['name']));
2959
					continue;
2960
				}
2961

    
2962
				/* add signing CA cert chain of server cert
2963
				 * to the list of CAs to write
2964
				 */
2965
				$cachain = ca_chain_array($cert);
2966
				if ($cachain && is_array($cachain)) {
2967
					foreach ($cachain as $cacrt) {
2968
						$vpncas[$cacrt['refid']] = $cacrt;
2969
					}
2970
				}
2971

    
2972
				@chmod($ipsec_swanctl_dirs['certpath'], 0600);
2973

    
2974
				$ph1keyfile = "{$ipsec_swanctl_dirs['keypath']}/cert-{$ikeid}.key";
2975
				if (!file_put_contents($ph1keyfile, base64_decode($cert['prv']))) {
2976
					log_error(sprintf(gettext("Error: Cannot write phase1 key file for %s"), $ph1ent['name']));
2977
					continue;
2978
				}
2979
				@chmod($ph1keyfile, 0600);
2980

    
2981
				$ph1certfile = "{$ipsec_swanctl_dirs['certpath']}/cert-{$ikeid}.crt";
2982
				if (!file_put_contents($ph1certfile, base64_decode($cert['crt']))) {
2983
					log_error(sprintf(gettext("Error: Cannot write phase1 certificate file for %s"), $ph1ent['name']));
2984
					@unlink($ph1keyfile);
2985
					continue;
2986
				}
2987
				@chmod($ph1certfile, 0600);
2988

    
2989
				$scconf['secrets']['private-' . $suffix++] = array('file' => $ph1keyfile);
2990
			} else if (strstr($ph1ent['authentication_method'], 'pkcs11')) {
2991
				$p11_id = array();
2992
				$output = shell_exec('/usr/local/bin/pkcs15-tool -c');
2993
				preg_match_all('/ID\s+: (.*)/', $output, $p11_id);
2994
				if (!empty($ph1ent['pkcs11certref']) && in_array($ph1ent['pkcs11certref'], $p11_id[1])) {
2995
					$scconf['secrets']['token-' . $suffix++] = array(
2996
						'handle' => $ph1ent['pkcs11certref'],
2997
						'pin' => $ph1ent['pkcs11pin'],
2998
					);
2999
				} else {
3000
					log_error(sprintf(gettext("Error: Invalid phase1 PKCS#11 certificate reference or PKCS#11 is not present for %s"), $ph1ent['name']));
3001
					continue;
3002
				}
3003
			} else {
3004
				/* Setup pre-shared keys */
3005
				list($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local', array(), $gateways_status);
3006
				list($peerid_type, $peerid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap, $gateways_status);
3007
				$myid = trim($myid_data);
3008

    
3009
				if (empty($peerid_data)) {
3010
					continue;
3011
				}
3012

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

    
3016
				if (!empty($ph1ent['pre-shared-key'])) {
3017
					$scconf['secrets']['ike-' . $suffix++] = array(
3018
						'secret' => '0s' . base64_encode(trim($ph1ent['pre-shared-key'])),
3019
						'id-0' => $myid,
3020
						'id-1' => $peerid,
3021
					);
3022
					if (isset($ph1ent['mobile'])) {
3023
						$scconf['secrets']['ike-' . $suffix++] = array(
3024
							'secret' => '0s' . base64_encode(trim($ph1ent['pre-shared-key'])),
3025
							'id-0' => $myid,
3026
							'id-1' => '%any',
3027
						);
3028
						$scconf['secrets']['ike-' . $suffix++] = array(
3029
							'secret' => '0s' . base64_encode(trim($ph1ent['pre-shared-key'])),
3030
						);
3031
					}
3032
				}
3033
			}
3034

    
3035
			/* if the client authenticates with a cert add the
3036
			 * client cert CA chain to the list of CAs to write
3037
			 */
3038
			if (in_array($ph1ent['authentication_method'],
3039
			    array('cert', 'eap-tls', 'xauth_cert_server', 'pkcs11'))) {
3040
				if (!empty($ph1ent['caref']) && !array_key_exists($ph1ent['caref'], $vpncas)) {
3041
					$thisca = lookup_ca($ph1ent['caref']);
3042
					$vpncas[$ph1ent['caref']] = $thisca;
3043
					/* follow chain up to root */
3044
					$cachain = ca_chain_array($thisca);
3045
					if ($cachain and is_array($cachain)) {
3046
						foreach ($cachain as $cacrt) {
3047
							$vpncas[$cacrt['refid']] = $cacrt;
3048
						}
3049
					}
3050
				}
3051
			}
3052
		}
3053
	}
3054

    
3055
	/* Write the required CAs */
3056
	foreach ($vpncas as $carefid => $cadata) {
3057
		$cacrt = base64_decode($cadata['crt']);
3058
		$cacrtattrs = openssl_x509_parse($cacrt);
3059
		if (!is_array($cacrtattrs) || !isset($cacrtattrs['hash'])) {
3060
			log_error(sprintf(gettext("Error: Invalid certificate hash info for %s"), $cadata['descr']));
3061
			continue;
3062
		}
3063
		$cafilename = "{$ipsec_swanctl_dirs['capath']}/{$cacrtattrs['hash']}.0";
3064
		if (!@file_put_contents($cafilename, $cacrt)) {
3065
				log_error(sprintf(gettext("Error: Cannot write IPsec CA file for %s"), $cadata['descr']));
3066
				continue;
3067
		}
3068
	}
3069

    
3070
	/* write out CRL files */
3071
	if (is_array($config['crl']) && count($config['crl']) && !empty($vpncas)) {
3072
		foreach ($config['crl'] as $crl) {
3073
			if (!isset($crl['text'])) {
3074
				log_error(sprintf(gettext("Warning: Missing CRL data for %s"), $crl['descr']));
3075
				continue;
3076
			}
3077
			$fpath = "{$ipsec_swanctl_dirs['crlpath']}/{$crl['refid']}.crl";
3078
			if (!@file_put_contents($fpath, base64_decode($crl['text']))) {
3079
				log_error(sprintf(gettext("Error: Cannot write IPsec CRL file for %s"), $crl['descr']));
3080
				continue;
3081
			}
3082
		}
3083
	}
3084

    
3085
	/* Add user PSKs */
3086
	if ($mobilepsk && is_array($config['system']) && is_array($config['system']['user'])) {
3087
		foreach ($config['system']['user'] as $user) {
3088
			if (!empty($user['ipsecpsk'])) {
3089
				$scconf['secrets']['ike-' . $suffix++] = array(
3090
					'secret' => '0s' . base64_encode(trim($user['ipsecpsk'])),
3091
					'id-0' => $myid,
3092
					'id-1' => $user['name'],
3093
				);
3094
			}
3095
		}
3096
		unset($user);
3097
	}
3098

    
3099
	/* add PSKs/EAPs for mobile clients */
3100
	if (is_array($ipseccfg['mobilekey'])) {
3101
		foreach ($ipseccfg['mobilekey'] as $key) {
3102
			if (($mobileeap && ($key['type'] == 'EAP')) ||
3103
			    ($key['type'] == 'PSK')) {
3104
				if (($key['ident'] == 'allusers') ||
3105
				    ($key['ident'] == 'any')) {
3106
					$key['ident'] = '%any';
3107
				}
3108
				$userkeyprefix = (strtolower($key['type']) == 'eap') ? 'eap' : 'ike';
3109
				$scconf['secrets'][$userkeyprefix . '-' . $suffix++] = array(
3110
					'secret' => '0s' . base64_encode(trim($key['pre-shared-key'])),
3111
					'id-0' => $key['ident'],
3112
				);
3113
			}
3114
		}
3115
		unset($key);
3116
	}
3117
	return;
3118
}
3119

    
3120
/****f* ipsec/ipsec_configure
3121
 * NAME
3122
 *   ipsec_configure - Configure IPsec
3123
 * INPUTS
3124
 *   $restart: Boolean (default false), whether or not to restart the IPsec
3125
 *             daemons.
3126
 * RESULT
3127
 *   IPsec-related configuration files are written, daemon is started or stopped
3128
 *   appropriately.
3129
 ******/
3130
function ipsec_configure($restart = false, $filterdns_restart = false) {
3131
	global $aggressive_mode_psk, $a_client, $a_phase1, $a_phase2, $config,
3132
		$filterdns_list, $g, $ifacesuse, $ipsec_idhandling, $ipsec_log_cats,
3133
		$ipsec_log_sevs, $ipsec_swanctl_basedir, $ipsec_swanctl_dirs,
3134
		$ipseccfg, $mobile_ipsec_auth, $natfilterrules, $p1_ealgos,
3135
		$p2_ealgos, $rgmap, $sa, $sn, $scconf, $tunnels, $mobile_configured,
3136
		$ipsec_vti_cleanup_ifs, $conn_defaults, $pool_addrs;
3137

    
3138
	/* service may have been enabled, disabled, or otherwise changed in a
3139
	 *way requiring rule updates */
3140
	filter_configure();
3141

    
3142
	if (!ipsec_enabled()) {
3143
		/* IPsec is disabled */
3144
		/* Stop charon */
3145
		mwexec("/usr/local/sbin/strongswanrc stop");
3146
		/* Stop dynamic monitoring */
3147
		killbypid("{$g['varrun_path']}/filterdns-ipsec.pid");
3148
		/* Wait for process to die */
3149
		sleep(2);
3150
		/* Stop PC/SC Smart Card Services */
3151
		killbyname("pcscd");
3152
		/* Shutdown enc0 interface*/
3153
		mwexec("/sbin/ifconfig enc0 down");
3154
		ipsec_gre_default_mtu(); 
3155
		return 0;
3156
	} else {
3157
		/* Startup enc0 interface */
3158
		mwexec("/sbin/ifconfig enc0 up");
3159
		/* PC/SC daemon must be started before strongswan */
3160
		if (isset($config['ipsec']['pkcs11support']) && !isvalidproc("pcscd")) {
3161
			if (platform_booting()) {
3162
				echo gettext("Starting PC/SC Smart Card Services...");
3163
			}
3164
			mwexec_bg("/usr/local/sbin/pcscd");
3165
			if (platform_booting()) {
3166
				echo gettext("done.\n");
3167
			}
3168
		} elseif (!isset($config['ipsec']['pkcs11support']) && isvalidproc("pcscd")) {
3169
			/* strongswan must be stopped first, otherwise it will flood the logs */
3170
			mwexec("/usr/local/sbin/strongswanrc stop");
3171
			killbyname("pcscd");
3172
		}
3173
	}
3174

    
3175
	if (platform_booting()) {
3176
		echo gettext("Configuring IPsec VPN... ");
3177
	}
3178

    
3179
	$ipsecstartlock = lock('ipsec', LOCK_EX);
3180

    
3181
	/* Prepare automatic ping_hosts.sh data */
3182
	unlink_if_exists("{$g['vardb_path']}/ipsecpinghosts");
3183
	touch("{$g['vardb_path']}/ipsecpinghosts");
3184
	$ipsecpinghostsactive = false;
3185

    
3186
	/* Populate convenience variables */
3187
	$syscfg = config_get_path('system');
3188
	init_config_arr(array('ipsec', 'phase1'));
3189
	$ipseccfg = config_get_path('ipsec');
3190
	$a_phase1 = config_get_path('ipsec/phase1');
3191
	init_config_arr(array('ipsec', 'phase2'));
3192
	$a_phase2 = config_get_path('ipsec/phase2');
3193
	init_config_arr(array('ipsec', 'client'));
3194
	$a_client = config_get_path('ipsec/client');
3195

    
3196
	$mobile_configured = false;
3197

    
3198

    
3199
	/* Setup a single structured array to process, to avoid repeatedly
3200
	 * looping through the arrays to locate entries later. */
3201
	$tunnels = array();
3202
	foreach ($a_phase1 as $p1) {
3203
		if (empty($p1)) {
3204
			continue;
3205
		}
3206
		if (isset($p1['mobile']) && !isset($p1['disabled'])) {
3207
			$mobile_configured = true;
3208
		}
3209
		$tunnels[$p1['ikeid']] = $p1;
3210
		$tunnels[$p1['ikeid']]['p2'] = array();
3211
	}
3212
	foreach ($a_phase2 as $p2) {
3213
		$tunnels[$p2['ikeid']]['p2'][] = $p2;
3214
	}
3215

    
3216
	$ipsec_vti_cleanup_ifs = array();
3217
	$rgmap = array();
3218
	$filterdns_list = array();
3219
	$aggressive_mode_psk = false;
3220
	$mobile_ipsec_auth = "";
3221
	$ifacesuse = array();
3222
	$natfilterrules = false;
3223

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

    
3227
	/* Build a list of all IPsec interfaces configured on the firewall at the OS level */
3228
	$gateways_status = return_gateways_status(true);
3229
	foreach (get_interface_arr() as $thisif) {
3230
		if (substr($thisif, 0, 5) == "ipsec") {
3231
			$ipsec_vti_cleanup_ifs[] = $thisif;
3232
		}
3233
	}
3234
	/* Create directory structure for IPsec */
3235
	ipsec_create_dirs();
3236
	/* Setup gateways and interfaces */
3237
	ipsec_setup_gwifs($gateways_status);
3238
	/* Setup and write strongswan.conf */
3239
	ipsec_setup_strongswan();
3240
	/* Start Global Connection default values */
3241
	$conn_defaults = array();
3242
	/* Fragmentation is on for everyone */
3243
	$conn_defaults['fragmentation'] = "yes";
3244
	/* Default to 'replace' for unique IDs (was 'yes' in ipsec.conf previously) */
3245
	$conn_defaults['unique'] = 'replace';
3246
	/* If the configuration has a valid alternate value for unique ID handling,
3247
	 * use it instead. */
3248
	if (!empty($config['ipsec']['uniqueids']) &&
3249
	    array_key_exists($config['ipsec']['uniqueids'], $ipsec_idhandling)) {
3250
		$conn_defaults['unique'] = config_get_path('ipsec/uniqueids');
3251
	}
3252
	/* Disable ipcomp for now. redmine #6167
3253
	if (isset($config['ipsec']['compression'])) {
3254
		$conn_defaults['compress'] = "yes";
3255
	}
3256
	set_single_sysctl('net.inet.ipcomp.ipcomp_enable', (isset($config['ipsec']['compression'])) ? 1 : 0);
3257
	*/
3258
	/* End Global Connection Defaults */
3259

    
3260
	/* Start swanctl configuration (scconf) */
3261
	$scconf = array();
3262
	$scconf[] = "# This file is automatically generated. Do not edit";
3263
	$scconf['connections'] = array();
3264
	/* Setup IPsec bypass */
3265
	ipsec_setup_bypass($gateways_status);
3266
	/* Setup connections */
3267
	ipsec_setup_tunnels($gateways_status);
3268
	$scconf['pools'] = array();
3269
	if ($mobile_configured) {
3270
		/* Setup mobile address pools */
3271
		ipsec_setup_pools();
3272
		/* Setup per-user pools */
3273
		ipsec_setup_userpools();
3274
	}
3275
	/* Setup secret data */
3276
	$scconf['secrets'] = array();
3277
	ipsec_setup_secrets($gateways_status);
3278

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

    
3281
	/* Clean up unused VTI interfaces */
3282
	foreach ($ipsec_vti_cleanup_ifs as $cleanif) {
3283
		if (does_interface_exist($cleanif)) {
3284
			mwexec("/sbin/ifconfig " . escapeshellarg($cleanif) . " destroy", false);
3285
		}
3286
	}
3287

    
3288
	/* set default MTU to 1400 for GRE over IPsec, otherwise to 1476 */
3289
	ipsec_gre_default_mtu(); 
3290

    
3291
	/* Manage process */
3292
	if ($restart === true) {
3293
		mwexec_bg("/usr/local/sbin/strongswanrc restart", false);
3294
	} else {
3295
		if (isvalidpid("{$g['varrun_path']}/charon.pid")) {
3296
			mwexec_bg("/usr/local/sbin/strongswanrc reload", false);
3297
		} else {
3298
			mwexec_bg("/usr/local/sbin/strongswanrc start", false);
3299
		}
3300
	}
3301

    
3302
	// Run ping_hosts.sh once if it's enabled to avoid wait for minicron
3303
	if ($ipsecpinghostsactive) {
3304
		mwexec_bg("/usr/local/bin/ping_hosts.sh");
3305
	}
3306

    
3307
	if ($natfilterrules == true) {
3308
		filter_configure();
3309
	}
3310
	/* start filterdns, if necessary */
3311
	if (count($filterdns_list) > 0) {
3312
		$interval = 60;
3313
		if (!empty($ipseccfg['dns-interval']) && is_numeric($ipseccfg['dns-interval'])) {
3314
			$interval = $ipseccfg['dns-interval'];
3315
		}
3316

    
3317
		$hostnames = "";
3318
		array_unique($filterdns_list);
3319
		foreach ($filterdns_list as $hostname) {
3320
			$hostnames .= "cmd {$hostname} '/usr/local/sbin/pfSctl -c \"service reload ipsecdns\"'\n";
3321
		}
3322
		file_put_contents("{$g['varetc_path']}/ipsec/filterdns-ipsec.hosts", $hostnames);
3323
		unset($hostnames);
3324

    
3325
		if (isvalidpid("{$g['varrun_path']}/filterdns-ipsec.pid")) {
3326
			if ($filterdns_restart) {
3327
				killbypid("{$g['varrun_path']}/filterdns-ipsec.pid");
3328
				sleep(2);
3329
				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");
3330
			} else {
3331
				sigkillbypid("{$g['varrun_path']}/filterdns-ipsec.pid", "HUP");
3332
			}
3333
		} else {
3334
			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");
3335
		}
3336
	} else {
3337
		killbypid("{$g['varrun_path']}/filterdns-ipsec.pid");
3338
		@unlink("{$g['varrun_path']}/filterdns-ipsec.pid");
3339
	}
3340

    
3341
	if (platform_booting()) {
3342
		echo "done\n";
3343
	}
3344

    
3345
	unlock($ipsecstartlock);
3346
	return count($filterdns_list);
3347
}
3348

    
3349
function ipsec_gre_default_mtu() {
3350
	global $config;
3351

    
3352
	foreach ($config['interfaces'] as $if => $ifdetail) { 
3353
		if (interface_is_type($ifdetail['if'], 'gre') && !isset($ifdetail['mtu'])) {
3354
			if (is_greipsec($ifdetail['if'])) {
3355
				set_interface_mtu($ifdetail['if'], 1400);
3356
			} else {
3357
				set_interface_mtu($ifdetail['if'], 1476);
3358
			}
3359
		}
3360
	}
3361
}
3362

    
3363
/* Return the larger of derived SA rekey time and reauth time */
3364
function ipsec_get_renewmax($entry) {
3365
	if (empty($entry) || !is_array($entry)) {
3366
		return 0;
3367
	}
3368
	return max(ipsec_get_rekey_time($entry), ipsec_get_reauth_time($entry));
3369
}
3370

    
3371
/* Determine the life time of an SA entry (Hard upper total time limit for SA before it is removed) */
3372
function ipsec_get_life_time($entry) {
3373
	if (empty($entry) || !is_array($entry)) {
3374
		return 0;
3375
	}
3376
	/* Use a hardcoded value if present in the configuration */
3377
	if ($entry['lifetime'] > 0) {
3378
		return $entry['lifetime'];
3379
	}
3380
	/* If rekey or reauth are enabled, attempt to derive a lifetime from one of those */
3381
	$renewmax = ipsec_get_renewmax($entry);
3382
	if ($renewmax > 0) {
3383
		return intval($renewmax / 0.9);
3384
	}
3385
	/* To reach here, rekey_time and lifetime are both 0 which is invalid
3386
	 * Default to 16000 for p1 and 4000 for p2 */
3387
	if ($entry['iketype']) {
3388
		return 16000;
3389
	} else {
3390
		return 4000;
3391
	}
3392
}
3393

    
3394
/* Determine the rekey time of an SA entry (Time at which to rekey IKEv2 or Child SA entries) */
3395
function ipsec_get_rekey_time($entry) {
3396
	if (empty($entry) || !is_array($entry)) {
3397
		return 0;
3398
	}
3399
	/* Use a hardcoded value if present in the configuration */
3400
	if (strlen($entry['rekey_time'])) {
3401
		/* Check via strlen since 0 is a valid value */
3402
		return $entry['rekey_time'];
3403
	} elseif ($entry['lifetime'] > 0) {
3404
		/* If rekey_time is empty and lifetime is non-zero, use 90% lifetime */
3405
		return intval($entry['lifetime'] * 0.9);
3406
	}
3407
	/* To reach here, rekey_time and lifetime are empty
3408
	 * Default to 14400 for p1 and 3600 for p2 */
3409
	if ($entry['iketype']) {
3410
		return 14400;
3411
	} else {
3412
		return 3600;
3413
	}
3414
}
3415

    
3416
/* Determine the reauth time of an SA entry (IKE SA tear-down/reauthenticate) */
3417
function ipsec_get_reauth_time($entry) {
3418
	if (empty($entry) || !is_array($entry)) {
3419
		return 0;
3420
	}
3421
	/* Use a hardcoded value if present in the configuration */
3422
	if (strlen($entry['reauth_time'])) {
3423
		/* Check via strlen since 0 is a valid value */
3424
		return $entry['reauth_time'];
3425
	} elseif ($entry['lifetime'] > 0) {
3426
		/* If reauth_time is empty and lifetime is non-zero,
3427
		 * use 90% lifetime for IKEv1, disable for IKEv2/auto */
3428
		if ($entry['iketype'] == 'ikev1') {
3429
			return intval($entry['lifetime'] * 0.9);
3430
		} else {
3431
			return 0;
3432
		}
3433
	}
3434
	/* To reach here, rekey_time and lifetime are empty
3435
	 * Default to disabled (0) */
3436
	return 0;
3437
}
3438

    
3439
/* Determine the over time of an SA entry (Hard upper IKE SA time limit, relative to rekey/reauth time) */
3440
function ipsec_get_over_time($entry) {
3441
	if (empty($entry) || !is_array($entry)) {
3442
		return 0;
3443
	}
3444
	/* Automatically derive the value for rand_time */
3445
	$lifetime = ipsec_get_life_time($entry);
3446
	$renewmax = ipsec_get_renewmax($entry);
3447
	if (($lifetime > 0) && ($renewmax > 0)) {
3448
		/* If life time and rekey/reauth time both have values, subtract to get rand time */
3449
		return $lifetime - $renewmax;
3450
	} elseif ($lifetime > 0) {
3451
		/* If only life time has a value, use 10% of that */
3452
		return intval($lifetime * 0.1);
3453
	} elseif ($renewmax > 0) {
3454
		/* If only rekey/reauth time has a value, use 10% of that */
3455
		return intval($renewmax * 0.1);
3456
	}
3457
	/* No value can be determined, default to 0 */
3458
	return 0;
3459
}
3460

    
3461
/* Determine the rand time of an SA entry (random value subtracted from renewal time to prevent collisions) */
3462
function ipsec_get_rand_time($entry) {
3463
	if (empty($entry) || !is_array($entry)) {
3464
		return 0;
3465
	}
3466
	/* Use a hardcoded value if present in the configuration */
3467
	if (strlen($entry['rand_time'])) {
3468
		/* Check via strlen since 0 is a valid value */
3469
		return $entry['rand_time'];
3470
	}
3471
	/* Logic to automatically determine rand time is identical to calculating over time */
3472
	return ipsec_get_over_time($entry);
3473
}
3474

    
3475
?>
(27-27/62)