Project

General

Profile

Download (29.4 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><?=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="btn btn-xs btn-default">
660
					<?=gettext("export")?>
661
				</a>
662
				<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="btn btn-xs btn-default">
663
					<?=gettext("export key")?>
664
				</a>
665
				<a href="system_certmanager.php?act=p12&amp;id=<?=$i?>" class="btn btn-xs btn-default">
666
					<?=gettext("export p12")?>
667
				</a>
668
				<?php if (!cert_in_use($cert['refid'])): ?>
669
					<a href="system_certmanager.php?act=del&amp;id=<?=$i?>" class="btn btn-xs btn-danger">
670
						<?=gettext("delete")?>
671
					</a>
672
				<?php endif?>
673
				<?php if ($cert['csr']): ?>
674
					<a href="system_certmanager.php?act=csr&amp;id=<?=$i?>" class="btn btn-xs btn-default">
675
						<?=gettext("update csr")?>
676
					</a>
677
				<?php endif?>
678
			</td>
679
		</tr>
680
<?php endforeach; ?>
681
	</tbody>
682
</table>
683
</div>
684

    
685
<nav class="action-buttons" style="margin-top: 10px;">
686
	<a href="?act=new" class="btn btn-success btn-sm">
687
		<i class="fa fa-plus" style="font-size:15px; vertical-align: middle; margin-right: 6px;"></i>
688
		<?=gettext("Add")?>
689
	</a>
690
</nav>
691
<?
692
	include("foot.inc");
693
	exit;
694
}
695

    
696
require_once('classes/Form.class.php');
697
$form = new Form;
698

    
699
if ($act == "csr" || (($_POST['save'] == gettext("Update")) && $input_errors))
700
{
701
	$form->setAction('system_certmanager.php?act=csr');
702

    
703
	$section = new Form_Section('Complete Signing Request');
704

    
705
	if (isset($id) && $a_cert[$id])
706
	{
707
		$form->addGlobal(new Form_Input(
708
			'id',
709
			null,
710
			'hidden',
711
			$id
712
		));
713
	}
714

    
715
	$section->addInput(new Form_Input(
716
		'descr',
717
		'Descriptive name',
718
		'text',
719
		$pconfig['descr']
720
	));
721

    
722
	$section->addInput(new Form_Textarea(
723
		'csr',
724
		'Signing request data',
725
		$pconfig['csr']
726
	))->setReadonly()->setHelp('Copy the certificate signing data from here and '.
727
		'forward it to your certificate authority for signing.');
728

    
729
	$section->addInput(new Form_Textarea(
730
		'cert',
731
		'Final certificate data',
732
		$pconfig["cert"]
733
	))->setHelp('Paste the certificate received from your certificate authority here.');
734

    
735
	$form->add($section);
736
	print $form;
737

    
738
	include("foot.inc");
739
	exit;
740
}
741

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

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

    
754
if (isset($id) && $a_cert[$id])
755
{
756
	$form->addGlobal(new Form_Input(
757
		'id',
758
		null,
759
		'hidden',
760
		$id
761
	));
762
}
763

    
764
$section = new Form_Section('Add a new certificate');
765

    
766
if (!isset($id))
767
{
768
	$section->addInput(new Form_Select(
769
		'method',
770
		'Method',
771
		$pconfig['method'],
772
		$cert_methods
773
	))->toggles();
774
}
775

    
776
$section->addInput(new Form_Input(
777
	'descr',
778
	'Descriptive name',
779
	'text',
780
	($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
781
))->addClass('toggle-existing');
782

    
783
$form->add($section);
784
$section = new Form_Section('Import Certificate');
785
$section->addClass('toggle-import collapse');
786

    
787
$section->addInput(new Form_Textarea(
788
	'cert',
789
	'Certificate data',
790
	$pconfig['cert']
791
))->setHelp('Paste a certificate in X.509 PEM format here.');
792

    
793
$section->addInput(new Form_Textarea(
794
	'key',
795
	'Private key data',
796
	$pconfig['key']
797
))->setHelp('Paste a private key in X.509 PEM format here.');
798

    
799
$form->add($section);
800
$section = new Form_Section('Internal Certificate');
801
$section->addClass('toggle-internal collapse');
802

    
803
if (!$internal_ca_count)
804
{
805
	$section->addInput(new Form_StaticText(
806
		'Certificate authority',
807
		gettext('No internal Certificate Authorities have been defined. You must ').
808
		'<a href="system_camanager.php?act=new&amp;method=internal"> '. gettext(" create") .'</a>'.
809
		gettext(' an internal CA before creating an internal certificate.')
810
	));
811
}
812
else
813
{
814
	$allCas = array();
815
	foreach ($a_ca as $ca)
816
	{
817
		if (!$ca['prv'])
818
				continue;
819

    
820
		$allCas[ $ca['refid'] ] = $ca['descr'];
821
	}
822

    
823
	$section->addInput(new Form_Select(
824
		'caref',
825
		'Certificate authority',
826
		$pconfig['caref'],
827
		$allCas
828
	));
829
}
830

    
831
$section->addInput(new Form_Select(
832
	'keylen',
833
	'Key length',
834
	$pconfig['keylen'],
835
	array_combine($cert_keylens, $cert_keylens)
836
));
837

    
838
$section->addInput(new Form_Select(
839
	'digest_alg',
840
	'Digest Algorithm',
841
	$pconfig['digest_alg'],
842
	array_combine($openssl_digest_algs, $openssl_digest_algs)
843
))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
844
	'SHA1 when possible.');
845

    
846
$section->addInput(new Form_Select(
847
	'type',
848
	'Certificate Type',
849
	$pconfig['type'],
850
	$cert_types
851
))->setHelp('Type of certificate to generate. Used for placing '.
852
	'restrictions on the usage of the generated certificate.');
853

    
854
$section->addInput(new Form_Input(
855
	'lifetime',
856
	'Lifetime (days)',
857
	'number',
858
	$pconfig['lifetime']
859
));
860

    
861
$section->addInput(new Form_Select(
862
	'dn_country',
863
	'Country Code',
864
	$pconfig['dn_country'],
865
	$dn_cc
866
));
867

    
868
$section->addInput(new Form_Input(
869
	'dn_state',
870
	'State or Province',
871
	'text',
872
	$pconfig['dn_state'],
873
	['placeholder' => 'e.g. Texas']
874
));
875

    
876
$section->addInput(new Form_Input(
877
	'dn_city',
878
	'City',
879
	'text',
880
	$pconfig['dn_city'],
881
	['placeholder' => 'e.g. Austin']
882
));
883

    
884
$section->addInput(new Form_Input(
885
	'dn_organization',
886
	'Organization',
887
	'text',
888
	$pconfig['dn_organization'],
889
	['placeholder' => 'e.g. My Company Inc.']
890
));
891

    
892
$section->addInput(new Form_Input(
893
	'dn_email',
894
	'Email Address',
895
	'email',
896
	$pconfig['dn_email'],
897
	['placeholder' => 'e.g. admin@mycompany.com']
898
));
899

    
900
$section->addInput(new Form_Input(
901
	'dn_commonname',
902
	'Common Name',
903
	'text',
904
	$pconfig['dn_commonname'],
905
	['placeholder' => 'e.g. www.example.com']
906
));
907

    
908
if (empty($pconfig['altnames']['item']))
909
{
910
	$pconfig['altnames']['item'] = array(
911
		array('type' => null, 'value' => null)
912
	);
913
}
914

    
915
$counter = 0;
916
$numrows = count($pconfig['altnames']['item']) - 1;
917

    
918
foreach ($pconfig['altnames']['item'] as $item) {
919

    
920
	$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
921

    
922
	$group->add(new Form_Select(
923
		'altname_type' . $counter,
924
		'Type',
925
		$item['type'],
926
		array(
927
			'DNS' => 'FQDN or Hostname',
928
			'IP' => 'IP address',
929
			'URI' => 'URI',
930
			'email' => 'email address',
931
		)
932
	))->setHelp(($counter == $numrows) ? 'Type':null);
933

    
934
	$group->add(new Form_Input(
935
		'altname_value' . $counter,
936
		null,
937
		'text',
938
		$item['value']
939
	))->setHelp(($counter == $numrows) ? 'Value':null);
940

    
941
	$group->add(new Form_Button(
942
		'deleterow' . $counter,
943
		'Delete'
944
	))->removeClass('btn-primary')->addClass('btn-warning');
945

    
946
	$group->addClass('repeatable');
947

    
948
	$section->add($group);
949

    
950
	$counter++;
951
}
952

    
953
$section->addInput(new Form_Button(
954
	'addrow',
955
	'Add'
956
))->removeClass('btn-primary')->addClass('btn-success');
957

    
958
$form->add($section);
959
$section = new Form_Section('External Signing Request');
960
$section->addClass('toggle-external collapse');
961

    
962
$section->addInput(new Form_Select(
963
	'csr_keylen',
964
	'Key length',
965
	$pconfig['csr_keylen'],
966
	$cert_keylens
967
));
968

    
969
$section->addInput(new Form_Select(
970
	'csr_digest_alg',
971
	'Digest Algorithm',
972
	$pconfig['csr_digest_alg'],
973
	$openssl_digest_algs
974
))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
975
	'SHA1 when possible');
976

    
977
$section->addInput(new Form_Select(
978
	'dn_country',
979
	'Country Code',
980
	$pconfig['dn_country'],
981
	$dn_cc
982
));
983

    
984
$section->addInput(new Form_Input(
985
	'csr_dn_state',
986
	'State or Province',
987
	'text',
988
	$pconfig['csr_dn_state'],
989
	['placeholder' => 'e.g. Texas']
990
));
991

    
992
$section->addInput(new Form_Input(
993
	'csr_dn_city',
994
	'City',
995
	'text',
996
	$pconfig['csr_dn_city'],
997
	['placeholder' => 'e.g. Austin']
998
));
999

    
1000
$section->addInput(new Form_Input(
1001
	'csr_dn_organization',
1002
	'Organization',
1003
	'text',
1004
	$pconfig['csr_dn_organization'],
1005
	['placeholder' => 'e.g. My Company Inc.']
1006
));
1007

    
1008
$section->addInput(new Form_Input(
1009
	'csr_dn_email',
1010
	'Email Address',
1011
	'email',
1012
	$pconfig['csr_dn_email'],
1013
	['placeholder' => 'e.g. admin@mycompany.com']
1014
));
1015

    
1016
$section->addInput(new Form_Input(
1017
	'csr_dn_commonname',
1018
	'Common Name',
1019
	'text',
1020
	$pconfig['csr_dn_commonname'],
1021
	['placeholder' => 'e.g. internal-ca']
1022
));
1023

    
1024
$form->add($section);
1025
$section = new Form_Section('Choose an Existing Certificate');
1026
$section->addClass('toggle-existing collapse');
1027

    
1028
$existCerts = array();
1029

    
1030
foreach ($config['cert'] as $cert)	{
1031
	if(is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
1032
		if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert']))
1033
			continue;
1034
	}
1035

    
1036
	$ca = lookup_ca($cert['caref']);
1037
	if ($ca)
1038
		$cert['descr'] .= " (CA: {$ca['descr']})";
1039

    
1040
	if (cert_in_use($cert['refid']))
1041
		$cert['descr'] .= " <i>In Use</i>";
1042
	if (is_cert_revoked($cert))
1043
		$cert['descr'] .= " <b>Revoked</b>";
1044

    
1045
	$existCerts[ $cert['refid'] ] = $cert['descr'];
1046
}
1047

    
1048

    
1049
$section->addInput(new Form_Select(
1050
	'certref',
1051
	'Existing Certificates',
1052
	$pconfig['certref'],
1053
	$existCerts
1054
));
1055

    
1056
$form->add($section);
1057
print $form;
1058

    
1059
?>
1060
<script>
1061
//<![CDATA[
1062
events.push(function(){
1063

    
1064
<?php if ($internal_ca_count): ?>
1065
	function internalca_change() {
1066

    
1067
		caref = $('#caref').val();
1068

    
1069
		switch (caref) {
1070
<?php
1071
			foreach ($a_ca as $ca):
1072
				if (!$ca['prv']) {
1073
					continue;
1074
				}
1075

    
1076
				$subject = cert_get_subject_array($ca['crt']);
1077

    
1078
?>
1079
				case "<?=$ca['refid'];?>":
1080
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1081
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1082
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1083
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1084
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1085
					break;
1086
<?php
1087
			endforeach;
1088
?>
1089
		}
1090
	}
1091

    
1092
	// ---------- Click checkbox handlers ---------------------------------------------------------
1093

    
1094
	$('#caref').on('change', function() {
1095
		internalca_change();
1096
	});
1097

    
1098
	// ---------- On initial page load ------------------------------------------------------------
1099

    
1100
	internalca_change();
1101

    
1102
	// Suppress "Delete row" button if there are fewer than two rows
1103
	checkLastRow();
1104

    
1105
<?php endif; ?>
1106

    
1107

    
1108
});
1109
//]]>
1110
</script>
1111
<?php
1112
include('foot.inc');
(195-195/234)