Project

General

Profile

Download (26.9 KB) Statistics
| Branch: | Tag: | Revision:
1 64cc39d3 Matthew Grooms
<?php
2
/*
3 c5d81585 Renato Botelho
 * system_camanager.php
4 f74457df Stephen Beaver
 *
5 c5d81585 Renato Botelho
 * part of pfSense (https://www.pfsense.org)
6 38809d47 Renato Botelho do Couto
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8 a68f7a3d Luiz Otavio O Souza
 * Copyright (c) 2014-2024 Rubicon Communications, LLC (Netgate)
9 c5d81585 Renato Botelho
 * Copyright (c) 2008 Shrew Soft Inc
10
 * All rights reserved.
11 f74457df Stephen Beaver
 *
12 b12ea3fb Renato Botelho
 * 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 f74457df Stephen Beaver
 *
16 b12ea3fb Renato Botelho
 * http://www.apache.org/licenses/LICENSE-2.0
17 f74457df Stephen Beaver
 *
18 b12ea3fb Renato Botelho
 * 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 f74457df Stephen Beaver
 */
24 64cc39d3 Matthew Grooms
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 c81ef6e2 Phil Davis
require_once("guiconfig.inc");
33 742d9c2d Ermal Lu?i
require_once("certs.inc");
34 afb7b75e doktornotor
require_once("pfsense-utils.inc");
35 64cc39d3 Matthew Grooms
36
$ca_methods = array(
37 95c8cf48 Evgeny Yurchenko
	"internal" => gettext("Create an internal Certificate Authority"),
38 b0a5c280 jim-p
	"existing" => gettext("Import an existing Certificate Authority"),
39 95c8cf48 Evgeny Yurchenko
	"intermediate" => gettext("Create an intermediate Certificate Authority"));
40 64cc39d3 Matthew Grooms
41 36cfae5f Justin Coffman
$ca_keylens = array("1024", "2048", "3072", "4096", "6144", "7680", "8192", "15360", "16384");
42 ff5bc49c Viktor Gurov
$ca_keytypes = array("RSA", "ECDSA");
43 84141846 jim-p
global $openssl_digest_algs;
44 3f0b7bc3 jim-p
global $cert_strict_values;
45 3a877e4a jim-p
$max_lifetime = cert_get_max_lifetime();
46
$default_lifetime = min(3650, $max_lifetime);
47 cffcf9bf jim-p
$openssl_ecnames = cert_build_curve_list();
48 f0b38e39 jim-p
$class = "success";
49 64cc39d3 Matthew Grooms
50 c6c398c6 jim-p
init_config_arr(array('ca'));
51
$a_ca = &$config['ca'];
52 64cc39d3 Matthew Grooms
53 c6c398c6 jim-p
init_config_arr(array('cert'));
54
$a_cert = &$config['cert'];
55 461aa9d0 jim-p
56 c6c398c6 jim-p
init_config_arr(array('crl'));
57
$a_crl = &$config['crl'];
58 461aa9d0 jim-p
59 f0b38e39 jim-p
$act = $_REQUEST['act'];
60
61
if (isset($_REQUEST['id']) && ctype_alnum($_REQUEST['id'])) {
62
	$id = $_REQUEST['id'];
63
}
64
if (!empty($id)) {
65
	$thisca =& lookup_ca($id);
66 56b1ed39 Phil Davis
}
67 64cc39d3 Matthew Grooms
68 7e83055a jim-p
/* Actions other than 'new' require an ID.
69
 * 'del' action must be submitted via POST. */
70
if ((!empty($act) &&
71
    ($act != 'new') &&
72 f0b38e39 jim-p
    !$thisca) ||
73 7e83055a jim-p
    (($act == 'del') && empty($_POST))) {
74 2f51259b jim-p
	pfSenseHeader("system_camanager.php");
75
	exit;
76 64cc39d3 Matthew Grooms
}
77
78 7e83055a jim-p
switch ($act) {
79
	case 'del':
80 f0b38e39 jim-p
		$name = htmlspecialchars($thisca['descr']);
81
		if (cert_in_use($id)) {
82
			$savemsg = sprintf(gettext("Certificate %s is in use and cannot be deleted"), $name);
83
			$class = "danger";
84
		} else {
85
			/* Only remove CA reference when deleting. It can be reconnected if a new matching CA is imported */
86
			foreach ($a_cert as $cid => $acrt) {
87
				if ($acrt['caref'] == $thisca['refid']) {
88
					unset($a_cert[$cid]['caref']);
89
				}
90 7e83055a jim-p
			}
91 f0b38e39 jim-p
			/* Remove any CRLs for this CA, there is no way to recover the connection once the CA has been removed. */
92
			foreach ($a_crl as $cid => $acrl) {
93
				if ($acrl['caref'] == $thisca['refid']) {
94
					unset($a_crl[$cid]);
95
				}
96 7e83055a jim-p
			}
97 f0b38e39 jim-p
			/* Delete the CA */
98
			foreach ($a_ca as $cid => $aca) {
99
				if ($aca['refid'] == $thisca['refid']) {
100
					unset($a_ca[$cid]);
101
				}
102
			}
103
			$savemsg = sprintf(gettext("Deleted Certificate Authority %s and associated CRLs"), htmlspecialchars($name));
104
			write_config($savemsg);
105
			ca_setup_trust_store();
106 7e83055a jim-p
		}
107 f0b38e39 jim-p
		unset($act);
108 7e83055a jim-p
		break;
109
	case 'edit':
110
		/* Editing an existing CA, so populate values. */
111
		$pconfig['method'] = 'existing';
112 f0b38e39 jim-p
		$pconfig['descr']  = $thisca['descr'];
113
		$pconfig['refid']  = $thisca['refid'];
114
		$pconfig['cert']   = base64_decode($thisca['crt']);
115
		$pconfig['serial'] = $thisca['serial'];
116
		$pconfig['trust']  = ($thisca['trust'] == 'enabled');
117
		$pconfig['randomserial']  = ($thisca['randomserial'] == 'enabled');
118
		if (!empty($thisca['prv'])) {
119
			$pconfig['key'] = base64_decode($thisca['prv']);
120 7e83055a jim-p
		}
121
		break;
122
	case 'new':
123
		/* New CA, so set default values */
124
		$pconfig['method'] = $_POST['method'];
125
		$pconfig['keytype'] = "RSA";
126
		$pconfig['keylen'] = "2048";
127 c3cda38e jim-p
		$pconfig['ecname'] = "prime256v1";
128 7e83055a jim-p
		$pconfig['digest_alg'] = "sha256";
129
		$pconfig['lifetime'] = $default_lifetime;
130
		$pconfig['dn_commonname'] = "internal-ca";
131
		break;
132
	case 'exp':
133
		/* Exporting a ca */
134 f0b38e39 jim-p
		send_user_download('data', base64_decode($thisca['crt']), "{$thisca['descr']}.crt");
135 7e83055a jim-p
		break;
136
	case 'expkey':
137
		/* Exporting a private key */
138 f0b38e39 jim-p
		send_user_download('data', base64_decode($thisca['prv']), "{$thisca['descr']}.key");
139 7e83055a jim-p
		break;
140
	default:
141
		break;
142 ecefc738 jim-p
}
143
144 1355f71c Steve Beaver
if ($_POST['save']) {
145 95c8cf48 Evgeny Yurchenko
	unset($input_errors);
146 2b8bfda4 Phil Davis
	$input_errors = array();
147 64cc39d3 Matthew Grooms
	$pconfig = $_POST;
148
149
	/* input validation */
150 7e83055a jim-p
	switch ($pconfig['method']) {
151
		case 'existing':
152
			$reqdfields = explode(" ", "descr cert");
153
			$reqdfieldsn = array(
154
				gettext("Descriptive name"),
155
				gettext("Certificate data"));
156 2fe0e0fa jim-p
			/* Make sure we do not have invalid characters in the fields for the certificate */
157
			if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
158
				array_push($input_errors, gettext("The field 'Descriptive Name' contains invalid characters."));
159
			}
160 7e83055a jim-p
			if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
161
				$input_errors[] = gettext("This certificate does not appear to be valid.");
162 9e608d7a jim-p
			}
163 7e83055a jim-p
			if ($_POST['key'] && strstr($_POST['key'], "ENCRYPTED")) {
164
				$input_errors[] = gettext("Encrypted private keys are not yet supported.");
165
			}
166
			if (!$input_errors && !empty($_POST['key']) && cert_get_publickey($_POST['cert'], false) != cert_get_publickey($_POST['key'], false, 'prv')) {
167
				$input_errors[] = gettext("The submitted private key does not match the submitted certificate data.");
168
			}
169
			/* we must ensure the certificate is capable of acting as a CA
170
			 * https://redmine.pfsense.org/issues/7885
171
			 */
172
			if (!$input_errors) {
173
				$purpose = cert_get_purpose($_POST['cert'], false);
174
				if ($purpose['ca'] != 'Yes') {
175
					$input_errors[] = gettext("The submitted certificate does not appear to be a Certificate Authority, import it on the Certificates tab instead.");
176
				}
177
			}
178
			break;
179
		case 'internal':
180
			$reqdfields = explode(" ",
181
				"descr keylen ecname keytype lifetime dn_commonname");
182
			$reqdfieldsn = array(
183
				gettext("Descriptive name"),
184
				gettext("Key length"),
185
				gettext("Elliptic Curve Name"),
186
				gettext("Key type"),
187
				gettext("Lifetime"),
188
				gettext("Common Name"));
189
			break;
190
		case 'intermediate':
191
			$reqdfields = explode(" ",
192
				"descr caref keylen ecname keytype lifetime dn_commonname");
193
			$reqdfieldsn = array(
194
				gettext("Descriptive name"),
195
				gettext("Signing Certificate Authority"),
196
				gettext("Key length"),
197
				gettext("Elliptic Curve Name"),
198
				gettext("Key type"),
199
				gettext("Lifetime"),
200
				gettext("Common Name"));
201
			break;
202
		default:
203
			break;
204 95c8cf48 Evgeny Yurchenko
	}
205 64cc39d3 Matthew Grooms
206 1e9b4611 Renato Botelho
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
207 ca621902 jim-p
	if ($pconfig['method'] != "existing") {
208 21cc2faa Evgeny Yurchenko
		/* Make sure we do not have invalid characters in the fields for the certificate */
209 b75cdd94 jim-p
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
210 762faef5 Phil Davis
			array_push($input_errors, gettext("The field 'Descriptive Name' contains invalid characters."));
211 b75cdd94 jim-p
		}
212 fe31d06f Viktor G
		$pattern = '/[^a-zA-Z0-9\ \'\/~`\!@#\$%\^&\*\(\)_\-\+=\{\}\[\]\|;:"\<\>,\.\?\\\]/';
213
		if (!empty($_POST['dn_commonname']) && preg_match($pattern, $_POST['dn_commonname'])) {
214
			$input_errors[] = gettext("The field 'Common Name' contains invalid characters.");
215
		}
216
		if (!empty($_POST['dn_state']) && preg_match($pattern, $_POST['dn_state'])) {
217
			$input_errors[] = gettext("The field 'State or Province' contains invalid characters.");
218
		}
219
		if (!empty($_POST['dn_city']) && preg_match($pattern, $_POST['dn_city'])) {
220
			$input_errors[] = gettext("The field 'City' contains invalid characters.");
221
		}
222
		if (!empty($_POST['dn_organization']) && preg_match($pattern, $_POST['dn_organization'])) {
223
			$input_errors[] = gettext("The field 'Organization' contains invalid characters.");
224
		}
225
		if (!empty($_POST['dn_organizationalunit']) && preg_match($pattern, $_POST['dn_organizationalunit'])) {
226
			$input_errors[] = gettext("The field 'Organizational Unit' contains invalid characters.");
227
		}
228 ff5bc49c Viktor Gurov
		if (!in_array($_POST["keytype"], $ca_keytypes)) {
229
			array_push($input_errors, gettext("Please select a valid Key Type."));
230
		}
231 56b1ed39 Phil Davis
		if (!in_array($_POST["keylen"], $ca_keylens)) {
232 ca621902 jim-p
			array_push($input_errors, gettext("Please select a valid Key Length."));
233 56b1ed39 Phil Davis
		}
234 cffcf9bf jim-p
		if (!in_array($_POST["ecname"], array_keys($openssl_ecnames))) {
235 ff5bc49c Viktor Gurov
			array_push($input_errors, gettext("Please select a valid Elliptic Curve Name."));
236
		}
237 56b1ed39 Phil Davis
		if (!in_array($_POST["digest_alg"], $openssl_digest_algs)) {
238 ca621902 jim-p
			array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
239 56b1ed39 Phil Davis
		}
240 3a877e4a jim-p
		if ($_POST['lifetime'] > $max_lifetime) {
241
			$input_errors[] = gettext("Lifetime is longer than the maximum allowed value. Use a shorter lifetime.");
242
		}
243 ca621902 jim-p
	}
244 1d6f93c5 Stephen Beaver
245 a6bd9e78 jim-p
	if (!empty($_POST['serial']) && !cert_validate_serial($_POST['serial'])) {
246
		$input_errors[] = gettext("Please enter a valid integer serial number.");
247
	}
248
249 64cc39d3 Matthew Grooms
	/* save modifications */
250
	if (!$input_errors) {
251
		$ca = array();
252 56b1ed39 Phil Davis
		if (!isset($pconfig['refid']) || empty($pconfig['refid'])) {
253 bfa992bc jim-p
			$ca['refid'] = uniqid();
254 56b1ed39 Phil Davis
		} else {
255 bfa992bc jim-p
			$ca['refid'] = $pconfig['refid'];
256 56b1ed39 Phil Davis
		}
257 bfa992bc jim-p
258 f0b38e39 jim-p
		if (isset($id) && $thisca) {
259
			$ca = $thisca;
260 56b1ed39 Phil Davis
		}
261 64cc39d3 Matthew Grooms
262 bfa992bc jim-p
		$ca['descr'] = $pconfig['descr'];
263 7daab3d8 jim-p
		$ca['trust'] = ($pconfig['trust'] == 'yes') ? "enabled" : "disabled";
264 2c9601c9 jim-p
		$ca['randomserial'] = ($pconfig['randomserial'] == 'yes') ? "enabled" : "disabled";
265 bfa992bc jim-p
266 5d2edeca Sjon Hortensius
		if ($act == "edit") {
267 bfa992bc jim-p
			$ca['descr']  = $pconfig['descr'];
268
			$ca['refid']  = $pconfig['refid'];
269
			$ca['serial'] = $pconfig['serial'];
270 0447f01b Viktor G
			$ca['crt'] = base64_encode($pconfig['cert']);
271
			$ca['prv'] = base64_encode($pconfig['key']);
272 f0b38e39 jim-p
			$savemsg = sprintf(gettext("Updated Certificate Authority %s"), $ca['descr']);
273 bfa992bc jim-p
		} else {
274 f416763b Phil Davis
			$old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */
275 56b1ed39 Phil Davis
			if ($pconfig['method'] == "existing") {
276 bfa992bc jim-p
				ca_import($ca, $pconfig['cert'], $pconfig['key'], $pconfig['serial']);
277 f0b38e39 jim-p
				$savemsg = sprintf(gettext("Imported Certificate Authority %s"), $ca['descr']);
278 56b1ed39 Phil Davis
			} else if ($pconfig['method'] == "internal") {
279 692510f2 Viktor G
				$dn = array('commonName' => $pconfig['dn_commonname']);
280 80d50253 jim-p
				if (!empty($pconfig['dn_country'])) {
281
					$dn['countryName'] = $pconfig['dn_country'];
282
				}
283
				if (!empty($pconfig['dn_state'])) {
284 692510f2 Viktor G
					$dn['stateOrProvinceName'] = $pconfig['dn_state'];
285 80d50253 jim-p
				}
286
				if (!empty($pconfig['dn_city'])) {
287 692510f2 Viktor G
					$dn['localityName'] = $pconfig['dn_city'];
288 80d50253 jim-p
				}
289
				if (!empty($pconfig['dn_organization'])) {
290 692510f2 Viktor G
					$dn['organizationName'] = $pconfig['dn_organization'];
291 80d50253 jim-p
				}
292 da0f70ed jim-p
				if (!empty($pconfig['dn_organizationalunit'])) {
293 692510f2 Viktor G
					$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
294 da0f70ed jim-p
				}
295 ff5bc49c Viktor Gurov
				if (!ca_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['digest_alg'], $pconfig['keytype'], $pconfig['ecname'])) {
296 5ce9bcf5 jim-p
					$input_errors = array();
297 56b1ed39 Phil Davis
					while ($ssl_err = openssl_error_string()) {
298 5ce9bcf5 jim-p
						if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
299
							array_push($input_errors, "openssl library returns: " . $ssl_err);
300
						}
301 1b6d9fa5 Evgeny Yurchenko
					}
302
				}
303 f0b38e39 jim-p
				$savemsg = sprintf(gettext("Created internal Certificate Authority %s"), $ca['descr']);
304 78863416 Phil Davis
			} else if ($pconfig['method'] == "intermediate") {
305 692510f2 Viktor G
				$dn = array('commonName' => $pconfig['dn_commonname']);
306 80d50253 jim-p
				if (!empty($pconfig['dn_country'])) {
307
					$dn['countryName'] = $pconfig['dn_country'];
308
				}
309
				if (!empty($pconfig['dn_state'])) {
310 692510f2 Viktor G
					$dn['stateOrProvinceName'] = $pconfig['dn_state'];
311 80d50253 jim-p
				}
312
				if (!empty($pconfig['dn_city'])) {
313 692510f2 Viktor G
					$dn['localityName'] = $pconfig['dn_city'];
314 80d50253 jim-p
				}
315
				if (!empty($pconfig['dn_organization'])) {
316 692510f2 Viktor G
					$dn['organizationName'] = $pconfig['dn_organization'];
317 80d50253 jim-p
				}
318 da0f70ed jim-p
				if (!empty($pconfig['dn_organizationalunit'])) {
319 692510f2 Viktor G
					$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
320 da0f70ed jim-p
				}
321 ff5bc49c Viktor Gurov
				if (!ca_inter_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['caref'], $pconfig['digest_alg'], $pconfig['keytype'], $pconfig['ecname'])) {
322 5ce9bcf5 jim-p
					$input_errors = array();
323 56b1ed39 Phil Davis
					while ($ssl_err = openssl_error_string()) {
324 5ce9bcf5 jim-p
						if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
325
							array_push($input_errors, "openssl library returns: " . $ssl_err);
326
						}
327 95c8cf48 Evgeny Yurchenko
					}
328
				}
329 f0b38e39 jim-p
				$savemsg = sprintf(gettext("Created internal intermediate Certificate Authority %s"), $ca['descr']);
330 95c8cf48 Evgeny Yurchenko
			}
331 1b6d9fa5 Evgeny Yurchenko
			error_reporting($old_err_level);
332 64cc39d3 Matthew Grooms
		}
333
334 f0b38e39 jim-p
		if (isset($id) && $thisca) {
335
			$thisca = $ca;
336 56b1ed39 Phil Davis
		} else {
337 64cc39d3 Matthew Grooms
			$a_ca[] = $ca;
338 56b1ed39 Phil Davis
		}
339 64cc39d3 Matthew Grooms
340 56b1ed39 Phil Davis
		if (!$input_errors) {
341 f0b38e39 jim-p
			write_config($savemsg);
342 7daab3d8 jim-p
			ca_setup_trust_store();
343 5ce9bcf5 jim-p
			pfSenseHeader("system_camanager.php");
344 56b1ed39 Phil Davis
		}
345 64cc39d3 Matthew Grooms
	}
346
}
347
348 c8014348 Christian McDonald
$pgtitle = array(gettext('System'), gettext('Certificate'), gettext('Authorities'));
349 edcd7535 Phil Davis
$pglinks = array("", "system_camanager.php", "system_camanager.php");
350 56c6b1cb k-paulius
351
if ($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors) {
352
	$pgtitle[] = gettext('Edit');
353 edcd7535 Phil Davis
	$pglinks[] = "@self";
354 56c6b1cb k-paulius
}
355 64cc39d3 Matthew Grooms
include("head.inc");
356
357 78863416 Phil Davis
if ($input_errors) {
358 5d2edeca Sjon Hortensius
	print_input_errors($input_errors);
359 78863416 Phil Davis
}
360 b8f22f61 Stephen Beaver
361 78863416 Phil Davis
if ($savemsg) {
362 f0b38e39 jim-p
	print_info_box($savemsg, $class);
363 78863416 Phil Davis
}
364 5d2edeca Sjon Hortensius
365
$tab_array = array();
366 c8014348 Christian McDonald
$tab_array[] = array(gettext('Authorities'), true, 'system_camanager.php');
367
$tab_array[] = array(gettext('Certificates'), false, 'system_certmanager.php');
368
$tab_array[] = array(gettext('Revocation'), false, 'system_crlmanager.php');
369 5d2edeca Sjon Hortensius
display_top_tabs($tab_array);
370
371 78863416 Phil Davis
if (!($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors)) {
372 5d2edeca Sjon Hortensius
?>
373 14973058 jim-p
<div class="panel panel-default" id="search-panel">
374
	<div class="panel-heading">
375
		<h2 class="panel-title">
376
			<?=gettext('Search')?>
377
			<span class="widget-heading-icon pull-right">
378
				<a data-toggle="collapse" href="#search-panel_panel-body">
379 e0cb987c Marcos Mendoza
					<i class="fa-solid fa-plus-circle"></i>
380 14973058 jim-p
				</a>
381
			</span>
382
		</h2>
383
	</div>
384
	<div id="search-panel_panel-body" class="panel-body collapse in">
385
		<div class="form-group">
386
			<label class="col-sm-2 control-label">
387
				<?=gettext("Search term")?>
388
			</label>
389
			<div class="col-sm-5"><input class="form-control" name="searchstr" id="searchstr" type="text"/></div>
390
			<div class="col-sm-2">
391
				<select id="where" class="form-control">
392
					<option value="0"><?=gettext("Name")?></option>
393
					<option value="1"><?=gettext("Distinguished Name")?></option>
394
					<option value="2" selected><?=gettext("Both")?></option>
395
				</select>
396
			</div>
397
			<div class="col-sm-3">
398 e0cb987c Marcos Mendoza
				<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>
399
				<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>
400 14973058 jim-p
			</div>
401
			<div class="col-sm-10 col-sm-offset-2">
402 f30da999 jim-p
				<span class="help-block"><?=gettext('Enter a search string or *nix regular expression to search certificate names and distinguished names.')?></span>
403 14973058 jim-p
			</div>
404
		</div>
405
	</div>
406
</div>
407
408 060ed238 Stephen Beaver
<div class="panel panel-default">
409
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificate Authorities')?></h2></div>
410
	<div class="panel-body">
411
		<div class="table-responsive">
412 14973058 jim-p
		<table id="catable" class="table table-striped table-hover table-rowdblclickedit sortable-theme-bootstrap" data-sortable>
413 060ed238 Stephen Beaver
			<thead>
414
				<tr>
415
					<th><?=gettext("Name")?></th>
416
					<th><?=gettext("Internal")?></th>
417
					<th><?=gettext("Issuer")?></th>
418
					<th><?=gettext("Certificates")?></th>
419
					<th><?=gettext("Distinguished Name")?></th>
420 80080a0c jim-p
					<th><?=gettext("In Use")?></th>
421 060ed238 Stephen Beaver
					<th><?=gettext("Actions")?></th>
422
				</tr>
423
			</thead>
424
			<tbody>
425 64cc39d3 Matthew Grooms
<?php
426 3bde5cdd PiBa-NL
$pluginparams = array();
427
$pluginparams['type'] = 'certificates';
428
$pluginparams['event'] = 'used_ca';
429
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
430
431 f0b38e39 jim-p
foreach ($a_ca as $ca):
432 5d2edeca Sjon Hortensius
	$name = htmlspecialchars($ca['descr']);
433
	$subj = cert_get_subject($ca['crt']);
434
	$issuer = cert_get_issuer($ca['crt']);
435 78863416 Phil Davis
	if ($subj == $issuer) {
436 a2a10102 Sjon Hortensius
		$issuer_name = gettext("self-signed");
437 78863416 Phil Davis
	} else {
438 a2a10102 Sjon Hortensius
		$issuer_name = gettext("external");
439 78863416 Phil Davis
	}
440 83d2b83a jim-p
	$subj = htmlspecialchars(cert_escape_x509_chars($subj, true));
441 5d2edeca Sjon Hortensius
	$issuer = htmlspecialchars($issuer);
442
	$certcount = 0;
443
444
	$issuer_ca = lookup_ca($ca['caref']);
445 78863416 Phil Davis
	if ($issuer_ca) {
446 2fe0e0fa jim-p
		$issuer_name = htmlspecialchars($issuer_ca['descr']);
447 78863416 Phil Davis
	}
448 5d2edeca Sjon Hortensius
449 78863416 Phil Davis
	foreach ($a_cert as $cert) {
450
		if ($cert['caref'] == $ca['refid']) {
451 5d2edeca Sjon Hortensius
			$certcount++;
452 78863416 Phil Davis
		}
453
	}
454 5d2edeca Sjon Hortensius
455 78863416 Phil Davis
	foreach ($a_ca as $cert) {
456
		if ($cert['caref'] == $ca['refid']) {
457 5d2edeca Sjon Hortensius
			$certcount++;
458 78863416 Phil Davis
		}
459
	}
460 64cc39d3 Matthew Grooms
?>
461 060ed238 Stephen Beaver
				<tr>
462
					<td><?=$name?></td>
463 d365c2c7 Marcos Mendoza
					<td><i class="<?= (!empty($ca['prv'])) ? "fa-solid fa-check" : "fa-solid fa-times" ; ?>"></i></td>
464 060ed238 Stephen Beaver
					<td><i><?=$issuer_name?></i></td>
465
					<td><?=$certcount?></td>
466
					<td>
467
						<?=$subj?>
468 fe72327b jim-p
						<?php cert_print_infoblock($ca); ?>
469
						<?php cert_print_dates($ca);?>
470 060ed238 Stephen Beaver
					</td>
471 80080a0c jim-p
					<td class="text-nowrap">
472 fe72327b jim-p
						<?php if (is_openvpn_server_ca($ca['refid'])): ?>
473
							<?=gettext("OpenVPN Server")?><br/>
474
						<?php endif?>
475
						<?php if (is_openvpn_client_ca($ca['refid'])): ?>
476
							<?=gettext("OpenVPN Client")?><br/>
477
						<?php endif?>
478
						<?php if (is_ipsec_peer_ca($ca['refid'])): ?>
479
							<?=gettext("IPsec Tunnel")?><br/>
480
						<?php endif?>
481
						<?php if (is_ldap_peer_ca($ca['refid'])): ?>
482
							<?=gettext("LDAP Server")?>
483
						<?php endif?>
484
						<?php echo cert_usedby_description($ca['refid'], $certificates_used_by_packages); ?>
485 80080a0c jim-p
					</td>
486
					<td class="text-nowrap">
487 e0cb987c Marcos Mendoza
						<a class="fa-solid fa-pencil"	title="<?=gettext("Edit CA")?>"	href="system_camanager.php?act=edit&amp;id=<?=$ca['refid']?>"></a>
488
						<a class="fa-solid fa-certificate"	title="<?=gettext("Export CA")?>"	href="system_camanager.php?act=exp&amp;id=<?=$ca['refid']?>"></a>
489 060ed238 Stephen Beaver
					<?php if ($ca['prv']): ?>
490 e0cb987c Marcos Mendoza
						<a class="fa-solid fa-key"	title="<?=gettext("Export key")?>"	href="system_camanager.php?act=expkey&amp;id=<?=$ca['refid']?>"></a>
491 060ed238 Stephen Beaver
					<?php endif?>
492 03a84081 jim-p
					<?php if (is_cert_locally_renewable($ca)): ?>
493 c1d304b3 Marcos Mendoza
						<a href="system_certmanager_renew.php?type=ca&amp;refid=<?=$ca['refid']?>" class="fa-solid fa-arrow-rotate-right" title="<?=gettext("Reissue/Renew")?>"></a>
494 03a84081 jim-p
					<?php endif ?>
495 80080a0c jim-p
					<?php if (!ca_in_use($ca['refid'])): ?>
496 c1d304b3 Marcos Mendoza
						<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>
497 80080a0c jim-p
					<?php endif?>
498 060ed238 Stephen Beaver
					</td>
499
				</tr>
500 5d2edeca Sjon Hortensius
<?php endforeach; ?>
501 060ed238 Stephen Beaver
			</tbody>
502
		</table>
503
		</div>
504
	</div>
505 04f1a496 NOYB
</div>
506 64cc39d3 Matthew Grooms
507 c10cb196 Stephen Beaver
<nav class="action-buttons">
508 4611e283 Steve Beaver
	<a href="?act=new" class="btn btn-success btn-sm">
509 e0cb987c Marcos Mendoza
		<i class="fa-solid fa-plus icon-embed-btn"></i>
510 f74457df Stephen Beaver
		<?=gettext("Add")?>
511
	</a>
512 5d2edeca Sjon Hortensius
</nav>
513 14973058 jim-p
<script type="text/javascript">
514
//<![CDATA[
515
516
events.push(function() {
517
518
	// Make these controls plain buttons
519
	$("#btnsearch").prop('type', 'button');
520
	$("#btnclear").prop('type', 'button');
521
522
	// Search for a term in the entry name and/or dn
523
	$("#btnsearch").click(function() {
524
		var searchstr = $('#searchstr').val().toLowerCase();
525
		var table = $("table tbody");
526
		var where = $('#where').val();
527
528
		table.find('tr').each(function (i) {
529
			var $tds = $(this).find('td'),
530
				shortname = $tds.eq(0).text().trim().toLowerCase(),
531
				dn = $tds.eq(4).text().trim().toLowerCase();
532
533
			regexp = new RegExp(searchstr);
534
			if (searchstr.length > 0) {
535
				if (!(regexp.test(shortname) && (where != 1)) && !(regexp.test(dn) && (where != 0))) {
536
					$(this).hide();
537
				} else {
538
					$(this).show();
539
				}
540
			} else {
541
				$(this).show();	// A blank search string shows all
542
			}
543
		});
544
	});
545
546
	// Clear the search term and unhide all rows (that were hidden during a previous search)
547
	$("#btnclear").click(function() {
548
		var table = $("table tbody");
549
550
		$('#searchstr').val("");
551
552
		table.find('tr').each(function (i) {
553
			$(this).show();
554
		});
555
	});
556
557
	// Hitting the enter key will do the same as clicking the search button
558
	$("#searchstr").on("keyup", function (event) {
559
		if (event.keyCode == 13) {
560
			$("#btnsearch").get(0).click();
561
		}
562
	});
563
});
564
//]]>
565
</script>
566
567 e9258698 NewEraCracker
<?php
568 5d2edeca Sjon Hortensius
	include("foot.inc");
569
	exit;
570
}
571 96c7a492 Matthew Grooms
572 5d2edeca Sjon Hortensius
$form = new Form;
573 b155730f Stephen Beaver
//$form->setAction('system_camanager.php?act=edit');
574 f0b38e39 jim-p
if (isset($id) && $thisca) {
575 5d2edeca Sjon Hortensius
	$form->addGlobal(new Form_Input(
576
		'id',
577
		null,
578
		'hidden',
579
		$id
580
	));
581
}
582 64cc39d3 Matthew Grooms
583 78863416 Phil Davis
if ($act == "edit") {
584 5d2edeca Sjon Hortensius
	$form->addGlobal(new Form_Input(
585
		'refid',
586
		null,
587
		'hidden',
588
		$pconfig['refid']
589
	));
590
}
591
592 5f88f964 k-paulius
$section = new Form_Section('Create / Edit CA');
593 5d2edeca Sjon Hortensius
594
$section->addInput(new Form_Input(
595
	'descr',
596 153c3aa6 Phil Davis
	'*Descriptive name',
597 5d2edeca Sjon Hortensius
	'text',
598
	$pconfig['descr']
599 f16d3f4d jim-p
))->setHelp('The name of this entry as displayed in the GUI for reference.%s' .
600
		'This name can contain spaces but it cannot contain any of the ' .
601
		'following characters: %s', '<br/>', "?, >, <, &, /, \, \", '");
602 5d2edeca Sjon Hortensius
603 78863416 Phil Davis
if (!isset($id) || $act == "edit") {
604 5d2edeca Sjon Hortensius
	$section->addInput(new Form_Select(
605
		'method',
606 153c3aa6 Phil Davis
		'*Method',
607 5d2edeca Sjon Hortensius
		$pconfig['method'],
608
		$ca_methods
609 44d906ca Sjon Hortensius
	))->toggles();
610 5d2edeca Sjon Hortensius
}
611 64cc39d3 Matthew Grooms
612 94ce250e jim-p
$section->addInput(new Form_Checkbox(
613
	'trust',
614
	'Trust Store',
615
	'Add this Certificate Authority to the Operating System Trust Store',
616
	$pconfig['trust']
617
))->setHelp('When enabled, the contents of the CA will be added to the trust ' .
618
	'store so that they will be trusted by the operating system.');
619
620
$section->addInput(new Form_Checkbox(
621
	'randomserial',
622
	'Randomize Serial',
623 aa8af662 jim-p
	'Use random serial numbers when signing certificates',
624 94ce250e jim-p
	$pconfig['randomserial']
625
))->setHelp('When enabled, if this CA is capable of signing certificates then ' .
626
		'serial numbers for certificates signed by this CA will be ' .
627
		'automatically randomized and checked for uniqueness instead of ' .
628
		'using the sequential value from Next Certificate Serial.');
629
630 5d2edeca Sjon Hortensius
$form->add($section);
631
632
$section = new Form_Section('Existing Certificate Authority');
633
$section->addClass('toggle-existing collapse');
634
635
$section->addInput(new Form_Textarea(
636
	'cert',
637 153c3aa6 Phil Davis
	'*Certificate data',
638 5d2edeca Sjon Hortensius
	$pconfig['cert']
639
))->setHelp('Paste a certificate in X.509 PEM format here.');
640
641
$section->addInput(new Form_Textarea(
642
	'key',
643
	'Certificate Private Key (optional)',
644
	$pconfig['key']
645
))->setHelp('Paste the private key for the above certificate here. This is '.
646 10ddac8a NOYB
	'optional in most cases, but is required when generating a '.
647 5d2edeca Sjon Hortensius
	'Certificate Revocation List (CRL).');
648
649
$section->addInput(new Form_Input(
650
	'serial',
651 2c9601c9 jim-p
	'Next Certificate Serial',
652 5d2edeca Sjon Hortensius
	'number',
653
	$pconfig['serial']
654 2c9601c9 jim-p
))->setHelp('Enter a decimal number to be used as a sequential serial number for ' .
655 663e29bb jim-p
	'the next certificate to be signed by this CA. This value is ignored ' .
656 4dc0624a jim-p
	'when Randomize Serial is checked.');
657 5d2edeca Sjon Hortensius
658
$form->add($section);
659
660
$section = new Form_Section('Internal Certificate Authority');
661
$section->addClass('toggle-internal', 'toggle-intermediate', 'collapse');
662
663
$allCas = array();
664 78863416 Phil Davis
foreach ($a_ca as $ca) {
665
	if (!$ca['prv']) {
666 5d2edeca Sjon Hortensius
			continue;
667 78863416 Phil Davis
	}
668 5d2edeca Sjon Hortensius
669
	$allCas[ $ca['refid'] ] = $ca['descr'];
670
}
671 64cc39d3 Matthew Grooms
672 153c3aa6 Phil Davis
$group = new Form_Group('*Signing Certificate Authority');
673 b8f22f61 Stephen Beaver
$group->addClass('toggle-intermediate', 'collapse');
674 5d2edeca Sjon Hortensius
$group->add(new Form_Select(
675
	'caref',
676
	null,
677
	$pconfig['caref'],
678
	$allCas
679
));
680
$section->add($group);
681
682
$section->addInput(new Form_Select(
683 ff5bc49c Viktor Gurov
	'keytype',
684
	'*Key type',
685
	$pconfig['keytype'],
686
	array_combine($ca_keytypes, $ca_keytypes)
687
));
688
689
$group = new Form_Group($i == 0 ? '*Key length':'');
690
$group->addClass('rsakeys');
691
$group->add(new Form_Select(
692 5d2edeca Sjon Hortensius
	'keylen',
693 ff5bc49c Viktor Gurov
	null,
694 5d2edeca Sjon Hortensius
	$pconfig['keylen'],
695 b698621d Stephen Beaver
	array_combine($ca_keylens, $ca_keylens)
696 3f0b7bc3 jim-p
))->setHelp('The length to use when generating a new RSA key, in bits. %1$s' .
697
	'The Key Length should not be lower than 2048 or some platforms ' .
698
	'may consider the certificate invalid.', '<br/>');
699 ff5bc49c Viktor Gurov
$section->add($group);
700
701
$group = new Form_Group($i == 0 ? '*Elliptic Curve Name':'');
702
$group->addClass('ecnames');
703
$group->add(new Form_Select(
704
	'ecname',
705
	null,
706
	$pconfig['ecname'],
707 cffcf9bf jim-p
	$openssl_ecnames
708
))->setHelp('Curves may not be compatible with all uses. Known compatible curve uses are denoted in brackets.');
709 ff5bc49c Viktor Gurov
$section->add($group);
710 5d2edeca Sjon Hortensius
711
$section->addInput(new Form_Select(
712
	'digest_alg',
713 153c3aa6 Phil Davis
	'*Digest Algorithm',
714 5d2edeca Sjon Hortensius
	$pconfig['digest_alg'],
715 b698621d Stephen Beaver
	array_combine($openssl_digest_algs, $openssl_digest_algs)
716 3f0b7bc3 jim-p
))->setHelp('The digest method used when the CA is signed. %1$s' .
717 e1267c0f jim-p
	'The best practice is to use SHA256 or higher. '.
718
	'Some services and platforms, such as the GUI web server and OpenVPN, consider weaker digest algorithms invalid.', '<br/>');
719 5d2edeca Sjon Hortensius
720
$section->addInput(new Form_Input(
721
	'lifetime',
722 153c3aa6 Phil Davis
	'*Lifetime (days)',
723 5d2edeca Sjon Hortensius
	'number',
724 3a877e4a jim-p
	$pconfig['lifetime'],
725
	['max' => $max_lifetime]
726 5d2edeca Sjon Hortensius
));
727
728 26e3967a jim-p
$section->addInput(new Form_Input(
729
	'dn_commonname',
730
	'*Common Name',
731
	'text',
732
	$pconfig['dn_commonname'],
733
	['placeholder' => 'e.g. internal-ca']
734
));
735
736
$section->addInput(new Form_StaticText(
737
	null,
738
	gettext('The following certificate authority subject components are optional and may be left blank.')
739
));
740
741 5d2edeca Sjon Hortensius
$section->addInput(new Form_Select(
742
	'dn_country',
743 80d50253 jim-p
	'Country Code',
744 5d2edeca Sjon Hortensius
	$pconfig['dn_country'],
745 232b1a69 Renato Botelho
	get_cert_country_codes()
746 5d2edeca Sjon Hortensius
));
747
748
$section->addInput(new Form_Input(
749
	'dn_state',
750 80d50253 jim-p
	'State or Province',
751 5d2edeca Sjon Hortensius
	'text',
752
	$pconfig['dn_state'],
753
	['placeholder' => 'e.g. Texas']
754
));
755
756
$section->addInput(new Form_Input(
757
	'dn_city',
758 80d50253 jim-p
	'City',
759 5d2edeca Sjon Hortensius
	'text',
760
	$pconfig['dn_city'],
761
	['placeholder' => 'e.g. Austin']
762
));
763
764
$section->addInput(new Form_Input(
765
	'dn_organization',
766 80d50253 jim-p
	'Organization',
767 5d2edeca Sjon Hortensius
	'text',
768
	$pconfig['dn_organization'],
769 da0f70ed jim-p
	['placeholder' => 'e.g. My Company Inc']
770
));
771
772
$section->addInput(new Form_Input(
773
	'dn_organizationalunit',
774
	'Organizational Unit',
775
	'text',
776
	$pconfig['dn_organizationalunit'],
777
	['placeholder' => 'e.g. My Department Name (optional)']
778 5d2edeca Sjon Hortensius
));
779
780
$form->add($section);
781
782
print $form;
783
784 b8f22f61 Stephen Beaver
$internal_ca_count = 0;
785
foreach ($a_ca as $ca) {
786
	if ($ca['prv']) {
787
		$internal_ca_count++;
788
	}
789
}
790
791 097094bd Phil Davis
?>
792 ff5bc49c Viktor Gurov
<script type="text/javascript">
793
//<![CDATA[
794
events.push(function() {
795
	function change_keytype() {
796
	hideClass('rsakeys', ($('#keytype').val() != 'RSA'));
797
		hideClass('ecnames', ($('#keytype').val() != 'ECDSA'));
798
	}
799
800
	$('#keytype').change(function () {
801
		change_keytype();
802
	});
803
804 3f0b7bc3 jim-p
	function check_keylen() {
805
		var min_keylen = <?= $cert_strict_values['min_private_key_bits'] ?>;
806
		var klid = '#keylen';
807
		/* Color the Parent/Label */
808
		if (parseInt($(klid).val()) < min_keylen) {
809
			$(klid).parent().parent().removeClass("text-normal").addClass("text-warning");
810
		} else {
811
			$(klid).parent().parent().removeClass("text-warning").addClass("text-normal");
812
		}
813
		/* Color individual options */
814
		$(klid + " option").filter(function() {
815
			return parseInt($(this).val()) < min_keylen;
816
		}).removeClass("text-normal").addClass("text-warning").siblings().removeClass("text-warning").addClass("text-normal");
817
	}
818
819
	function check_digest() {
820
		var weak_algs = <?= json_encode($cert_strict_values['digest_blacklist']) ?>;
821
		var daid = '#digest_alg';
822
		/* Color the Parent/Label */
823
		if (jQuery.inArray($(daid).val(), weak_algs) > -1) {
824
			$(daid).parent().parent().removeClass("text-normal").addClass("text-warning");
825
		} else {
826
			$(daid).parent().parent().removeClass("text-warning").addClass("text-normal");
827
		}
828
		/* Color individual options */
829
		$(daid + " option").filter(function() {
830
			return (jQuery.inArray($(this).val(), weak_algs) > -1);
831
		}).removeClass("text-normal").addClass("text-warning").siblings().removeClass("text-warning").addClass("text-normal");
832
	}
833
834
	// ---------- Control change handlers ---------------------------------------------------------
835
836
	$('#method').on('change', function() {
837
		check_keylen();
838
		check_digest();
839
	});
840
841
	$('#keylen').on('change', function() {
842
		check_keylen();
843
	});
844
845
	$('#digest_alg').on('change', function() {
846
		check_digest();
847
	});
848
849 ff5bc49c Viktor Gurov
	// ---------- On initial page load ------------------------------------------------------------
850
	change_keytype();
851 3f0b7bc3 jim-p
	check_keylen();
852
	check_digest();
853 ff5bc49c Viktor Gurov
});
854
//]]>
855
</script>
856
<?php
857
include('foot.inc');