Project

General

Profile

Download (27 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * system_camanager.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2025 Rubicon Communications, LLC (Netgate)
9
 * Copyright (c) 2008 Shrew Soft Inc
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
##|+PRIV
26
##|*IDENT=page-system-camanager
27
##|*NAME=System: CA Manager
28
##|*DESCR=Allow access to the 'System: CA Manager' page.
29
##|*MATCH=system_camanager.php*
30
##|-PRIV
31

    
32
require_once("guiconfig.inc");
33
require_once("certs.inc");
34
require_once("pfsense-utils.inc");
35

    
36
$ca_methods = array(
37
	"internal" => gettext("Create an internal Certificate Authority"),
38
	"existing" => gettext("Import an existing Certificate Authority"),
39
	"intermediate" => gettext("Create an intermediate Certificate Authority"));
40

    
41
$ca_keylens = array("1024", "2048", "3072", "4096", "6144", "7680", "8192", "15360", "16384");
42
$ca_keytypes = array("RSA", "ECDSA");
43
global $openssl_digest_algs;
44
global $cert_strict_values;
45
$max_lifetime = cert_get_max_lifetime();
46
$default_lifetime = min(3650, $max_lifetime);
47
$openssl_ecnames = cert_build_curve_list();
48
$class = "success";
49

    
50
$act = $_REQUEST['act'];
51

    
52
if (isset($_REQUEST['id']) && ctype_alnum($_REQUEST['id'])) {
53
	$id = $_REQUEST['id'];
54
}
55
if (!empty($id)) {
56
	$ca_item_config = lookup_ca($id);
57
	$thisca = &$ca_item_config['item'];
58
}
59

    
60
/* Actions other than 'new' require an ID.
61
 * 'del' action must be submitted via POST. */
62
if ((!empty($act) &&
63
    ($act != 'new') &&
64
    !$thisca) ||
65
    (($act == 'del') && empty($_POST))) {
66
	pfSenseHeader("system_camanager.php");
67
	exit;
68
}
69

    
70
switch ($act) {
71
	case 'del':
72
		$name = htmlspecialchars($thisca['descr']);
73
		if (cert_in_use($id)) {
74
			$savemsg = sprintf(gettext("Certificate %s is in use and cannot be deleted"), $name);
75
			$class = "danger";
76
		} else {
77
			/* Only remove CA reference when deleting. It can be reconnected if a new matching CA is imported */
78
			foreach (config_get_path('cert', []) as $cid => $acrt) {
79
				if ($acrt['caref'] == $thisca['refid']) {
80
					config_del_path("cert/{$cid}/caref");
81
				}
82
			}
83
			/* Remove any CRLs for this CA, there is no way to recover the connection once the CA has been removed. */
84
			foreach (config_get_path('crl', []) as $cid => $acrl) {
85
				if ($acrl['caref'] == $thisca['refid']) {
86
					config_del_path("crl/{$cid}");
87
				}
88
			}
89
			/* Delete the CA */
90
			foreach (config_get_path('ca', []) as $cid => $aca) {
91
				if ($aca['refid'] == $thisca['refid']) {
92
					config_del_path("ca/{$cid}");
93
				}
94
			}
95
			$savemsg = sprintf(gettext("Deleted Certificate Authority %s and associated CRLs"), htmlspecialchars($name));
96
			write_config($savemsg);
97
			ca_setup_trust_store();
98
		}
99
		unset($act);
100
		break;
101
	case 'edit':
102
		/* Editing an existing CA, so populate values. */
103
		$pconfig['method'] = 'existing';
104
		$pconfig['descr']  = $thisca['descr'];
105
		$pconfig['refid']  = $thisca['refid'];
106
		$pconfig['cert']   = base64_decode($thisca['crt']);
107
		$pconfig['serial'] = $thisca['serial'];
108
		$pconfig['trust']  = ($thisca['trust'] == 'enabled');
109
		$pconfig['randomserial']  = ($thisca['randomserial'] == 'enabled');
110
		if (!empty($thisca['prv'])) {
111
			$pconfig['key'] = base64_decode($thisca['prv']);
112
		}
113
		break;
114
	case 'new':
115
		/* New CA, so set default values */
116
		$pconfig['method'] = $_POST['method'];
117
		$pconfig['keytype'] = "RSA";
118
		$pconfig['keylen'] = "2048";
119
		$pconfig['ecname'] = "prime256v1";
120
		$pconfig['digest_alg'] = "sha256";
121
		$pconfig['lifetime'] = $default_lifetime;
122
		$pconfig['dn_commonname'] = "internal-ca";
123
		break;
124
	case 'exp':
125
		/* Exporting a ca */
126
		send_user_download('data', base64_decode($thisca['crt']), "{$thisca['descr']}.crt");
127
		break;
128
	case 'expkey':
129
		/* Exporting a private key */
130
		send_user_download('data', base64_decode($thisca['prv']), "{$thisca['descr']}.key");
131
		break;
132
	default:
133
		break;
134
}
135

    
136
if ($_POST['save']) {
137
	unset($input_errors);
138
	$input_errors = array();
139
	$pconfig = $_POST;
140

    
141
	/* input validation */
142
	switch ($pconfig['method']) {
143
		case 'existing':
144
			$reqdfields = explode(" ", "descr cert");
145
			$reqdfieldsn = array(
146
				gettext("Descriptive name"),
147
				gettext("Certificate data"));
148
			/* Make sure we do not have invalid characters in the fields for the certificate */
149
			if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
150
				array_push($input_errors, gettext("The field 'Descriptive Name' contains invalid characters."));
151
			}
152
			if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
153
				$input_errors[] = gettext("This certificate does not appear to be valid.");
154
			}
155
			if ($_POST['key'] && strstr($_POST['key'], "ENCRYPTED")) {
156
				$input_errors[] = gettext("Encrypted private keys are not yet supported.");
157
			}
158
			if (!$input_errors && !empty($_POST['key']) && cert_get_publickey($_POST['cert'], false) != cert_get_publickey($_POST['key'], false, 'prv')) {
159
				$input_errors[] = gettext("The submitted private key does not match the submitted certificate data.");
160
			}
161
			/* we must ensure the certificate is capable of acting as a CA
162
			 * https://redmine.pfsense.org/issues/7885
163
			 */
164
			if (!$input_errors) {
165
				$purpose = cert_get_purpose($_POST['cert'], false);
166
				if ($purpose['ca'] != 'Yes') {
167
					$input_errors[] = gettext("The submitted certificate does not appear to be a Certificate Authority, import it on the Certificates tab instead.");
168
				}
169
			}
170
			break;
171
		case 'internal':
172
			$reqdfields = explode(" ",
173
				"descr keylen ecname keytype lifetime dn_commonname");
174
			$reqdfieldsn = array(
175
				gettext("Descriptive name"),
176
				gettext("Key length"),
177
				gettext("Elliptic Curve Name"),
178
				gettext("Key type"),
179
				gettext("Lifetime"),
180
				gettext("Common Name"));
181
			break;
182
		case 'intermediate':
183
			$reqdfields = explode(" ",
184
				"descr caref keylen ecname keytype lifetime dn_commonname");
185
			$reqdfieldsn = array(
186
				gettext("Descriptive name"),
187
				gettext("Signing Certificate Authority"),
188
				gettext("Key length"),
189
				gettext("Elliptic Curve Name"),
190
				gettext("Key type"),
191
				gettext("Lifetime"),
192
				gettext("Common Name"));
193
			break;
194
		default:
195
			break;
196
	}
197

    
198
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
199
	if ($pconfig['method'] != "existing") {
200
		/* Make sure we do not have invalid characters in the fields for the certificate */
201
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
202
			array_push($input_errors, gettext("The field 'Descriptive Name' contains invalid characters."));
203
		}
204
		$pattern = '/[^a-zA-Z0-9\ \'\/~`\!@#\$%\^&\*\(\)_\-\+=\{\}\[\]\|;:"\<\>,\.\?\\\]/';
205
		if (!empty($_POST['dn_commonname']) && preg_match($pattern, $_POST['dn_commonname'])) {
206
			$input_errors[] = gettext("The field 'Common Name' contains invalid characters.");
207
		}
208
		if (!empty($_POST['dn_state']) && preg_match($pattern, $_POST['dn_state'])) {
209
			$input_errors[] = gettext("The field 'State or Province' contains invalid characters.");
210
		}
211
		if (!empty($_POST['dn_city']) && preg_match($pattern, $_POST['dn_city'])) {
212
			$input_errors[] = gettext("The field 'City' contains invalid characters.");
213
		}
214
		if (!empty($_POST['dn_organization']) && preg_match($pattern, $_POST['dn_organization'])) {
215
			$input_errors[] = gettext("The field 'Organization' contains invalid characters.");
216
		}
217
		if (!empty($_POST['dn_organizationalunit']) && preg_match($pattern, $_POST['dn_organizationalunit'])) {
218
			$input_errors[] = gettext("The field 'Organizational Unit' contains invalid characters.");
219
		}
220
		if (!in_array($_POST["keytype"], $ca_keytypes)) {
221
			array_push($input_errors, gettext("Please select a valid Key Type."));
222
		}
223
		if (!in_array($_POST["keylen"], $ca_keylens)) {
224
			array_push($input_errors, gettext("Please select a valid Key Length."));
225
		}
226
		if (!in_array($_POST["ecname"], array_keys($openssl_ecnames))) {
227
			array_push($input_errors, gettext("Please select a valid Elliptic Curve Name."));
228
		}
229
		if (!in_array($_POST["digest_alg"], $openssl_digest_algs)) {
230
			array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
231
		}
232
		if ($_POST['lifetime'] > $max_lifetime) {
233
			$input_errors[] = gettext("Lifetime is longer than the maximum allowed value. Use a shorter lifetime.");
234
		}
235
	}
236

    
237
	if (!empty($_POST['serial']) && !cert_validate_serial($_POST['serial'])) {
238
		$input_errors[] = gettext("Please enter a valid integer serial number.");
239
	}
240

    
241
	/* save modifications */
242
	if (!$input_errors) {
243
		$ca = array();
244
		if (!isset($pconfig['refid']) || empty($pconfig['refid'])) {
245
			$ca['refid'] = uniqid();
246
		} else {
247
			$ca['refid'] = $pconfig['refid'];
248
		}
249

    
250
		if (isset($id) && $thisca) {
251
			$ca = $thisca;
252
		}
253

    
254
		$ca['descr'] = $pconfig['descr'];
255
		$ca['trust'] = ($pconfig['trust'] == 'yes') ? "enabled" : "disabled";
256
		$ca['randomserial'] = ($pconfig['randomserial'] == 'yes') ? "enabled" : "disabled";
257

    
258
		if ($act == "edit") {
259
			$ca['descr']  = $pconfig['descr'];
260
			$ca['refid']  = $pconfig['refid'];
261
			$ca['serial'] = $pconfig['serial'];
262
			$ca['crt'] = base64_encode($pconfig['cert']);
263
			$ca['prv'] = base64_encode($pconfig['key']);
264
			$savemsg = sprintf(gettext("Updated Certificate Authority %s"), $ca['descr']);
265
		} else {
266
			$old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */
267
			if ($pconfig['method'] == "existing") {
268
				ca_import($ca, $pconfig['cert'], $pconfig['key'], $pconfig['serial']);
269
				$savemsg = sprintf(gettext("Imported Certificate Authority %s"), $ca['descr']);
270
			} else if ($pconfig['method'] == "internal") {
271
				$dn = array('commonName' => $pconfig['dn_commonname']);
272
				if (!empty($pconfig['dn_country'])) {
273
					$dn['countryName'] = $pconfig['dn_country'];
274
				}
275
				if (!empty($pconfig['dn_state'])) {
276
					$dn['stateOrProvinceName'] = $pconfig['dn_state'];
277
				}
278
				if (!empty($pconfig['dn_city'])) {
279
					$dn['localityName'] = $pconfig['dn_city'];
280
				}
281
				if (!empty($pconfig['dn_organization'])) {
282
					$dn['organizationName'] = $pconfig['dn_organization'];
283
				}
284
				if (!empty($pconfig['dn_organizationalunit'])) {
285
					$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
286
				}
287
				if (!ca_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['digest_alg'], $pconfig['keytype'], $pconfig['ecname'])) {
288
					$input_errors = array();
289
					while ($ssl_err = openssl_error_string()) {
290
						if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
291
							array_push($input_errors, "openssl library returns: " . $ssl_err);
292
						}
293
					}
294
				}
295
				$savemsg = sprintf(gettext("Created internal Certificate Authority %s"), $ca['descr']);
296
			} else if ($pconfig['method'] == "intermediate") {
297
				$dn = array('commonName' => $pconfig['dn_commonname']);
298
				if (!empty($pconfig['dn_country'])) {
299
					$dn['countryName'] = $pconfig['dn_country'];
300
				}
301
				if (!empty($pconfig['dn_state'])) {
302
					$dn['stateOrProvinceName'] = $pconfig['dn_state'];
303
				}
304
				if (!empty($pconfig['dn_city'])) {
305
					$dn['localityName'] = $pconfig['dn_city'];
306
				}
307
				if (!empty($pconfig['dn_organization'])) {
308
					$dn['organizationName'] = $pconfig['dn_organization'];
309
				}
310
				if (!empty($pconfig['dn_organizationalunit'])) {
311
					$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
312
				}
313
				if (!ca_inter_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['caref'], $pconfig['digest_alg'], $pconfig['keytype'], $pconfig['ecname'])) {
314
					$input_errors = array();
315
					while ($ssl_err = openssl_error_string()) {
316
						if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
317
							array_push($input_errors, "openssl library returns: " . $ssl_err);
318
						}
319
					}
320
				}
321
				$savemsg = sprintf(gettext("Created internal intermediate Certificate Authority %s"), $ca['descr']);
322
			}
323
			error_reporting($old_err_level);
324
		}
325

    
326
		if (isset($id) && $thisca) {
327
			config_set_path("ca/{$ca_item_config['idx']}", $ca);
328
		} else {
329
			config_set_path('ca/', $ca);
330
		}
331

    
332
		if (!$input_errors) {
333
			write_config($savemsg);
334
			ca_setup_trust_store();
335
			pfSenseHeader("system_camanager.php");
336
		}
337
	}
338
}
339

    
340
$pgtitle = array(gettext('System'), gettext('Certificates'), gettext('Authorities'));
341
$pglinks = array("", "system_camanager.php", "system_camanager.php");
342

    
343
if ($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors) {
344
	$pgtitle[] = gettext('Edit');
345
	$pglinks[] = "@self";
346
}
347
include("head.inc");
348

    
349
if ($input_errors) {
350
	print_input_errors($input_errors);
351
}
352

    
353
if ($savemsg) {
354
	print_info_box($savemsg, $class);
355
}
356

    
357
$tab_array = array();
358
$tab_array[] = array(gettext('Authorities'), true, 'system_camanager.php');
359
$tab_array[] = array(gettext('Certificates'), false, 'system_certmanager.php');
360
$tab_array[] = array(gettext('Revocation'), false, 'system_crlmanager.php');
361
display_top_tabs($tab_array);
362

    
363
if (!($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors)) {
364
?>
365
<div class="panel panel-default" id="search-panel">
366
	<div class="panel-heading">
367
		<h2 class="panel-title">
368
			<?=gettext('Search')?>
369
			<span class="widget-heading-icon pull-right">
370
				<a data-toggle="collapse" href="#search-panel_panel-body">
371
					<i class="fa-solid fa-plus-circle"></i>
372
				</a>
373
			</span>
374
		</h2>
375
	</div>
376
	<div id="search-panel_panel-body" class="panel-body collapse in">
377
		<div class="form-group">
378
			<label class="col-sm-2 control-label">
379
				<?=gettext("Search term")?>
380
			</label>
381
			<div class="col-sm-5"><input class="form-control" name="searchstr" id="searchstr" type="text"/></div>
382
			<div class="col-sm-2">
383
				<select id="where" class="form-control">
384
					<option value="0"><?=gettext("Name")?></option>
385
					<option value="1"><?=gettext("Distinguished Name")?></option>
386
					<option value="2" selected><?=gettext("Both")?></option>
387
				</select>
388
			</div>
389
			<div class="col-sm-3">
390
				<a id="btnsearch" title="<?=gettext("Search")?>" class="btn btn-primary btn-sm"><i class="fa-solid fa-search icon-embed-btn"></i><?=gettext("Search")?></a>
391
				<a id="btnclear" title="<?=gettext("Clear")?>" class="btn btn-info btn-sm"><i class="fa-solid fa-undo icon-embed-btn"></i><?=gettext("Clear")?></a>
392
			</div>
393
			<div class="col-sm-10 col-sm-offset-2">
394
				<span class="help-block"><?=gettext('Enter a search string or *nix regular expression to search certificate names and distinguished names.')?></span>
395
			</div>
396
		</div>
397
	</div>
398
</div>
399

    
400
<div class="panel panel-default">
401
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificate Authorities')?></h2></div>
402
	<div class="panel-body">
403
		<div class="table-responsive">
404
		<table id="catable" class="table table-striped table-hover table-rowdblclickedit sortable-theme-bootstrap" data-sortable>
405
			<thead>
406
				<tr>
407
					<th><?=gettext("Name")?></th>
408
					<th><?=gettext("Internal")?></th>
409
					<th><?=gettext("Issuer")?></th>
410
					<th><?=gettext("Certificates")?></th>
411
					<th><?=gettext("Distinguished Name")?></th>
412
					<th><?=gettext("In Use")?></th>
413
					<th><?=gettext("Actions")?></th>
414
				</tr>
415
			</thead>
416
			<tbody>
417
<?php
418
$pluginparams = array();
419
$pluginparams['type'] = 'certificates';
420
$pluginparams['event'] = 'used_ca';
421
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
422

    
423
foreach (config_get_path('ca', []) as $ca):
424
	$name = htmlspecialchars($ca['descr']);
425
	$subj = cert_get_subject($ca['crt']);
426
	$issuer = cert_get_issuer($ca['crt']);
427
	if ($subj == $issuer) {
428
		$issuer_name = gettext("self-signed");
429
	} else {
430
		$issuer_name = gettext("external");
431
	}
432
	$subj = htmlspecialchars(cert_escape_x509_chars($subj, true));
433
	$issuer = htmlspecialchars($issuer);
434
	$certcount = 0;
435

    
436
	$issuer_ca = lookup_ca($ca['caref']);
437
	$issuer_ca = $issuer_ca['item'];
438
	if ($issuer_ca) {
439
		$issuer_name = htmlspecialchars($issuer_ca['descr']);
440
	}
441

    
442
	foreach (config_get_path('cert', []) as $cert) {
443
		if ($cert['caref'] == $ca['refid']) {
444
			$certcount++;
445
		}
446
	}
447

    
448
	foreach (config_get_path('ca', []) as $cert) {
449
		if ($cert['caref'] == $ca['refid']) {
450
			$certcount++;
451
		}
452
	}
453
?>
454
				<tr>
455
					<td><?=$name?></td>
456
					<td><i class="<?= (!empty($ca['prv'])) ? "fa-solid fa-check" : "fa-solid fa-times" ; ?>"></i></td>
457
					<td><i><?=$issuer_name?></i></td>
458
					<td><?=$certcount?></td>
459
					<td>
460
						<?=$subj?>
461
						<?php cert_print_infoblock($ca); ?>
462
						<?php cert_print_dates($ca);?>
463
					</td>
464
					<td class="text-nowrap">
465
						<?php if (is_ipsec_peer_ca($ca['refid'])): ?>
466
							<?=gettext("IPsec Tunnel")?><br/>
467
						<?php endif?>
468
						<?php if (is_ldap_peer_ca($ca['refid'])): ?>
469
							<?=gettext("LDAP Server")?><br/>
470
						<?php endif?>
471
						<?php if (is_openvpn_client_ca($ca['refid'])): ?>
472
							<?=gettext("OpenVPN Client")?><br/>
473
						<?php endif?>
474
						<?php if (is_openvpn_server_ca($ca['refid'])): ?>
475
							<?=gettext("OpenVPN Server")?><br/>
476
						<?php endif?>
477
						<?php echo cert_usedby_description($ca['refid'], $certificates_used_by_packages); ?>
478
					</td>
479
					<td class="text-nowrap">
480
						<a class="fa-solid fa-pencil"	title="<?=gettext("Edit CA")?>"	href="system_camanager.php?act=edit&amp;id=<?=$ca['refid']?>"></a>
481
						<a class="fa-solid fa-certificate"	title="<?=gettext("Export CA")?>"	href="system_camanager.php?act=exp&amp;id=<?=$ca['refid']?>"></a>
482
					<?php if ($ca['prv']): ?>
483
						<a class="fa-solid fa-key"	title="<?=gettext("Export key")?>"	href="system_camanager.php?act=expkey&amp;id=<?=$ca['refid']?>"></a>
484
					<?php endif?>
485
					<?php if (is_cert_locally_renewable($ca)): ?>
486
						<a href="system_certmanager_renew.php?type=ca&amp;refid=<?=$ca['refid']?>" class="fa-solid fa-arrow-rotate-right" title="<?=gettext("Reissue/Renew")?>"></a>
487
					<?php endif ?>
488
					<?php if (!ca_in_use($ca['refid'])): ?>
489
						<a class="fa-solid fa-trash-can" 	title="<?=gettext("Delete CA and its CRLs")?>"	href="system_camanager.php?act=del&amp;id=<?=$ca['refid']?>" usepost ></a>
490
					<?php endif?>
491
					</td>
492
				</tr>
493
<?php endforeach; ?>
494
			</tbody>
495
		</table>
496
		</div>
497
	</div>
498
</div>
499

    
500
<nav class="action-buttons">
501
	<a href="?act=new" class="btn btn-success btn-sm">
502
		<i class="fa-solid fa-plus icon-embed-btn"></i>
503
		<?=gettext("Add")?>
504
	</a>
505
</nav>
506
<script type="text/javascript">
507
//<![CDATA[
508

    
509
events.push(function() {
510

    
511
	// Make these controls plain buttons
512
	$("#btnsearch").prop('type', 'button');
513
	$("#btnclear").prop('type', 'button');
514

    
515
	// Search for a term in the entry name and/or dn
516
	$("#btnsearch").click(function() {
517
		var searchstr = $('#searchstr').val().toLowerCase();
518
		var table = $("table tbody");
519
		var where = $('#where').val();
520

    
521
		table.find('tr').each(function (i) {
522
			var $tds = $(this).find('td'),
523
				shortname = $tds.eq(0).text().trim().toLowerCase(),
524
				dn = $tds.eq(4).text().trim().toLowerCase();
525

    
526
			regexp = new RegExp(searchstr);
527
			if (searchstr.length > 0) {
528
				if (!(regexp.test(shortname) && (where != 1)) && !(regexp.test(dn) && (where != 0))) {
529
					$(this).hide();
530
				} else {
531
					$(this).show();
532
				}
533
			} else {
534
				$(this).show();	// A blank search string shows all
535
			}
536
		});
537
	});
538

    
539
	// Clear the search term and unhide all rows (that were hidden during a previous search)
540
	$("#btnclear").click(function() {
541
		var table = $("table tbody");
542

    
543
		$('#searchstr').val("");
544

    
545
		table.find('tr').each(function (i) {
546
			$(this).show();
547
		});
548
	});
549

    
550
	// Hitting the enter key will do the same as clicking the search button
551
	$("#searchstr").on("keyup", function (event) {
552
		if (event.keyCode == 13) {
553
			$("#btnsearch").get(0).click();
554
		}
555
	});
556
});
557
//]]>
558
</script>
559

    
560
<?php
561
	include("foot.inc");
562
	exit;
563
}
564

    
565
$form = new Form;
566
//$form->setAction('system_camanager.php?act=edit');
567
if (isset($id) && $thisca) {
568
	$form->addGlobal(new Form_Input(
569
		'id',
570
		null,
571
		'hidden',
572
		$id
573
	));
574
}
575

    
576
if ($act == "edit") {
577
	$form->addGlobal(new Form_Input(
578
		'refid',
579
		null,
580
		'hidden',
581
		$pconfig['refid']
582
	));
583
}
584

    
585
$section = new Form_Section('Create / Edit CA');
586

    
587
$section->addInput(new Form_Input(
588
	'descr',
589
	'*Descriptive name',
590
	'text',
591
	$pconfig['descr']
592
))->setHelp('The name of this entry as displayed in the GUI for reference.%s' .
593
		'This name can contain spaces but it cannot contain any of the ' .
594
		'following characters: %s', '<br/>', "?, >, <, &, /, \, \", '");
595

    
596
if (!isset($id) || $act == "edit") {
597
	$section->addInput(new Form_Select(
598
		'method',
599
		'*Method',
600
		$pconfig['method'],
601
		$ca_methods
602
	))->toggles();
603
}
604

    
605
$section->addInput(new Form_Checkbox(
606
	'trust',
607
	'Trust Store',
608
	'Add this Certificate Authority to the Operating System Trust Store',
609
	$pconfig['trust']
610
))->setHelp('When enabled, the contents of the CA will be added to the trust ' .
611
	'store so that they will be trusted by the operating system.');
612

    
613
$section->addInput(new Form_Checkbox(
614
	'randomserial',
615
	'Randomize Serial',
616
	'Use random serial numbers when signing certificates',
617
	$pconfig['randomserial']
618
))->setHelp('When enabled, if this CA is capable of signing certificates then ' .
619
		'serial numbers for certificates signed by this CA will be ' .
620
		'automatically randomized and checked for uniqueness instead of ' .
621
		'using the sequential value from Next Certificate Serial.');
622

    
623
$form->add($section);
624

    
625
$section = new Form_Section('Existing Certificate Authority');
626
$section->addClass('toggle-existing collapse');
627

    
628
$section->addInput(new Form_Textarea(
629
	'cert',
630
	'*Certificate data',
631
	$pconfig['cert']
632
))->setHelp('Paste a certificate in X.509 PEM format here.');
633

    
634
$section->addInput(new Form_Textarea(
635
	'key',
636
	'Certificate Private Key (optional)',
637
	$pconfig['key']
638
))->setHelp('Paste the private key for the above certificate here. This is '.
639
	'optional in most cases, but is required when generating a '.
640
	'Certificate Revocation List (CRL).');
641

    
642
$section->addInput(new Form_Input(
643
	'serial',
644
	'Next Certificate Serial',
645
	'number',
646
	$pconfig['serial']
647
))->setHelp('Enter a decimal number to be used as a sequential serial number for ' .
648
	'the next certificate to be signed by this CA. This value is ignored ' .
649
	'when Randomize Serial is checked.');
650

    
651
$form->add($section);
652

    
653
$section = new Form_Section('Internal Certificate Authority');
654
$section->addClass('toggle-internal', 'toggle-intermediate', 'collapse');
655

    
656
$allCas = array();
657
foreach (config_get_path('ca', []) as $ca) {
658
	if (!$ca['prv']) {
659
			continue;
660
	}
661

    
662
	$allCas[ $ca['refid'] ] = $ca['descr'];
663
}
664

    
665
$group = new Form_Group('*Signing Certificate Authority');
666
$group->addClass('toggle-intermediate', 'collapse');
667
$group->add(new Form_Select(
668
	'caref',
669
	null,
670
	$pconfig['caref'],
671
	$allCas
672
));
673
$section->add($group);
674

    
675
$section->addInput(new Form_Select(
676
	'keytype',
677
	'*Key type',
678
	$pconfig['keytype'],
679
	array_combine($ca_keytypes, $ca_keytypes)
680
));
681

    
682
$group = new Form_Group($i == 0 ? '*Key length':'');
683
$group->addClass('rsakeys');
684
$group->add(new Form_Select(
685
	'keylen',
686
	null,
687
	$pconfig['keylen'],
688
	array_combine($ca_keylens, $ca_keylens)
689
))->setHelp('The length to use when generating a new RSA key, in bits. %1$s' .
690
	'The Key Length should not be lower than 2048 or some platforms ' .
691
	'may consider the certificate invalid.', '<br/>');
692
$section->add($group);
693

    
694
$group = new Form_Group($i == 0 ? '*Elliptic Curve Name':'');
695
$group->addClass('ecnames');
696
$group->add(new Form_Select(
697
	'ecname',
698
	null,
699
	$pconfig['ecname'],
700
	$openssl_ecnames
701
))->setHelp('Curves may not be compatible with all uses. Known compatible curve uses are denoted in brackets.');
702
$section->add($group);
703

    
704
$section->addInput(new Form_Select(
705
	'digest_alg',
706
	'*Digest Algorithm',
707
	$pconfig['digest_alg'],
708
	array_combine($openssl_digest_algs, $openssl_digest_algs)
709
))->setHelp('The digest method used when the CA is signed. %1$s' .
710
	'The best practice is to use SHA256 or higher. '.
711
	'Some services and platforms, such as the GUI web server and OpenVPN, consider weaker digest algorithms invalid.', '<br/>');
712

    
713
$section->addInput(new Form_Input(
714
	'lifetime',
715
	'*Lifetime (days)',
716
	'number',
717
	$pconfig['lifetime'],
718
	['max' => $max_lifetime]
719
));
720

    
721
$section->addInput(new Form_Input(
722
	'dn_commonname',
723
	'*Common Name',
724
	'text',
725
	$pconfig['dn_commonname'],
726
	['placeholder' => 'e.g. internal-ca']
727
));
728

    
729
$section->addInput(new Form_StaticText(
730
	null,
731
	gettext('The following certificate authority subject components are optional and may be left blank.')
732
));
733

    
734
$section->addInput(new Form_Select(
735
	'dn_country',
736
	'Country Code',
737
	$pconfig['dn_country'],
738
	get_cert_country_codes()
739
));
740

    
741
$section->addInput(new Form_Input(
742
	'dn_state',
743
	'State or Province',
744
	'text',
745
	$pconfig['dn_state'],
746
	['placeholder' => 'e.g. Texas']
747
));
748

    
749
$section->addInput(new Form_Input(
750
	'dn_city',
751
	'City',
752
	'text',
753
	$pconfig['dn_city'],
754
	['placeholder' => 'e.g. Austin']
755
));
756

    
757
$section->addInput(new Form_Input(
758
	'dn_organization',
759
	'Organization',
760
	'text',
761
	$pconfig['dn_organization'],
762
	['placeholder' => 'e.g. My Company Inc']
763
));
764

    
765
$section->addInput(new Form_Input(
766
	'dn_organizationalunit',
767
	'Organizational Unit',
768
	'text',
769
	$pconfig['dn_organizationalunit'],
770
	['placeholder' => 'e.g. My Department Name (optional)']
771
));
772

    
773
$form->add($section);
774

    
775
print $form;
776

    
777
$internal_ca_count = 0;
778
foreach (config_get_path('ca', []) as $ca) {
779
	if ($ca['prv']) {
780
		$internal_ca_count++;
781
	}
782
}
783

    
784
?>
785
<script type="text/javascript">
786
//<![CDATA[
787
events.push(function() {
788
	function change_keytype() {
789
	hideClass('rsakeys', ($('#keytype').val() != 'RSA'));
790
		hideClass('ecnames', ($('#keytype').val() != 'ECDSA'));
791
	}
792

    
793
	$('#keytype').change(function () {
794
		change_keytype();
795
	});
796

    
797
	function check_keylen() {
798
		var min_keylen = <?= $cert_strict_values['min_private_key_bits'] ?>;
799
		var klid = '#keylen';
800
		/* Color the Parent/Label */
801
		if (parseInt($(klid).val()) < min_keylen) {
802
			$(klid).parent().parent().removeClass("text-normal").addClass("text-warning");
803
		} else {
804
			$(klid).parent().parent().removeClass("text-warning").addClass("text-normal");
805
		}
806
		/* Color individual options */
807
		$(klid + " option").filter(function() {
808
			return parseInt($(this).val()) < min_keylen;
809
		}).removeClass("text-normal").addClass("text-warning").siblings().removeClass("text-warning").addClass("text-normal");
810
	}
811

    
812
	function check_digest() {
813
		var weak_algs = <?= json_encode($cert_strict_values['digest_blacklist']) ?>;
814
		var daid = '#digest_alg';
815
		/* Color the Parent/Label */
816
		if (jQuery.inArray($(daid).val(), weak_algs) > -1) {
817
			$(daid).parent().parent().removeClass("text-normal").addClass("text-warning");
818
		} else {
819
			$(daid).parent().parent().removeClass("text-warning").addClass("text-normal");
820
		}
821
		/* Color individual options */
822
		$(daid + " option").filter(function() {
823
			return (jQuery.inArray($(this).val(), weak_algs) > -1);
824
		}).removeClass("text-normal").addClass("text-warning").siblings().removeClass("text-warning").addClass("text-normal");
825
	}
826

    
827
	// ---------- Control change handlers ---------------------------------------------------------
828

    
829
	$('#method').on('change', function() {
830
		check_keylen();
831
		check_digest();
832
	});
833

    
834
	$('#keylen').on('change', function() {
835
		check_keylen();
836
	});
837

    
838
	$('#digest_alg').on('change', function() {
839
		check_digest();
840
	});
841

    
842
	// ---------- On initial page load ------------------------------------------------------------
843
	change_keytype();
844
	check_keylen();
845
	check_digest();
846
});
847
//]]>
848
</script>
849
<?php
850
include('foot.inc');
(197-197/233)