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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
148

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
235
if ($_POST) {
236

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
542
			write_config();
543

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

    
549
include("head.inc");
550

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
939
	$counter++;
940
}
941

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

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

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

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

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

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

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

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

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

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

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

    
1017
$existCerts = array();
1018

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

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

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

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

    
1037

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

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

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

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

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

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

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

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

    
1081
	// ---------- Click checkbox handlers ---------------------------------------------------------
1082

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

    
1087
	// ---------- On initial page load ------------------------------------------------------------
1088

    
1089
	internalca_change();
1090

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

    
1094
<?php endif; ?>
1095

    
1096

    
1097
});
1098
//]]>
1099
</script>
1100
<?php
1101
include('foot.inc');
(193-193/228)