Project

General

Profile

Download (29.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	system_certmanager.php
4
*/
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *	Copyright (c)  2004, 2005 Scott Ullrich
8
 *	Copyright (c)  2008 Shrew Soft Inc.
9
 *
10
 *	Redistribution and use in source and binary forms, with or without modification,
11
 *	are permitted provided that the following conditions are met:
12
 *
13
 *	1. Redistributions of source code must retain the above copyright notice,
14
 *		this list of conditions and the following disclaimer.
15
 *
16
 *	2. Redistributions in binary form must reproduce the above copyright
17
 *		notice, this list of conditions and the following disclaimer in
18
 *		the documentation and/or other materials provided with the
19
 *		distribution.
20
 *
21
 *	3. All advertising materials mentioning features or use of this software
22
 *		must display the following acknowledgment:
23
 *		"This product includes software developed by the pfSense Project
24
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
25
 *
26
 *	4. The names "pfSense" and "pfSense Project" must not be used to
27
 *		 endorse or promote products derived from this software without
28
 *		 prior written permission. For written permission, please contact
29
 *		 coreteam@pfsense.org.
30
 *
31
 *	5. Products derived from this software may not be called "pfSense"
32
 *		nor may "pfSense" appear in their names without prior written
33
 *		permission of the Electric Sheep Fencing, LLC.
34
 *
35
 *	6. Redistributions of any form whatsoever must retain the following
36
 *		acknowledgment:
37
 *
38
 *	"This product includes software developed by the pfSense Project
39
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
40
 *
41
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
42
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
44
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
45
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
47
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
48
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
50
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
52
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
53
 *
54
 *	====================================================================
55
 *
56
 */
57
/*
58
	pfSense_MODULE: certificate_manager
59
*/
60

    
61
##|+PRIV
62
##|*IDENT=page-system-certmanager
63
##|*NAME=System: Certificate Manager
64
##|*DESCR=Allow access to the 'System: Certificate Manager' page.
65
##|*MATCH=system_certmanager.php*
66
##|-PRIV
67

    
68
require("guiconfig.inc");
69
require_once("certs.inc");
70

    
71
$cert_methods = array(
72
	"import" => gettext("Import an existing Certificate"),
73
	"internal" => gettext("Create an internal Certificate"),
74
	"external" => gettext("Create a Certificate Signing Request"),
75
);
76

    
77
$cert_keylens = array("512", "1024", "2048", "4096");
78
$cert_types = array(
79
	"ca" => "Certificate Authority",
80
	"server" => "Server Certificate",
81
	"user" => "User Certificate");
82

    
83
$altname_types = array("DNS", "IP", "email", "URI");
84
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
85

    
86
$pgtitle = array(gettext("System"), gettext("Certificate Manager"));
87

    
88
if (is_numericint($_GET['userid'])) {
89
	$userid = $_GET['userid'];
90
}
91
if (isset($_POST['userid']) && is_numericint($_POST['userid'])) {
92
	$userid = $_POST['userid'];
93
}
94

    
95
if (isset($userid)) {
96
	$cert_methods["existing"] = gettext("Choose an existing certificate");
97
	if (!is_array($config['system']['user'])) {
98
		$config['system']['user'] = array();
99
	}
100
	$a_user =& $config['system']['user'];
101
}
102

    
103
if (is_numericint($_GET['id'])) {
104
	$id = $_GET['id'];
105
}
106
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
107
	$id = $_POST['id'];
108
}
109

    
110
if (!is_array($config['ca'])) {
111
	$config['ca'] = array();
112
}
113

    
114
$a_ca =& $config['ca'];
115

    
116
if (!is_array($config['cert'])) {
117
	$config['cert'] = array();
118
}
119

    
120
$a_cert =& $config['cert'];
121

    
122
$internal_ca_count = 0;
123
foreach ($a_ca as $ca) {
124
	if ($ca['prv']) {
125
		$internal_ca_count++;
126
	}
127
}
128

    
129
$act = $_GET['act'];
130

    
131
if ($_POST['act']) {
132
	$act = $_POST['act'];
133
}
134

    
135
if ($act == "del") {
136

    
137
	if (!isset($a_cert[$id])) {
138
		pfSenseHeader("system_certmanager.php");
139
		exit;
140
	}
141

    
142
	unset($a_cert[$id]);
143
	write_config();
144
	$savemsg = sprintf(gettext("Certificate %s successfully deleted"), htmlspecialchars($a_cert[$id]['descr'])) . "<br />";
145
	pfSenseHeader("system_certmanager.php");
146
	exit;
147
}
148

    
149

    
150
if ($act == "new") {
151
	$pconfig['method'] = $_GET['method'];
152
	$pconfig['keylen'] = "2048";
153
	$pconfig['digest_alg'] = "sha256";
154
	$pconfig['csr_keylen'] = "2048";
155
	$pconfig['csr_digest_alg'] = "sha256";
156
	$pconfig['type'] = "user";
157
	$pconfig['lifetime'] = "3650";
158
}
159

    
160
if ($act == "exp") {
161

    
162
	if (!$a_cert[$id]) {
163
		pfSenseHeader("system_certmanager.php");
164
		exit;
165
	}
166

    
167
	$exp_name = urlencode("{$a_cert[$id]['descr']}.crt");
168
	$exp_data = base64_decode($a_cert[$id]['crt']);
169
	$exp_size = strlen($exp_data);
170

    
171
	header("Content-Type: application/octet-stream");
172
	header("Content-Disposition: attachment; filename={$exp_name}");
173
	header("Content-Length: $exp_size");
174
	echo $exp_data;
175
	exit;
176
}
177

    
178
if ($act == "key") {
179

    
180
	if (!$a_cert[$id]) {
181
		pfSenseHeader("system_certmanager.php");
182
		exit;
183
	}
184

    
185
	$exp_name = urlencode("{$a_cert[$id]['descr']}.key");
186
	$exp_data = base64_decode($a_cert[$id]['prv']);
187
	$exp_size = strlen($exp_data);
188

    
189
	header("Content-Type: application/octet-stream");
190
	header("Content-Disposition: attachment; filename={$exp_name}");
191
	header("Content-Length: $exp_size");
192
	echo $exp_data;
193
	exit;
194
}
195

    
196
if ($act == "p12") {
197
	if (!$a_cert[$id]) {
198
		pfSenseHeader("system_certmanager.php");
199
		exit;
200
	}
201

    
202
	$exp_name = urlencode("{$a_cert[$id]['descr']}.p12");
203
	$args = array();
204
	$args['friendly_name'] = $a_cert[$id]['descr'];
205

    
206
	$ca = lookup_ca($a_cert[$id]['caref']);
207
	if ($ca) {
208
		$args['extracerts'] = openssl_x509_read(base64_decode($ca['crt']));
209
	}
210

    
211
	$res_crt = openssl_x509_read(base64_decode($a_cert[$id]['crt']));
212
	$res_key = openssl_pkey_get_private(array(0 => base64_decode($a_cert[$id]['prv']) , 1 => ""));
213

    
214
	$exp_data = "";
215
	openssl_pkcs12_export($res_crt, $exp_data, $res_key, null, $args);
216
	$exp_size = strlen($exp_data);
217

    
218
	header("Content-Type: application/octet-stream");
219
	header("Content-Disposition: attachment; filename={$exp_name}");
220
	header("Content-Length: $exp_size");
221
	echo $exp_data;
222
	exit;
223
}
224

    
225
if ($act == "csr") {
226

    
227
	if (!$a_cert[$id]) {
228
		pfSenseHeader("system_certmanager.php");
229
		exit;
230
	}
231

    
232
	$pconfig['descr'] = $a_cert[$id]['descr'];
233
	$pconfig['csr'] = base64_decode($a_cert[$id]['csr']);
234
}
235

    
236
if ($_POST) {
237

    
238
	// This is just the blank altername name that is added for display purposes. We don't want to validate/save it
239
	if($_POST['altname_value0']  == "") {
240
		unset($_POST['altname_type0']);
241
		unset($_POST['altname_value0']);
242
	}
243

    
244
	if ($_POST['save'] == gettext("Save")) {
245
		$input_errors = array();
246
		$pconfig = $_POST;
247

    
248
		/* input validation */
249
		if ($pconfig['method'] == "import") {
250
			$reqdfields = explode(" ",
251
				"descr cert key");
252
			$reqdfieldsn = array(
253
				gettext("Descriptive name"),
254
				gettext("Certificate data"),
255
				gettext("Key data"));
256
			if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
257
				$input_errors[] = gettext("This certificate does not appear to be valid.");
258
			}
259
		}
260

    
261
		if ($pconfig['method'] == "internal") {
262
			$reqdfields = explode(" ",
263
				"descr caref keylen type lifetime dn_country dn_state dn_city ".
264
				"dn_organization dn_email dn_commonname");
265
			$reqdfieldsn = array(
266
				gettext("Descriptive name"),
267
				gettext("Certificate authority"),
268
				gettext("Key length"),
269
				gettext("Certificate Type"),
270
				gettext("Lifetime"),
271
				gettext("Distinguished name Country Code"),
272
				gettext("Distinguished name State or Province"),
273
				gettext("Distinguished name City"),
274
				gettext("Distinguished name Organization"),
275
				gettext("Distinguished name Email Address"),
276
				gettext("Distinguished name Common Name"));
277
		}
278

    
279
		if ($pconfig['method'] == "external") {
280
			$reqdfields = explode(" ",
281
				"descr csr_keylen csr_dn_country csr_dn_state csr_dn_city ".
282
				"csr_dn_organization csr_dn_email csr_dn_commonname");
283
			$reqdfieldsn = array(
284
				gettext("Descriptive name"),
285
				gettext("Key length"),
286
				gettext("Distinguished name Country Code"),
287
				gettext("Distinguished name State or Province"),
288
				gettext("Distinguished name City"),
289
				gettext("Distinguished name Organization"),
290
				gettext("Distinguished name Email Address"),
291
				gettext("Distinguished name Common Name"));
292
		}
293

    
294
		if ($pconfig['method'] == "existing") {
295
			$reqdfields = array("certref");
296
			$reqdfieldsn = array(gettext("Existing Certificate Choice"));
297
		}
298

    
299
		$altnames = array();
300
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
301
		if ($pconfig['method'] != "import" && $pconfig['method'] != "existing") {
302
			/* subjectAltNames */
303
			foreach ($_POST as $key => $value) {
304
				$entry = '';
305
				if (!substr_compare('altname_type', $key, 0, 12)) {
306
					$entry = substr($key, 12);
307
					$field = 'type';
308
				}
309
				elseif (!substr_compare('altname_value', $key, 0, 13)) {
310
					$entry = substr($key, 13);
311
					$field = 'value';
312
				}
313

    
314
				if (ctype_digit($entry)) {
315
					$entry++;	// Pre-bootstrap code is one-indexed, but the bootstrap code is 0-indexed
316
					$altnames[$entry][$field] = $value;
317
				}
318
			}
319

    
320
			$pconfig['altnames']['item'] = $altnames;
321

    
322
			/* Input validation for subjectAltNames */
323
			foreach ($altnames as $idx => $altname) {
324
				switch ($altname['type']) {
325
					case "DNS":
326
						if (!is_hostname($altname['value'], true)) {
327
							array_push($input_errors, "DNS subjectAltName values must be valid hostnames, FQDNs or wildcard domains.");
328
						}
329
						break;
330
					case "IP":
331
						if (!is_ipaddr($altname['value'])) {
332
							array_push($input_errors, "IP subjectAltName values must be valid IP Addresses");
333
						}
334
						break;
335
					case "email":
336
						if (empty($altname['value'])) {
337
							array_push($input_errors, "You must provide an e-mail address for this type of subjectAltName");
338
						}
339
						if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $altname['value'])) {
340
							array_push($input_errors, "The e-mail provided in a subjectAltName contains invalid characters.");
341
						}
342
						break;
343
					case "URI":
344
						/* Close enough? */
345
						if (!is_URL($altname['value'])) {
346
							$input_errors[] = "URI subjectAltName types must be a valid URI";
347
						}
348
						break;
349
					default:
350
						$input_errors[] = "Unrecognized subjectAltName type.";
351
				}
352
			}
353

    
354
			/* Make sure we do not have invalid characters in the fields for the certificate */
355

    
356
			if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
357
				array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
358
			}
359

    
360
			for ($i = 0; $i < count($reqdfields); $i++) {
361
				if (preg_match('/email/', $reqdfields[$i])) { /* dn_email or csr_dn_name */
362
					if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) {
363
						array_push($input_errors, "The field 'Distinguished name Email Address' contains invalid characters.");
364
					}
365
				} else if (preg_match('/commonname/', $reqdfields[$i])) { /* dn_commonname or csr_dn_commonname */
366
					if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) {
367
						array_push($input_errors, "The field 'Distinguished name Common Name' contains invalid characters.");
368
					}
369
				} else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST[$reqdfields[$i]])) {
370
					array_push($input_errors, "The field '" . $reqdfieldsn[$i] . "' contains invalid characters.");
371
				}
372
			}
373

    
374
			if (($pconfig['method'] != "external") && isset($_POST["keylen"]) && !in_array($_POST["keylen"], $cert_keylens)) {
375
				array_push($input_errors, gettext("Please select a valid Key Length."));
376
			}
377
			if (($pconfig['method'] != "external") && !in_array($_POST["digest_alg"], $openssl_digest_algs)) {
378
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
379
			}
380

    
381
			if (($pconfig['method'] == "external") && isset($_POST["csr_keylen"]) && !in_array($_POST["csr_keylen"], $cert_keylens)) {
382
				array_push($input_errors, gettext("Please select a valid Key Length."));
383
			}
384
			if (($pconfig['method'] == "external") && !in_array($_POST["csr_digest_alg"], $openssl_digest_algs)) {
385
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
386
			}
387
		}
388

    
389
		/* if this is an AJAX caller then handle via JSON */
390
		if (isAjax() && is_array($input_errors)) {
391
			input_errors2Ajax($input_errors);
392
			exit;
393
		}
394

    
395
		/* save modifications */
396
		if (!$input_errors) {
397

    
398
			if ($pconfig['method'] == "existing") {
399
				$cert = lookup_cert($pconfig['certref']);
400
				if ($cert && $a_user) {
401
					$a_user[$userid]['cert'][] = $cert['refid'];
402
				}
403
			} else {
404
				$cert = array();
405
				$cert['refid'] = uniqid();
406
				if (isset($id) && $a_cert[$id]) {
407
					$cert = $a_cert[$id];
408
				}
409

    
410
				$cert['descr'] = $pconfig['descr'];
411

    
412
				$old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */
413

    
414
				if ($pconfig['method'] == "import") {
415
					cert_import($cert, $pconfig['cert'], $pconfig['key']);
416
				}
417

    
418
				if ($pconfig['method'] == "internal") {
419
					$dn = array(
420
						'countryName' => $pconfig['dn_country'],
421
						'stateOrProvinceName' => $pconfig['dn_state'],
422
						'localityName' => $pconfig['dn_city'],
423
						'organizationName' => $pconfig['dn_organization'],
424
						'emailAddress' => $pconfig['dn_email'],
425
						'commonName' => $pconfig['dn_commonname']);
426

    
427
					if (count($altnames)) {
428
						$altnames_tmp = "";
429
						foreach ($altnames as $altname) {
430
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
431
						}
432

    
433
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
434
					}
435

    
436
					if (!cert_create($cert, $pconfig['caref'], $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['type'], $pconfig['digest_alg'])) {
437
						while ($ssl_err = openssl_error_string()) {
438
							$input_errors = array();
439
							array_push($input_errors, "openssl library returns: " . $ssl_err);
440
						}
441
					}
442
				}
443

    
444
				if ($pconfig['method'] == "external") {
445
					$dn = array(
446
						'countryName' => $pconfig['csr_dn_country'],
447
						'stateOrProvinceName' => $pconfig['csr_dn_state'],
448
						'localityName' => $pconfig['csr_dn_city'],
449
						'organizationName' => $pconfig['csr_dn_organization'],
450
						'emailAddress' => $pconfig['csr_dn_email'],
451
						'commonName' => $pconfig['csr_dn_commonname']);
452
					if (count($altnames)) {
453
						$altnames_tmp = "";
454
						foreach ($altnames as $altname) {
455
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
456
						}
457
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
458
					}
459
					if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) {
460
						while ($ssl_err = openssl_error_string()) {
461
							$input_errors = array();
462
							array_push($input_errors, "openssl library returns: " . $ssl_err);
463
						}
464
					}
465
				}
466
				error_reporting($old_err_level);
467

    
468
				if (isset($id) && $a_cert[$id]) {
469
					$a_cert[$id] = $cert;
470
				} else {
471
					$a_cert[] = $cert;
472
				}
473

    
474
				if (isset($a_user) && isset($userid)) {
475
					$a_user[$userid]['cert'][] = $cert['refid'];
476
				}
477
			}
478

    
479
			if (!$input_errors) {
480
				write_config();
481
			}
482

    
483
			if ($userid) {
484
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
485
				exit;
486
			}
487
		}
488
	}
489

    
490
	if ($_POST['save'] == gettext("Update")) {
491
		unset($input_errors);
492
		$pconfig = $_POST;
493

    
494
		/* input validation */
495
		$reqdfields = explode(" ", "descr cert");
496
		$reqdfieldsn = array(
497
		gettext("Descriptive name"),
498
		gettext("Final Certificate data"));
499

    
500
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
501

    
502
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
503
			array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
504
		}
505

    
506
//		old way
507
		/* make sure this csr and certificate subjects match */
508
//		$subj_csr = csr_get_subject($pconfig['csr'], false);
509
//		$subj_cert = cert_get_subject($pconfig['cert'], false);
510
//
511
//		if (!isset($_POST['ignoresubjectmismatch']) && !($_POST['ignoresubjectmismatch'] == "yes")) {
512
//			if (strcmp($subj_csr, $subj_cert)) {
513
//				$input_errors[] = sprintf(gettext("The certificate subject '%s' does not match the signing request subject."), $subj_cert);
514
//				$subject_mismatch = true;
515
//			}
516
//		}
517
		$mod_csr = csr_get_modulus($pconfig['csr'], false);
518
		$mod_cert = cert_get_modulus($pconfig['cert'], false);
519

    
520
		if (strcmp($mod_csr, $mod_cert)) {
521
			// simply: if the moduli don't match, then the private key and public key won't match
522
			$input_errors[] = sprintf(gettext("The certificate modulus does not match the signing request modulus."), $subj_cert);
523
			$subject_mismatch = true;
524
		}
525

    
526
		/* if this is an AJAX caller then handle via JSON */
527
		if (isAjax() && is_array($input_errors)) {
528
			input_errors2Ajax($input_errors);
529
			exit;
530
		}
531

    
532
		/* save modifications */
533
		if (!$input_errors) {
534

    
535
			$cert = $a_cert[$id];
536

    
537
			$cert['descr'] = $pconfig['descr'];
538

    
539
			csr_complete($cert, $pconfig['cert']);
540

    
541
			$a_cert[$id] = $cert;
542

    
543
			write_config();
544

    
545
			pfSenseHeader("system_certmanager.php");
546
		}
547
	}
548
}
549

    
550
include("head.inc");
551

    
552
if ($input_errors)
553
	print_input_errors($input_errors);
554

    
555
if ($savemsg)
556
	print_info_box($savemsg, 'success');
557

    
558
$tab_array = array();
559
$tab_array[] = array(gettext("CAs"), false, "system_camanager.php");
560
$tab_array[] = array(gettext("Certificates"), true, "system_certmanager.php");
561
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
562
display_top_tabs($tab_array);
563

    
564
// Load valid country codes
565
$dn_cc = array();
566
if (file_exists("/etc/ca_countries")){
567
	$dn_cc_file=file("/etc/ca_countries");
568
	foreach($dn_cc_file as $line) {
569
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
570
			$dn_cc[$matches[1]] = $matches[1];
571
		}
572
	}
573
}
574

    
575
if (!($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)))
576
{
577
?>
578
<div class="table-responsive">
579
<table class="table table-striped table-hover">
580
	<thead>
581
		<tr>
582
			<th><?=gettext("Name")?></th>
583
			<th><?=gettext("Issuer")?></th>
584
			<th><?=gettext("Distinguished Name")?></th>
585
			<th><?=gettext("In Use")?></th>
586
			<th class="col-sm-2"><?=gettext("Actions")?></th>
587
		</tr>
588
	</thead>
589
	<tbody>
590
<?php
591
foreach($a_cert as $i => $cert):
592
	$name = htmlspecialchars($cert['descr']);
593

    
594
	if ($cert['crt']) {
595
		$subj = cert_get_subject($cert['crt']);
596
		$issuer = cert_get_issuer($cert['crt']);
597
		$purpose = cert_get_purpose($cert['crt']);
598
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
599

    
600
		if ($subj==$issuer)
601
			$caname = '<i>'. gettext("self-signed") .'</i>';
602
		else
603
			$caname = '<i>'. gettext("external").'</i>';
604

    
605
		$subj = htmlspecialchars($subj);
606
	}
607

    
608
	if ($cert['csr']) {
609
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
610
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
611
	}
612

    
613
	$ca = lookup_ca($cert['caref']);
614
	if ($ca)
615
		$caname = $ca['descr'];
616
?>
617
		<tr>
618
			<td>
619
				<?=$name?><br />
620
				<?php if ($cert['type']): ?>
621
					<i><?=$cert_types[$cert['type']]?></i><br />
622
				<?php endif?>
623
				<?php if (is_array($purpose)): ?>
624
					CA: <b><?=$purpose['ca']?></b>, Server: <b><?=$purpose['server']?></b>
625
				<?php endif?>
626
			</td>
627
			<td><?=$caname?></td>
628
			<td>
629
				<?=$subj?>
630
				<br />
631
				<small>
632
					<?=gettext("Valid From")?>: <b><?=$startdate ?></b><br /><?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
633
				</small>
634
			</td>
635
			<td>
636
				<?php if (is_cert_revoked($cert)): ?>
637
					<i>Revoked </i>
638
				<?php endif?>
639
				<?php if (is_webgui_cert($cert['refid'])): ?>
640
					webConfigurator
641
				<?php endif?>
642
				<?php if (is_user_cert($cert['refid'])): ?>
643
					User Cert
644
				<?php endif?>
645
				<?php if (is_openvpn_server_cert($cert['refid'])): ?>
646
					OpenVPN Server
647
				<?php endif?>
648
				<?php if (is_openvpn_client_cert($cert['refid'])): ?>
649
					OpenVPN Client
650
				<?php endif?>
651
				<?php if (is_ipsec_cert($cert['refid'])): ?>
652
					IPsec Tunnel
653
				<?php endif?>
654
				<?php if (is_captiveportal_cert($cert['refid'])): ?>
655
					Captive Portal
656
				<?php endif?>
657
			</td>
658
			<td>
659
				<a href="system_certmanager.php?act=exp&amp;id=<?=$i?>" class="fa fa-sign-in" title="<?=gettext("Export")?>"></a>
660
				<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export key")?>"></a>
661
				<a href="system_certmanager.php?act=p12&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export P12")?>"> P12</a>
662
				<?php if (!cert_in_use($cert['refid'])): ?>
663
					<a href="system_certmanager.php?act=del&amp;id=<?=$i?>" class="fa fa-trash" title="<?=gettext("Delete")?>"></a>
664
				<?php endif?>
665
				<?php if ($cert['csr']): ?>
666
					<a href="system_certmanager.php?act=csr&amp;id=<?=$i?>" class="fa fa-refresh" title="<?=gettext("Update csr")?>"></a>
667
				<?php endif?>
668
			</td>
669
		</tr>
670
<?php endforeach; ?>
671
	</tbody>
672
</table>
673
</div>
674

    
675
<nav class="action-buttons">
676
	<a href="?act=new" class="btn btn-success btn-sm">
677
		<i class="fa fa-plus icon-embed-btn"></i>
678
		<?=gettext("Add")?>
679
	</a>
680
</nav>
681
<?
682
	include("foot.inc");
683
	exit;
684
}
685

    
686
require_once('classes/Form.class.php');
687
$form = new Form;
688

    
689
if ($act == "csr" || (($_POST['save'] == gettext("Update")) && $input_errors))
690
{
691
	$form->setAction('system_certmanager.php?act=csr');
692

    
693
	$section = new Form_Section('Complete Signing Request');
694

    
695
	if (isset($id) && $a_cert[$id])
696
	{
697
		$form->addGlobal(new Form_Input(
698
			'id',
699
			null,
700
			'hidden',
701
			$id
702
		));
703
	}
704

    
705
	$section->addInput(new Form_Input(
706
		'descr',
707
		'Descriptive name',
708
		'text',
709
		$pconfig['descr']
710
	));
711

    
712
	$section->addInput(new Form_Textarea(
713
		'csr',
714
		'Signing request data',
715
		$pconfig['csr']
716
	))->setReadonly()->setHelp('Copy the certificate signing data from here and '.
717
		'forward it to your certificate authority for signing.');
718

    
719
	$section->addInput(new Form_Textarea(
720
		'cert',
721
		'Final certificate data',
722
		$pconfig["cert"]
723
	))->setHelp('Paste the certificate received from your certificate authority here.');
724

    
725
	$form->add($section);
726
	print $form;
727

    
728
	include("foot.inc");
729
	exit;
730
}
731

    
732
$form->setAction('system_certmanager.php?act=edit');
733

    
734
if (isset($userid) && $a_user)
735
{
736
	$form->addGlobal(new Form_Input(
737
		'userid',
738
		null,
739
		'hidden',
740
		$userid
741
	));
742
}
743

    
744
if (isset($id) && $a_cert[$id])
745
{
746
	$form->addGlobal(new Form_Input(
747
		'id',
748
		null,
749
		'hidden',
750
		$id
751
	));
752
}
753

    
754
$section = new Form_Section('Add a new certificate');
755

    
756
if (!isset($id))
757
{
758
	$section->addInput(new Form_Select(
759
		'method',
760
		'Method',
761
		$pconfig['method'],
762
		$cert_methods
763
	))->toggles();
764
}
765

    
766
$section->addInput(new Form_Input(
767
	'descr',
768
	'Descriptive name',
769
	'text',
770
	($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
771
))->addClass('toggle-existing');
772

    
773
$form->add($section);
774
$section = new Form_Section('Import Certificate');
775
$section->addClass('toggle-import collapse');
776

    
777
$section->addInput(new Form_Textarea(
778
	'cert',
779
	'Certificate data',
780
	$pconfig['cert']
781
))->setHelp('Paste a certificate in X.509 PEM format here.');
782

    
783
$section->addInput(new Form_Textarea(
784
	'key',
785
	'Private key data',
786
	$pconfig['key']
787
))->setHelp('Paste a private key in X.509 PEM format here.');
788

    
789
$form->add($section);
790
$section = new Form_Section('Internal Certificate');
791
$section->addClass('toggle-internal collapse');
792

    
793
if (!$internal_ca_count)
794
{
795
	$section->addInput(new Form_StaticText(
796
		'Certificate authority',
797
		gettext('No internal Certificate Authorities have been defined. You must ').
798
		'<a href="system_camanager.php?act=new&amp;method=internal"> '. gettext(" create") .'</a>'.
799
		gettext(' an internal CA before creating an internal certificate.')
800
	));
801
}
802
else
803
{
804
	$allCas = array();
805
	foreach ($a_ca as $ca)
806
	{
807
		if (!$ca['prv'])
808
				continue;
809

    
810
		$allCas[ $ca['refid'] ] = $ca['descr'];
811
	}
812

    
813
	$section->addInput(new Form_Select(
814
		'caref',
815
		'Certificate authority',
816
		$pconfig['caref'],
817
		$allCas
818
	));
819
}
820

    
821
$section->addInput(new Form_Select(
822
	'keylen',
823
	'Key length',
824
	$pconfig['keylen'],
825
	array_combine($cert_keylens, $cert_keylens)
826
));
827

    
828
$section->addInput(new Form_Select(
829
	'digest_alg',
830
	'Digest Algorithm',
831
	$pconfig['digest_alg'],
832
	array_combine($openssl_digest_algs, $openssl_digest_algs)
833
))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
834
	'SHA1 when possible.');
835

    
836
$section->addInput(new Form_Select(
837
	'type',
838
	'Certificate Type',
839
	$pconfig['type'],
840
	$cert_types
841
))->setHelp('Type of certificate to generate. Used for placing '.
842
	'restrictions on the usage of the generated certificate.');
843

    
844
$section->addInput(new Form_Input(
845
	'lifetime',
846
	'Lifetime (days)',
847
	'number',
848
	$pconfig['lifetime']
849
));
850

    
851
$section->addInput(new Form_Select(
852
	'dn_country',
853
	'Country Code',
854
	$pconfig['dn_country'],
855
	$dn_cc
856
));
857

    
858
$section->addInput(new Form_Input(
859
	'dn_state',
860
	'State or Province',
861
	'text',
862
	$pconfig['dn_state'],
863
	['placeholder' => 'e.g. Texas']
864
));
865

    
866
$section->addInput(new Form_Input(
867
	'dn_city',
868
	'City',
869
	'text',
870
	$pconfig['dn_city'],
871
	['placeholder' => 'e.g. Austin']
872
));
873

    
874
$section->addInput(new Form_Input(
875
	'dn_organization',
876
	'Organization',
877
	'text',
878
	$pconfig['dn_organization'],
879
	['placeholder' => 'e.g. My Company Inc.']
880
));
881

    
882
$section->addInput(new Form_Input(
883
	'dn_email',
884
	'Email Address',
885
	'email',
886
	$pconfig['dn_email'],
887
	['placeholder' => 'e.g. admin@mycompany.com']
888
));
889

    
890
$section->addInput(new Form_Input(
891
	'dn_commonname',
892
	'Common Name',
893
	'text',
894
	$pconfig['dn_commonname'],
895
	['placeholder' => 'e.g. www.example.com']
896
));
897

    
898
if (empty($pconfig['altnames']['item']))
899
{
900
	$pconfig['altnames']['item'] = array(
901
		array('type' => null, 'value' => null)
902
	);
903
}
904

    
905
$counter = 0;
906
$numrows = count($pconfig['altnames']['item']) - 1;
907

    
908
foreach ($pconfig['altnames']['item'] as $item) {
909

    
910
	$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
911

    
912
	$group->add(new Form_Select(
913
		'altname_type' . $counter,
914
		'Type',
915
		$item['type'],
916
		array(
917
			'DNS' => 'FQDN or Hostname',
918
			'IP' => 'IP address',
919
			'URI' => 'URI',
920
			'email' => 'email address',
921
		)
922
	))->setHelp(($counter == $numrows) ? 'Type':null);
923

    
924
	$group->add(new Form_Input(
925
		'altname_value' . $counter,
926
		null,
927
		'text',
928
		$item['value']
929
	))->setHelp(($counter == $numrows) ? 'Value':null);
930

    
931
	$group->add(new Form_Button(
932
		'deleterow' . $counter,
933
		'Delete'
934
	))->removeClass('btn-primary')->addClass('btn-warning');
935

    
936
	$group->addClass('repeatable');
937

    
938
	$section->add($group);
939

    
940
	$counter++;
941
}
942

    
943
$section->addInput(new Form_Button(
944
	'addrow',
945
	'Add'
946
))->removeClass('btn-primary')->addClass('btn-success');
947

    
948
$form->add($section);
949
$section = new Form_Section('External Signing Request');
950
$section->addClass('toggle-external collapse');
951

    
952
$section->addInput(new Form_Select(
953
	'csr_keylen',
954
	'Key length',
955
	$pconfig['csr_keylen'],
956
	$cert_keylens
957
));
958

    
959
$section->addInput(new Form_Select(
960
	'csr_digest_alg',
961
	'Digest Algorithm',
962
	$pconfig['csr_digest_alg'],
963
	$openssl_digest_algs
964
))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
965
	'SHA1 when possible');
966

    
967
$section->addInput(new Form_Select(
968
	'dn_country',
969
	'Country Code',
970
	$pconfig['dn_country'],
971
	$dn_cc
972
));
973

    
974
$section->addInput(new Form_Input(
975
	'csr_dn_state',
976
	'State or Province',
977
	'text',
978
	$pconfig['csr_dn_state'],
979
	['placeholder' => 'e.g. Texas']
980
));
981

    
982
$section->addInput(new Form_Input(
983
	'csr_dn_city',
984
	'City',
985
	'text',
986
	$pconfig['csr_dn_city'],
987
	['placeholder' => 'e.g. Austin']
988
));
989

    
990
$section->addInput(new Form_Input(
991
	'csr_dn_organization',
992
	'Organization',
993
	'text',
994
	$pconfig['csr_dn_organization'],
995
	['placeholder' => 'e.g. My Company Inc.']
996
));
997

    
998
$section->addInput(new Form_Input(
999
	'csr_dn_email',
1000
	'Email Address',
1001
	'email',
1002
	$pconfig['csr_dn_email'],
1003
	['placeholder' => 'e.g. admin@mycompany.com']
1004
));
1005

    
1006
$section->addInput(new Form_Input(
1007
	'csr_dn_commonname',
1008
	'Common Name',
1009
	'text',
1010
	$pconfig['csr_dn_commonname'],
1011
	['placeholder' => 'e.g. internal-ca']
1012
));
1013

    
1014
$form->add($section);
1015
$section = new Form_Section('Choose an Existing Certificate');
1016
$section->addClass('toggle-existing collapse');
1017

    
1018
$existCerts = array();
1019

    
1020
foreach ($config['cert'] as $cert)	{
1021
	if(is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
1022
		if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert']))
1023
			continue;
1024
	}
1025

    
1026
	$ca = lookup_ca($cert['caref']);
1027
	if ($ca)
1028
		$cert['descr'] .= " (CA: {$ca['descr']})";
1029

    
1030
	if (cert_in_use($cert['refid']))
1031
		$cert['descr'] .= " <i>In Use</i>";
1032
	if (is_cert_revoked($cert))
1033
		$cert['descr'] .= " <b>Revoked</b>";
1034

    
1035
	$existCerts[ $cert['refid'] ] = $cert['descr'];
1036
}
1037

    
1038

    
1039
$section->addInput(new Form_Select(
1040
	'certref',
1041
	'Existing Certificates',
1042
	$pconfig['certref'],
1043
	$existCerts
1044
));
1045

    
1046
$form->add($section);
1047
print $form;
1048

    
1049
?>
1050
<script>
1051
//<![CDATA[
1052
events.push(function(){
1053

    
1054
<?php if ($internal_ca_count): ?>
1055
	function internalca_change() {
1056

    
1057
		caref = $('#caref').val();
1058

    
1059
		switch (caref) {
1060
<?php
1061
			foreach ($a_ca as $ca):
1062
				if (!$ca['prv']) {
1063
					continue;
1064
				}
1065

    
1066
				$subject = cert_get_subject_array($ca['crt']);
1067

    
1068
?>
1069
				case "<?=$ca['refid'];?>":
1070
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1071
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1072
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1073
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1074
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1075
					break;
1076
<?php
1077
			endforeach;
1078
?>
1079
		}
1080
	}
1081

    
1082
	// ---------- Click checkbox handlers ---------------------------------------------------------
1083

    
1084
	$('#caref').on('change', function() {
1085
		internalca_change();
1086
	});
1087

    
1088
	// ---------- On initial page load ------------------------------------------------------------
1089

    
1090
	internalca_change();
1091

    
1092
	// Suppress "Delete row" button if there are fewer than two rows
1093
	checkLastRow();
1094

    
1095
<?php endif; ?>
1096

    
1097

    
1098
});
1099
//]]>
1100
</script>
1101
<?php
1102
include('foot.inc');
(195-195/234)