Project

General

Profile

Download (29.2 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></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">
686
	<a href="?act=new" class="btn btn-success">add new</a>
687
</nav>
688
<?
689
	include("foot.inc");
690
	exit;
691
}
692

    
693
require_once('classes/Form.class.php');
694
$form = new Form;
695

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

    
700
	$section = new Form_Section('Complete Signing Request');
701

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

    
712
	$section->addInput(new Form_Input(
713
		'descr',
714
		'Descriptive name',
715
		'text',
716
		$pconfig['descr']
717
	));
718

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

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

    
732
	$form->add($section);
733
	print $form;
734

    
735
	include("foot.inc");
736
	exit;
737
}
738

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

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

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

    
761
$section = new Form_Section('Add a new certificate');
762

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

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

    
780
$form->add($section);
781
$section = new Form_Section('Import Certificate');
782
$section->addClass('toggle-import collapse');
783

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

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

    
796
$form->add($section);
797
$section = new Form_Section('Internal Certificate');
798
$section->addClass('toggle-internal collapse');
799

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

    
817
		$allCas[ $ca['refid'] ] = $ca['descr'];
818
	}
819

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

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

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

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

    
851
$section->addInput(new Form_Input(
852
	'lifetime',
853
	'Lifetime (days)',
854
	'number',
855
	$pconfig['lifetime']
856
));
857

    
858
$section->addInput(new Form_Select(
859
	'dn_country',
860
	'Country Code',
861
	$pconfig['dn_country'],
862
	$dn_cc
863
));
864

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

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

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

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

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

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

    
912
$counter = 0;
913
$numrows = count($pconfig['altnames']['item']) - 1;
914

    
915
foreach ($pconfig['altnames']['item'] as $item) {
916

    
917
	$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
918

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

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

    
938
	$group->add(new Form_Button(
939
		'deleterow' . $counter,
940
		'Delete'
941
	))->removeClass('btn-primary')->addClass('btn-warning');
942

    
943
	$group->addClass('repeatable');
944

    
945
	$section->add($group);
946

    
947
	$counter++;
948
}
949

    
950
$section->addInput(new Form_Button(
951
	'addrow',
952
	'Add'
953
))->removeClass('btn-primary')->addClass('btn-success');
954

    
955
$form->add($section);
956
$section = new Form_Section('External Signing Request');
957
$section->addClass('toggle-external collapse');
958

    
959
$section->addInput(new Form_Select(
960
	'csr_keylen',
961
	'Key length',
962
	$pconfig['csr_keylen'],
963
	$cert_keylens
964
));
965

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

    
974
$section->addInput(new Form_Select(
975
	'dn_country',
976
	'Country Code',
977
	$pconfig['dn_country'],
978
	$dn_cc
979
));
980

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

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

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

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

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

    
1021
$form->add($section);
1022
$section = new Form_Section('Choose an Existing Certificate');
1023
$section->addClass('toggle-existing collapse');
1024

    
1025
$existCerts = array();
1026

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

    
1033
	$ca = lookup_ca($cert['caref']);
1034
	if ($ca)
1035
		$cert['descr'] .= " (CA: {$ca['descr']})";
1036

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

    
1042
	$existCerts[ $cert['refid'] ] = $cert['descr'];
1043
}
1044

    
1045

    
1046
$section->addInput(new Form_Select(
1047
	'certref',
1048
	'Existing Certificates',
1049
	$pconfig['certref'],
1050
	$existCerts
1051
));
1052

    
1053
$form->add($section);
1054
print $form;
1055

    
1056
?>
1057
<script>
1058
//<![CDATA[
1059
events.push(function(){
1060

    
1061
<?php if ($internal_ca_count): ?>
1062
	function internalca_change() {
1063

    
1064
		caref = $('#caref').val();
1065

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

    
1073
				$subject = cert_get_subject_array($ca['crt']);
1074

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

    
1089
	// ---------- Click checkbox handlers ---------------------------------------------------------
1090
	
1091
	$('#caref').on('change', function() {
1092
		internalca_change();
1093
	});
1094

    
1095
	// ---------- On initial page load ------------------------------------------------------------
1096

    
1097
	internalca_change();
1098

    
1099
<?php endif; ?>
1100

    
1101

    
1102
});
1103
//]]>
1104
</script>
1105
<?php
1106
include('foot.inc');
(196-196/235)