Project

General

Profile

Download (32.6 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * system_certmanager.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
7
 * Copyright (c) 2008 Shrew Soft Inc
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, 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
##|+PRIV
56
##|*IDENT=page-system-certmanager
57
##|*NAME=System: Certificate Manager
58
##|*DESCR=Allow access to the 'System: Certificate Manager' page.
59
##|*MATCH=system_certmanager.php*
60
##|-PRIV
61

    
62
require_once("guiconfig.inc");
63
require_once("certs.inc");
64

    
65
$cert_methods = array(
66
	"import" => gettext("Import an existing Certificate"),
67
	"internal" => gettext("Create an internal Certificate"),
68
	"external" => gettext("Create a Certificate Signing Request"),
69
);
70

    
71
$cert_keylens = array("512", "1024", "2048", "3072", "4096", "7680", "8192", "15360", "16384");
72
$cert_types = array(
73
	"server" => "Server Certificate",
74
	"user" => "User Certificate");
75

    
76
$altname_types = array("DNS", "IP", "email", "URI");
77
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512", "whirlpool");
78

    
79
if (is_numericint($_GET['userid'])) {
80
	$userid = $_GET['userid'];
81
}
82
if (isset($_POST['userid']) && is_numericint($_POST['userid'])) {
83
	$userid = $_POST['userid'];
84
}
85

    
86
if (isset($userid)) {
87
	$cert_methods["existing"] = gettext("Choose an existing certificate");
88
	if (!is_array($config['system']['user'])) {
89
		$config['system']['user'] = array();
90
	}
91
	$a_user =& $config['system']['user'];
92
}
93

    
94
if (is_numericint($_GET['id'])) {
95
	$id = $_GET['id'];
96
}
97
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
98
	$id = $_POST['id'];
99
}
100

    
101
if (!is_array($config['ca'])) {
102
	$config['ca'] = array();
103
}
104

    
105
$a_ca =& $config['ca'];
106

    
107
if (!is_array($config['cert'])) {
108
	$config['cert'] = array();
109
}
110

    
111
$a_cert =& $config['cert'];
112

    
113
$internal_ca_count = 0;
114
foreach ($a_ca as $ca) {
115
	if ($ca['prv']) {
116
		$internal_ca_count++;
117
	}
118
}
119

    
120
$act = $_GET['act'];
121

    
122
if ($_POST['act']) {
123
	$act = $_POST['act'];
124
}
125

    
126
if ($act == "del") {
127

    
128
	if (!isset($a_cert[$id])) {
129
		pfSenseHeader("system_certmanager.php");
130
		exit;
131
	}
132

    
133
	unset($a_cert[$id]);
134
	write_config();
135
	$savemsg = sprintf(gettext("Certificate %s successfully deleted."), htmlspecialchars($a_cert[$id]['descr']));
136
	pfSenseHeader("system_certmanager.php");
137
	exit;
138
}
139

    
140

    
141
if ($act == "new") {
142
	$pconfig['method'] = $_GET['method'];
143
	$pconfig['keylen'] = "2048";
144
	$pconfig['digest_alg'] = "sha256";
145
	$pconfig['csr_keylen'] = "2048";
146
	$pconfig['csr_digest_alg'] = "sha256";
147
	$pconfig['type'] = "user";
148
	$pconfig['lifetime'] = "3650";
149
}
150

    
151
if ($act == "exp") {
152

    
153
	if (!$a_cert[$id]) {
154
		pfSenseHeader("system_certmanager.php");
155
		exit;
156
	}
157

    
158
	$exp_name = urlencode("{$a_cert[$id]['descr']}.crt");
159
	$exp_data = base64_decode($a_cert[$id]['crt']);
160
	$exp_size = strlen($exp_data);
161

    
162
	header("Content-Type: application/octet-stream");
163
	header("Content-Disposition: attachment; filename={$exp_name}");
164
	header("Content-Length: $exp_size");
165
	echo $exp_data;
166
	exit;
167
}
168

    
169
if ($act == "req") {
170

    
171
	if (!$a_cert[$id]) {
172
		pfSenseHeader("system_certmanager.php");
173
		exit;
174
	}
175

    
176
	$exp_name = urlencode("{$a_cert[$id]['descr']}.req");
177
	$exp_data = base64_decode($a_cert[$id]['csr']);
178
	$exp_size = strlen($exp_data);
179

    
180
	header("Content-Type: application/octet-stream");
181
	header("Content-Disposition: attachment; filename={$exp_name}");
182
	header("Content-Length: $exp_size");
183
	echo $exp_data;
184
	exit;
185
}
186

    
187
if ($act == "key") {
188

    
189
	if (!$a_cert[$id]) {
190
		pfSenseHeader("system_certmanager.php");
191
		exit;
192
	}
193

    
194
	$exp_name = urlencode("{$a_cert[$id]['descr']}.key");
195
	$exp_data = base64_decode($a_cert[$id]['prv']);
196
	$exp_size = strlen($exp_data);
197

    
198
	header("Content-Type: application/octet-stream");
199
	header("Content-Disposition: attachment; filename={$exp_name}");
200
	header("Content-Length: $exp_size");
201
	echo $exp_data;
202
	exit;
203
}
204

    
205
if ($act == "p12") {
206
	if (!$a_cert[$id]) {
207
		pfSenseHeader("system_certmanager.php");
208
		exit;
209
	}
210

    
211
	$exp_name = urlencode("{$a_cert[$id]['descr']}.p12");
212
	$args = array();
213
	$args['friendly_name'] = $a_cert[$id]['descr'];
214

    
215
	$ca = lookup_ca($a_cert[$id]['caref']);
216
	if ($ca) {
217
		$args['extracerts'] = openssl_x509_read(base64_decode($ca['crt']));
218
	}
219

    
220
	$res_crt = openssl_x509_read(base64_decode($a_cert[$id]['crt']));
221
	$res_key = openssl_pkey_get_private(array(0 => base64_decode($a_cert[$id]['prv']) , 1 => ""));
222

    
223
	$exp_data = "";
224
	openssl_pkcs12_export($res_crt, $exp_data, $res_key, null, $args);
225
	$exp_size = strlen($exp_data);
226

    
227
	header("Content-Type: application/octet-stream");
228
	header("Content-Disposition: attachment; filename={$exp_name}");
229
	header("Content-Length: $exp_size");
230
	echo $exp_data;
231
	exit;
232
}
233

    
234
if ($act == "csr") {
235
	if (!$a_cert[$id]) {
236
		pfSenseHeader("system_certmanager.php");
237
		exit;
238
	}
239

    
240
	$pconfig['descr'] = $a_cert[$id]['descr'];
241
	$pconfig['csr'] = base64_decode($a_cert[$id]['csr']);
242
}
243

    
244
if ($_POST) {
245
	// This is just the blank alternate name that is added for display purposes. We don't want to validate/save it
246
	if ($_POST['altname_value0'] == "") {
247
		unset($_POST['altname_type0']);
248
		unset($_POST['altname_value0']);
249
	}
250

    
251
	if ($_POST['save'] == gettext("Save")) {
252
		$input_errors = array();
253
		$pconfig = $_POST;
254

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

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

    
286
		if ($pconfig['method'] == "external") {
287
			$reqdfields = explode(" ",
288
				"descr csr_keylen csr_dn_country csr_dn_state csr_dn_city ".
289
				"csr_dn_organization csr_dn_email csr_dn_commonname");
290
			$reqdfieldsn = array(
291
				gettext("Descriptive name"),
292
				gettext("Key length"),
293
				gettext("Distinguished name Country Code"),
294
				gettext("Distinguished name State or Province"),
295
				gettext("Distinguished name City"),
296
				gettext("Distinguished name Organization"),
297
				gettext("Distinguished name Email Address"),
298
				gettext("Distinguished name Common Name"));
299
		}
300

    
301
		if ($pconfig['method'] == "existing") {
302
			$reqdfields = array("certref");
303
			$reqdfieldsn = array(gettext("Existing Certificate Choice"));
304
		}
305

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

    
320
				if (ctype_digit($entry)) {
321
					$entry++;	// Pre-bootstrap code is one-indexed, but the bootstrap code is 0-indexed
322
					$altnames[$entry][$field] = $value;
323
				}
324
			}
325

    
326
			$pconfig['altnames']['item'] = $altnames;
327

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

    
360
			/* Make sure we do not have invalid characters in the fields for the certificate */
361

    
362
			if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
363
				array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
364
			}
365

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

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

    
387
			if (($pconfig['method'] == "external") && isset($_POST["csr_keylen"]) && !in_array($_POST["csr_keylen"], $cert_keylens)) {
388
				array_push($input_errors, gettext("Please select a valid Key Length."));
389
			}
390
			if (($pconfig['method'] == "external") && !in_array($_POST["csr_digest_alg"], $openssl_digest_algs)) {
391
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
392
			}
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
					if (!empty($pconfig['dn_organizationalunit'])) {
427
						$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
428
					}
429
					if (count($altnames)) {
430
						$altnames_tmp = "";
431
						foreach ($altnames as $altname) {
432
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
433
						}
434

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

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

    
446
				if ($pconfig['method'] == "external") {
447
					$dn = array(
448
						'countryName' => $pconfig['csr_dn_country'],
449
						'stateOrProvinceName' => $pconfig['csr_dn_state'],
450
						'localityName' => $pconfig['csr_dn_city'],
451
						'organizationName' => $pconfig['csr_dn_organization'],
452
						'emailAddress' => $pconfig['csr_dn_email'],
453
						'commonName' => $pconfig['csr_dn_commonname']);
454
					if (!empty($pconfig['csr_dn_organizationalunit'])) {
455
						$dn['organizationalUnitName'] = $pconfig['csr_dn_organizationalunit'];
456
					}
457
					if (count($altnames)) {
458
						$altnames_tmp = "";
459
						foreach ($altnames as $altname) {
460
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
461
						}
462
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
463
					}
464

    
465
					if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) {
466
						while ($ssl_err = openssl_error_string()) {
467
							$input_errors = array();
468
							array_push($input_errors, "openssl library returns: " . $ssl_err);
469
						}
470
					}
471
				}
472
				error_reporting($old_err_level);
473

    
474
				if (isset($id) && $a_cert[$id]) {
475
					$a_cert[$id] = $cert;
476
				} else {
477
					$a_cert[] = $cert;
478
				}
479

    
480
				if (isset($a_user) && isset($userid)) {
481
					$a_user[$userid]['cert'][] = $cert['refid'];
482
				}
483
			}
484

    
485
			if (!$input_errors) {
486
				write_config();
487
			}
488

    
489
			if ($userid) {
490
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
491
				exit;
492
			}
493
		}
494
	}
495

    
496
	if ($_POST['save'] == gettext("Update")) {
497
		unset($input_errors);
498
		$pconfig = $_POST;
499

    
500
		/* input validation */
501
		$reqdfields = explode(" ", "descr cert");
502
		$reqdfieldsn = array(
503
			gettext("Descriptive name"),
504
			gettext("Final Certificate data"));
505

    
506
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
507

    
508
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
509
			array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
510
		}
511

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

    
526
		if (strcmp($mod_csr, $mod_cert)) {
527
			// simply: if the moduli don't match, then the private key and public key won't match
528
			$input_errors[] = sprintf(gettext("The certificate modulus does not match the signing request modulus."), $subj_cert);
529
			$subject_mismatch = true;
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
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
551

    
552
if (($act == "new" || ($_POST['save'] == gettext("Save") && $input_errors)) || ($act == "csr" || ($_POST['save'] == gettext("Update") && $input_errors))) {
553
	$pgtitle[] = gettext('Edit');
554
}
555
include("head.inc");
556

    
557
if ($input_errors) {
558
	print_input_errors($input_errors);
559
}
560

    
561
if ($savemsg) {
562
	print_info_box($savemsg, 'success');
563
}
564

    
565
$tab_array = array();
566
$tab_array[] = array(gettext("CAs"), false, "system_camanager.php");
567
$tab_array[] = array(gettext("Certificates"), true, "system_certmanager.php");
568
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
569
display_top_tabs($tab_array);
570

    
571
// Load valid country codes
572
$dn_cc = array();
573
if (file_exists("/etc/ca_countries")) {
574
	$dn_cc_file=file("/etc/ca_countries");
575
	foreach ($dn_cc_file as $line) {
576
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
577
			$dn_cc[$matches[1]] = $matches[1];
578
		}
579
	}
580
}
581

    
582
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
583
	$form = new Form();
584
	$form->setAction('system_certmanager.php?act=edit');
585

    
586
	if (isset($userid) && $a_user) {
587
		$form->addGlobal(new Form_Input(
588
			'userid',
589
			null,
590
			'hidden',
591
			$userid
592
		));
593
	}
594

    
595
	if (isset($id) && $a_cert[$id]) {
596
		$form->addGlobal(new Form_Input(
597
			'id',
598
			null,
599
			'hidden',
600
			$id
601
		));
602
	}
603

    
604
	$section = new Form_Section('Add a New Certificate');
605

    
606
	if (!isset($id)) {
607
		$section->addInput(new Form_Select(
608
			'method',
609
			'Method',
610
			$pconfig['method'],
611
			$cert_methods
612
		))->toggles();
613
	}
614

    
615
	$section->addInput(new Form_Input(
616
		'descr',
617
		'Descriptive name',
618
		'text',
619
		($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
620
	))->addClass('toggle-existing');
621

    
622
	$form->add($section);
623
	$section = new Form_Section('Import Certificate');
624
	$section->addClass('toggle-import collapse');
625

    
626
	$section->addInput(new Form_Textarea(
627
		'cert',
628
		'Certificate data',
629
		$pconfig['cert']
630
	))->setHelp('Paste a certificate in X.509 PEM format here.');
631

    
632
	$section->addInput(new Form_Textarea(
633
		'key',
634
		'Private key data',
635
		$pconfig['key']
636
	))->setHelp('Paste a private key in X.509 PEM format here.');
637

    
638
	$form->add($section);
639
	$section = new Form_Section('Internal Certificate');
640
	$section->addClass('toggle-internal collapse');
641

    
642
	if (!$internal_ca_count) {
643
		$section->addInput(new Form_StaticText(
644
			'Certificate authority',
645
			gettext('No internal Certificate Authorities have been defined. ').
646
			gettext('An internal CA must be defined in order to create an internal certificate. ').
647
			'<a href="system_camanager.php?act=new&amp;method=internal"> '. gettext("Create") .'</a>'.
648
			gettext(' an internal CA.')
649
		));
650
	} else {
651
		$allCas = array();
652
		foreach ($a_ca as $ca) {
653
			if (!$ca['prv']) {
654
				continue;
655
			}
656

    
657
			$allCas[ $ca['refid'] ] = $ca['descr'];
658
		}
659

    
660
		$section->addInput(new Form_Select(
661
			'caref',
662
			'Certificate authority',
663
			$pconfig['caref'],
664
			$allCas
665
		));
666
	}
667

    
668
	$section->addInput(new Form_Select(
669
		'keylen',
670
		'Key length',
671
		$pconfig['keylen'],
672
		array_combine($cert_keylens, $cert_keylens)
673
	));
674

    
675
	$section->addInput(new Form_Select(
676
		'digest_alg',
677
		'Digest Algorithm',
678
		$pconfig['digest_alg'],
679
		array_combine($openssl_digest_algs, $openssl_digest_algs)
680
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
681
		'SHA1 when possible.');
682

    
683
	$section->addInput(new Form_Select(
684
		'type',
685
		'Certificate Type',
686
		$pconfig['type'],
687
		$cert_types
688
	))->setHelp('Type of certificate to generate. Used for placing '.
689
		'restrictions on the usage of the generated certificate.');
690

    
691
	$section->addInput(new Form_Input(
692
		'lifetime',
693
		'Lifetime (days)',
694
		'number',
695
		$pconfig['lifetime']
696
	));
697

    
698
	$section->addInput(new Form_Select(
699
		'dn_country',
700
		'Country Code',
701
		$pconfig['dn_country'],
702
		$dn_cc
703
	));
704

    
705
	$section->addInput(new Form_Input(
706
		'dn_state',
707
		'State or Province',
708
		'text',
709
		$pconfig['dn_state'],
710
		['placeholder' => 'e.g. Texas']
711
	));
712

    
713
	$section->addInput(new Form_Input(
714
		'dn_city',
715
		'City',
716
		'text',
717
		$pconfig['dn_city'],
718
		['placeholder' => 'e.g. Austin']
719
	));
720

    
721
	$section->addInput(new Form_Input(
722
		'dn_organization',
723
		'Organization',
724
		'text',
725
		$pconfig['dn_organization'],
726
		['placeholder' => 'e.g. My Company Inc']
727
	));
728

    
729
	$section->addInput(new Form_Input(
730
		'dn_organizationalunit',
731
		'Organizational Unit',
732
		'text',
733
		$pconfig['dn_organizationalunit'],
734
		['placeholder' => 'e.g. My Department Name (optional)']
735
	));
736

    
737
	$section->addInput(new Form_Input(
738
		'dn_email',
739
		'Email Address',
740
		'text',
741
		$pconfig['dn_email'],
742
		['placeholder' => 'e.g. admin@mycompany.com']
743
	));
744

    
745
	$section->addInput(new Form_Input(
746
		'dn_commonname',
747
		'Common Name',
748
		'text',
749
		$pconfig['dn_commonname'],
750
		['placeholder' => 'e.g. www.example.com']
751
	));
752

    
753
	if (empty($pconfig['altnames']['item'])) {
754
		$pconfig['altnames']['item'] = array(
755
			array('type' => null, 'value' => null)
756
		);
757
	}
758

    
759
	$counter = 0;
760
	$numrows = count($pconfig['altnames']['item']) - 1;
761

    
762
	foreach ($pconfig['altnames']['item'] as $item) {
763

    
764
		$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
765

    
766
		$group->add(new Form_Select(
767
			'altname_type' . $counter,
768
			'Type',
769
			$item['type'],
770
			array(
771
				'DNS' => gettext('FQDN or Hostname'),
772
				'IP' => gettext('IP address'),
773
				'URI' => gettext('URI'),
774
				'email' => gettext('email address'),
775
			)
776
		))->setHelp(($counter == $numrows) ? 'Type':null);
777

    
778
		$group->add(new Form_Input(
779
			'altname_value' . $counter,
780
			null,
781
			'text',
782
			$item['value']
783
		))->setHelp(($counter == $numrows) ? 'Value':null);
784

    
785
		$group->add(new Form_Button(
786
			'deleterow' . $counter,
787
			'Delete',
788
			null,
789
			'fa-trash'
790
		))->addClass('btn-warning');
791

    
792
		$group->addClass('repeatable');
793

    
794
		$section->add($group);
795

    
796
		$counter++;
797
	}
798

    
799
	$section->addInput(new Form_Button(
800
		'addrow',
801
		'Add',
802
		null,
803
		'fa-plus'
804
	))->addClass('btn-success');
805

    
806
	$form->add($section);
807
	$section = new Form_Section('External Signing Request');
808
	$section->addClass('toggle-external collapse');
809

    
810
	$section->addInput(new Form_Select(
811
		'csr_keylen',
812
		'Key length',
813
		$pconfig['csr_keylen'],
814
		array_combine($cert_keylens, $cert_keylens)
815
	));
816

    
817
	$section->addInput(new Form_Select(
818
		'csr_digest_alg',
819
		'Digest Algorithm',
820
		$pconfig['csr_digest_alg'],
821
		array_combine($openssl_digest_algs, $openssl_digest_algs)
822
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
823
		'SHA1 when possible');
824

    
825
	$section->addInput(new Form_Select(
826
		'csr_dn_country',
827
		'Country Code',
828
		$pconfig['csr_dn_country'],
829
		$dn_cc
830
	));
831

    
832
	$section->addInput(new Form_Input(
833
		'csr_dn_state',
834
		'State or Province',
835
		'text',
836
		$pconfig['csr_dn_state'],
837
		['placeholder' => 'e.g. Texas']
838
	));
839

    
840
	$section->addInput(new Form_Input(
841
		'csr_dn_city',
842
		'City',
843
		'text',
844
		$pconfig['csr_dn_city'],
845
		['placeholder' => 'e.g. Austin']
846
	));
847

    
848
	$section->addInput(new Form_Input(
849
		'csr_dn_organization',
850
		'Organization',
851
		'text',
852
		$pconfig['csr_dn_organization'],
853
		['placeholder' => 'e.g. My Company Inc']
854
	));
855

    
856
	$section->addInput(new Form_Input(
857
		'csr_dn_organizationalunit',
858
		'Organizational Unit',
859
		'text',
860
		$pconfig['csr_dn_organizationalunit'],
861
		['placeholder' => 'e.g. My Department Name (optional)']
862
	));
863

    
864
	$section->addInput(new Form_Input(
865
		'csr_dn_email',
866
		'Email Address',
867
		'text',
868
		$pconfig['csr_dn_email'],
869
		['placeholder' => 'e.g. admin@mycompany.com']
870
	));
871

    
872
	$section->addInput(new Form_Input(
873
		'csr_dn_commonname',
874
		'Common Name',
875
		'text',
876
		$pconfig['csr_dn_commonname'],
877
		['placeholder' => 'e.g. internal-ca']
878
	));
879

    
880
	$form->add($section);
881
	$section = new Form_Section('Choose an Existing Certificate');
882
	$section->addClass('toggle-existing collapse');
883

    
884
	$existCerts = array();
885

    
886
	foreach ($config['cert'] as $cert)	{
887
		if (is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
888
			if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert'])) {
889
				continue;
890
			}
891
		}
892

    
893
		$ca = lookup_ca($cert['caref']);
894
		if ($ca) {
895
			$cert['descr'] .= " (CA: {$ca['descr']})";
896
		}
897

    
898
		if (cert_in_use($cert['refid'])) {
899
			$cert['descr'] .= " <i>In Use</i>";
900
		}
901
		if (is_cert_revoked($cert)) {
902
			$cert['descr'] .= " <b>Revoked</b>";
903
		}
904

    
905
		$existCerts[ $cert['refid'] ] = $cert['descr'];
906
	}
907

    
908
	$section->addInput(new Form_Select(
909
		'certref',
910
		'Existing Certificates',
911
		$pconfig['certref'],
912
		$existCerts
913
	));
914

    
915
	$form->add($section);
916
	print $form;
917

    
918
} else if ($act == "csr" || (($_POST['save'] == gettext("Update")) && $input_errors)) {
919
	$form = new Form(false);
920
	$form->setAction('system_certmanager.php?act=csr');
921

    
922
	$section = new Form_Section("Complete Signing Request for " . $pconfig['descr']);
923

    
924
	$section->addInput(new Form_Input(
925
		'descr',
926
		'Descriptive name',
927
		'text',
928
		$pconfig['descr']
929
	));
930

    
931
	$section->addInput(new Form_Textarea(
932
		'csr',
933
		'Signing request data',
934
		$pconfig['csr']
935
	))->setReadonly()
936
	  ->setWidth(7)
937
	  ->setHelp('Copy the certificate signing data from here and forward it to a certificate authority for signing.');
938

    
939
	$section->addInput(new Form_Textarea(
940
		'cert',
941
		'Final certificate data',
942
		$pconfig['cert']
943
	))->setWidth(7)
944
	  ->setHelp('Paste the certificate received from the certificate authority here.');
945

    
946
	 if (isset($id) && $a_cert[$id]) {
947
		 $section->addInput(new Form_Input(
948
			'id',
949
			null,
950
			'hidden',
951
			$id
952
		 ));
953

    
954
		 $section->addInput(new Form_Input(
955
			'act',
956
			null,
957
			'hidden',
958
			'csr'
959
		 ));
960
	 }
961

    
962
	$form->add($section);
963

    
964
	$form->addGlobal(new Form_Button(
965
		'save',
966
		'Update',
967
		null,
968
		'fa-save'
969
	))->addClass('btn-primary');
970

    
971
	print($form);
972
} else {
973
?>
974
<div class="panel panel-default">
975
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificates')?></h2></div>
976
	<div class="panel-body">
977
		<div class="table-responsive">
978
		<table class="table table-striped table-hover">
979
			<thead>
980
				<tr>
981
					<th><?=gettext("Name")?></th>
982
					<th><?=gettext("Issuer")?></th>
983
					<th><?=gettext("Distinguished Name")?></th>
984
					<th><?=gettext("In Use")?></th>
985

    
986
					<th class="col-sm-2"><?=gettext("Actions")?></th>
987
				</tr>
988
			</thead>
989
			<tbody>
990
<?php
991

    
992
$pluginparams = array();
993
$pluginparams['type'] = 'certificates';
994
$pluginparams['event'] = 'used_certificates';
995
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
996
$i = 0;
997
foreach ($a_cert as $i => $cert):
998
	$name = htmlspecialchars($cert['descr']);
999

    
1000
	if ($cert['crt']) {
1001
		$subj = cert_get_subject($cert['crt']);
1002
		$issuer = cert_get_issuer($cert['crt']);
1003
		$purpose = cert_get_purpose($cert['crt']);
1004
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
1005

    
1006
		if ($subj == $issuer) {
1007
			$caname = '<i>'. gettext("self-signed") .'</i>';
1008
		} else {
1009
			$caname = '<i>'. gettext("external").'</i>';
1010
		}
1011

    
1012
		$subj = htmlspecialchars($subj);
1013
	}
1014

    
1015
	if ($cert['csr']) {
1016
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
1017
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
1018
	}
1019

    
1020
	$ca = lookup_ca($cert['caref']);
1021
	if ($ca) {
1022
		$caname = $ca['descr'];
1023
	}
1024
?>
1025
				<tr>
1026
					<td>
1027
						<?=$name?><br />
1028
						<?php if ($cert['type']): ?>
1029
							<i><?=$cert_types[$cert['type']]?></i><br />
1030
						<?php endif?>
1031
						<?php if (is_array($purpose)): ?>
1032
							CA: <b><?=$purpose['ca']?></b>, <?=gettext("Server")?>: <b><?=$purpose['server']?></b>
1033
						<?php endif?>
1034
					</td>
1035
					<td><?=$caname?></td>
1036
					<td>
1037
						<?=$subj?>
1038
						<?php if (!$cert['csr']): ?>
1039
						<br />
1040
						<small>
1041
							<?=gettext("Valid From")?>: <b><?=$startdate ?></b><br /><?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
1042
						</small>
1043
						<?php endif?>
1044
					</td>
1045
					<td>
1046
						<?php if (is_cert_revoked($cert)): ?>
1047
							<i><?=gettext("Revoked")?></i>
1048
						<?php endif?>
1049
						<?php if (is_webgui_cert($cert['refid'])): ?>
1050
							<?=gettext("webConfigurator")?>
1051
						<?php endif?>
1052
						<?php if (is_user_cert($cert['refid'])): ?>
1053
							<?=gettext("User Cert")?>
1054
						<?php endif?>
1055
						<?php if (is_openvpn_server_cert($cert['refid'])): ?>
1056
							<?=gettext("OpenVPN Server")?>
1057
						<?php endif?>
1058
						<?php if (is_openvpn_client_cert($cert['refid'])): ?>
1059
							<?=gettext("OpenVPN Client")?>
1060
						<?php endif?>
1061
						<?php if (is_ipsec_cert($cert['refid'])): ?>
1062
							<?=gettext("IPsec Tunnel")?>
1063
						<?php endif?>
1064
						<?php if (is_captiveportal_cert($cert['refid'])): ?>
1065
							<?=gettext("Captive Portal")?>
1066
						<?php endif?>
1067
<?php
1068
							$refid = $cert['refid'];
1069
							if (is_array($certificates_used_by_packages)) {
1070
								foreach ($certificates_used_by_packages as $name => $package) {
1071
									if (isset($package['certificatelist'][$refid])) {
1072
										$hint = "" ;
1073
										if (is_array($package['certificatelist'][$refid])) {
1074
											foreach ($package['certificatelist'][$refid] as $cert_used) {
1075
												$hint = $hint . $cert_used['usedby']."\n";
1076
											}
1077
										}
1078
										$count = count($package['certificatelist'][$refid]);
1079
										echo "<div title='".htmlspecialchars($hint)."'>";
1080
										echo htmlspecialchars($package['pkgname'])." ($count)<br />";
1081
										echo "</div>";
1082
									}
1083
								}
1084
							}
1085
?>
1086
					</td>
1087
					<td>
1088
						<?php if (!$cert['csr']): ?>
1089
							<a href="system_certmanager.php?act=exp&amp;id=<?=$i?>" class="fa fa-certificate" title="<?=gettext("Export Certificate")?>"></a>
1090
							<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1091
							<a href="system_certmanager.php?act=p12&amp;id=<?=$i?>" class="fa fa-archive" title="<?=gettext("Export P12")?>"></a>
1092
						<?php else: ?>
1093
							<a href="system_certmanager.php?act=csr&amp;id=<?=$i?>" class="fa fa-pencil" title="<?=gettext("Update CSR")?>"></a>
1094
							<a href="system_certmanager.php?act=req&amp;id=<?=$i?>" class="fa fa-sign-in" title="<?=gettext("Export Request")?>"></a>
1095
							<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1096
						<?php endif?>
1097
						<?php if (!cert_in_use($cert['refid'])): ?>
1098
							<a href="system_certmanager.php?act=del&amp;id=<?=$i?>" class="fa fa-trash" title="<?=gettext("Delete Certificate")?>"></a>
1099
						<?php endif?>
1100
					</td>
1101
				</tr>
1102
<?php
1103
	$i++; 
1104
	endforeach; ?>
1105
			</tbody>
1106
		</table>
1107
		</div>
1108
	</div>
1109
</div>
1110

    
1111
<nav class="action-buttons">
1112
	<a href="?act=new" class="btn btn-success btn-sm">
1113
		<i class="fa fa-plus icon-embed-btn"></i>
1114
		<?=gettext("Add")?>
1115
	</a>
1116
</nav>
1117
<?php
1118
	include("foot.inc");
1119
	exit;
1120
}
1121

    
1122

    
1123
?>
1124
<script type="text/javascript">
1125
//<![CDATA[
1126
events.push(function() {
1127

    
1128
<?php if ($internal_ca_count): ?>
1129
	function internalca_change() {
1130

    
1131
		caref = $('#caref').val();
1132

    
1133
		switch (caref) {
1134
<?php
1135
			foreach ($a_ca as $ca):
1136
				if (!$ca['prv']) {
1137
					continue;
1138
				}
1139

    
1140
				$subject = cert_get_subject_array($ca['crt']);
1141

    
1142
?>
1143
				case "<?=$ca['refid'];?>":
1144
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1145
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1146
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1147
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1148
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1149
					$('#dn_organizationalunit').val("<?=$subject[6]['v'];?>");
1150
					break;
1151
<?php
1152
			endforeach;
1153
?>
1154
		}
1155
	}
1156

    
1157
	// ---------- Click checkbox handlers ---------------------------------------------------------
1158

    
1159
	$('#caref').on('change', function() {
1160
		internalca_change();
1161
	});
1162

    
1163
	// ---------- On initial page load ------------------------------------------------------------
1164

    
1165
	internalca_change();
1166

    
1167
	// Suppress "Delete row" button if there are fewer than two rows
1168
	checkLastRow();
1169

    
1170
<?php endif; ?>
1171

    
1172

    
1173
});
1174
//]]>
1175
</script>
1176
<?php
1177
include('foot.inc');
(195-195/227)