Project

General

Profile

Download (33 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
global $openssl_digest_algs;
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
						$input_errors = array();
440
						while ($ssl_err = openssl_error_string()) {
441
							if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
442
								array_push($input_errors, "openssl library returns: " . $ssl_err);
443
							}
444
						}
445
					}
446
				}
447

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

    
467
					if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) {
468
						$input_errors = array();
469
						while ($ssl_err = openssl_error_string()) {
470
							if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
471
								array_push($input_errors, "openssl library returns: " . $ssl_err);
472
							}
473
						}
474
					}
475
				}
476
				error_reporting($old_err_level);
477

    
478
				if (isset($id) && $a_cert[$id]) {
479
					$a_cert[$id] = $cert;
480
				} else {
481
					$a_cert[] = $cert;
482
				}
483

    
484
				if (isset($a_user) && isset($userid)) {
485
					$a_user[$userid]['cert'][] = $cert['refid'];
486
				}
487
			}
488

    
489
			if (!$input_errors) {
490
				write_config();
491
			}
492

    
493
			if ($userid) {
494
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
495
				exit;
496
			}
497
		}
498
	}
499

    
500
	if ($_POST['save'] == gettext("Update")) {
501
		unset($input_errors);
502
		$pconfig = $_POST;
503

    
504
		/* input validation */
505
		$reqdfields = explode(" ", "descr cert");
506
		$reqdfieldsn = array(
507
			gettext("Descriptive name"),
508
			gettext("Final Certificate data"));
509

    
510
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
511

    
512
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
513
			array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
514
		}
515

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

    
530
		if (strcmp($mod_csr, $mod_cert)) {
531
			// simply: if the moduli don't match, then the private key and public key won't match
532
			$input_errors[] = sprintf(gettext("The certificate modulus does not match the signing request modulus."), $subj_cert);
533
			$subject_mismatch = true;
534
		}
535

    
536
		/* save modifications */
537
		if (!$input_errors) {
538

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

    
541
			$cert['descr'] = $pconfig['descr'];
542

    
543
			csr_complete($cert, $pconfig['cert']);
544

    
545
			$a_cert[$id] = $cert;
546

    
547
			write_config();
548

    
549
			pfSenseHeader("system_certmanager.php");
550
		}
551
	}
552
}
553

    
554
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
555
$pglinks = array("", "system_camanager.php", "system_certmanager.php");
556

    
557
if (($act == "new" || ($_POST['save'] == gettext("Save") && $input_errors)) || ($act == "csr" || ($_POST['save'] == gettext("Update") && $input_errors))) {
558
	$pgtitle[] = gettext('Edit');
559
	$pglinks[] = "@self";
560
}
561
include("head.inc");
562

    
563
if ($input_errors) {
564
	print_input_errors($input_errors);
565
}
566

    
567
if ($savemsg) {
568
	print_info_box($savemsg, 'success');
569
}
570

    
571
$tab_array = array();
572
$tab_array[] = array(gettext("CAs"), false, "system_camanager.php");
573
$tab_array[] = array(gettext("Certificates"), true, "system_certmanager.php");
574
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
575
display_top_tabs($tab_array);
576

    
577
// Load valid country codes
578
$dn_cc = array();
579
if (file_exists("/etc/ca_countries")) {
580
	$dn_cc_file=file("/etc/ca_countries");
581
	foreach ($dn_cc_file as $line) {
582
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
583
			$dn_cc[$matches[1]] = $matches[1];
584
		}
585
	}
586
}
587

    
588
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
589
	$form = new Form();
590
	$form->setAction('system_certmanager.php?act=edit');
591

    
592
	if (isset($userid) && $a_user) {
593
		$form->addGlobal(new Form_Input(
594
			'userid',
595
			null,
596
			'hidden',
597
			$userid
598
		));
599
	}
600

    
601
	if (isset($id) && $a_cert[$id]) {
602
		$form->addGlobal(new Form_Input(
603
			'id',
604
			null,
605
			'hidden',
606
			$id
607
		));
608
	}
609

    
610
	$section = new Form_Section('Add a New Certificate');
611

    
612
	if (!isset($id)) {
613
		$section->addInput(new Form_Select(
614
			'method',
615
			'*Method',
616
			$pconfig['method'],
617
			$cert_methods
618
		))->toggles();
619
	}
620

    
621
	$section->addInput(new Form_Input(
622
		'descr',
623
		'*Descriptive name',
624
		'text',
625
		($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
626
	))->addClass('toggle-existing');
627

    
628
	$form->add($section);
629
	$section = new Form_Section('Import Certificate');
630
	$section->addClass('toggle-import collapse');
631

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

    
638
	$section->addInput(new Form_Textarea(
639
		'key',
640
		'*Private key data',
641
		$pconfig['key']
642
	))->setHelp('Paste a private key in X.509 PEM format here.');
643

    
644
	$form->add($section);
645
	$section = new Form_Section('Internal Certificate');
646
	$section->addClass('toggle-internal collapse');
647

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

    
663
			$allCas[ $ca['refid'] ] = $ca['descr'];
664
		}
665

    
666
		$section->addInput(new Form_Select(
667
			'caref',
668
			'*Certificate authority',
669
			$pconfig['caref'],
670
			$allCas
671
		));
672
	}
673

    
674
	$section->addInput(new Form_Select(
675
		'keylen',
676
		'*Key length',
677
		$pconfig['keylen'],
678
		array_combine($cert_keylens, $cert_keylens)
679
	));
680

    
681
	$section->addInput(new Form_Select(
682
		'digest_alg',
683
		'*Digest Algorithm',
684
		$pconfig['digest_alg'],
685
		array_combine($openssl_digest_algs, $openssl_digest_algs)
686
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
687
		'SHA1 when possible.');
688

    
689
	$section->addInput(new Form_Select(
690
		'type',
691
		'*Certificate Type',
692
		$pconfig['type'],
693
		$cert_types
694
	))->setHelp('Type of certificate to generate. Used for placing '.
695
		'restrictions on the usage of the generated certificate.');
696

    
697
	$section->addInput(new Form_Input(
698
		'lifetime',
699
		'*Lifetime (days)',
700
		'number',
701
		$pconfig['lifetime']
702
	));
703

    
704
	$section->addInput(new Form_Select(
705
		'dn_country',
706
		'*Country Code',
707
		$pconfig['dn_country'],
708
		$dn_cc
709
	));
710

    
711
	$section->addInput(new Form_Input(
712
		'dn_state',
713
		'*State or Province',
714
		'text',
715
		$pconfig['dn_state'],
716
		['placeholder' => 'e.g. Texas']
717
	));
718

    
719
	$section->addInput(new Form_Input(
720
		'dn_city',
721
		'*City',
722
		'text',
723
		$pconfig['dn_city'],
724
		['placeholder' => 'e.g. Austin']
725
	));
726

    
727
	$section->addInput(new Form_Input(
728
		'dn_organization',
729
		'*Organization',
730
		'text',
731
		$pconfig['dn_organization'],
732
		['placeholder' => 'e.g. My Company Inc']
733
	));
734

    
735
	$section->addInput(new Form_Input(
736
		'dn_organizationalunit',
737
		'Organizational Unit',
738
		'text',
739
		$pconfig['dn_organizationalunit'],
740
		['placeholder' => 'e.g. My Department Name (optional)']
741
	));
742

    
743
	$section->addInput(new Form_Input(
744
		'dn_email',
745
		'*Email Address',
746
		'text',
747
		$pconfig['dn_email'],
748
		['placeholder' => 'e.g. admin@mycompany.com']
749
	));
750

    
751
	$section->addInput(new Form_Input(
752
		'dn_commonname',
753
		'*Common Name',
754
		'text',
755
		$pconfig['dn_commonname'],
756
		['placeholder' => 'e.g. www.example.com']
757
	));
758

    
759
	if (empty($pconfig['altnames']['item'])) {
760
		$pconfig['altnames']['item'] = array(
761
			array('type' => null, 'value' => null)
762
		);
763
	}
764

    
765
	$counter = 0;
766
	$numrows = count($pconfig['altnames']['item']) - 1;
767

    
768
	foreach ($pconfig['altnames']['item'] as $item) {
769

    
770
		$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
771

    
772
		$group->add(new Form_Select(
773
			'altname_type' . $counter,
774
			'Type',
775
			$item['type'],
776
			array(
777
				'DNS' => gettext('FQDN or Hostname'),
778
				'IP' => gettext('IP address'),
779
				'URI' => gettext('URI'),
780
				'email' => gettext('email address'),
781
			)
782
		))->setHelp(($counter == $numrows) ? 'Type':null);
783

    
784
		$group->add(new Form_Input(
785
			'altname_value' . $counter,
786
			null,
787
			'text',
788
			$item['value']
789
		))->setHelp(($counter == $numrows) ? 'Value':null);
790

    
791
		$group->add(new Form_Button(
792
			'deleterow' . $counter,
793
			'Delete',
794
			null,
795
			'fa-trash'
796
		))->addClass('btn-warning');
797

    
798
		$group->addClass('repeatable');
799

    
800
		$section->add($group);
801

    
802
		$counter++;
803
	}
804

    
805
	$section->addInput(new Form_Button(
806
		'addrow',
807
		'Add',
808
		null,
809
		'fa-plus'
810
	))->addClass('btn-success');
811

    
812
	$form->add($section);
813
	$section = new Form_Section('External Signing Request');
814
	$section->addClass('toggle-external collapse');
815

    
816
	$section->addInput(new Form_Select(
817
		'csr_keylen',
818
		'*Key length',
819
		$pconfig['csr_keylen'],
820
		array_combine($cert_keylens, $cert_keylens)
821
	));
822

    
823
	$section->addInput(new Form_Select(
824
		'csr_digest_alg',
825
		'*Digest Algorithm',
826
		$pconfig['csr_digest_alg'],
827
		array_combine($openssl_digest_algs, $openssl_digest_algs)
828
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
829
		'SHA1 when possible');
830

    
831
	$section->addInput(new Form_Select(
832
		'csr_dn_country',
833
		'*Country Code',
834
		$pconfig['csr_dn_country'],
835
		$dn_cc
836
	));
837

    
838
	$section->addInput(new Form_Input(
839
		'csr_dn_state',
840
		'*State or Province',
841
		'text',
842
		$pconfig['csr_dn_state'],
843
		['placeholder' => 'e.g. Texas']
844
	));
845

    
846
	$section->addInput(new Form_Input(
847
		'csr_dn_city',
848
		'*City',
849
		'text',
850
		$pconfig['csr_dn_city'],
851
		['placeholder' => 'e.g. Austin']
852
	));
853

    
854
	$section->addInput(new Form_Input(
855
		'csr_dn_organization',
856
		'*Organization',
857
		'text',
858
		$pconfig['csr_dn_organization'],
859
		['placeholder' => 'e.g. My Company Inc']
860
	));
861

    
862
	$section->addInput(new Form_Input(
863
		'csr_dn_organizationalunit',
864
		'Organizational Unit',
865
		'text',
866
		$pconfig['csr_dn_organizationalunit'],
867
		['placeholder' => 'e.g. My Department Name (optional)']
868
	));
869

    
870
	$section->addInput(new Form_Input(
871
		'csr_dn_email',
872
		'*Email Address',
873
		'text',
874
		$pconfig['csr_dn_email'],
875
		['placeholder' => 'e.g. admin@mycompany.com']
876
	));
877

    
878
	$section->addInput(new Form_Input(
879
		'csr_dn_commonname',
880
		'*Common Name',
881
		'text',
882
		$pconfig['csr_dn_commonname'],
883
		['placeholder' => 'e.g. internal-ca']
884
	));
885

    
886
	$form->add($section);
887
	$section = new Form_Section('Choose an Existing Certificate');
888
	$section->addClass('toggle-existing collapse');
889

    
890
	$existCerts = array();
891

    
892
	foreach ($config['cert'] as $cert)	{
893
		if (is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
894
			if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert'])) {
895
				continue;
896
			}
897
		}
898

    
899
		$ca = lookup_ca($cert['caref']);
900
		if ($ca) {
901
			$cert['descr'] .= " (CA: {$ca['descr']})";
902
		}
903

    
904
		if (cert_in_use($cert['refid'])) {
905
			$cert['descr'] .= " (In Use)";
906
		}
907
		if (is_cert_revoked($cert)) {
908
			$cert['descr'] .= " (Revoked)";
909
		}
910

    
911
		$existCerts[ $cert['refid'] ] = $cert['descr'];
912
	}
913

    
914
	$section->addInput(new Form_Select(
915
		'certref',
916
		'*Existing Certificates',
917
		$pconfig['certref'],
918
		$existCerts
919
	));
920

    
921
	$form->add($section);
922
	print $form;
923

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

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

    
930
	$section->addInput(new Form_Input(
931
		'descr',
932
		'*Descriptive name',
933
		'text',
934
		$pconfig['descr']
935
	));
936

    
937
	$section->addInput(new Form_Textarea(
938
		'csr',
939
		'Signing request data',
940
		$pconfig['csr']
941
	))->setReadonly()
942
	  ->setWidth(7)
943
	  ->setHelp('Copy the certificate signing data from here and forward it to a certificate authority for signing.');
944

    
945
	$section->addInput(new Form_Textarea(
946
		'cert',
947
		'*Final certificate data',
948
		$pconfig['cert']
949
	))->setWidth(7)
950
	  ->setHelp('Paste the certificate received from the certificate authority here.');
951

    
952
	 if (isset($id) && $a_cert[$id]) {
953
		 $section->addInput(new Form_Input(
954
			'id',
955
			null,
956
			'hidden',
957
			$id
958
		 ));
959

    
960
		 $section->addInput(new Form_Input(
961
			'act',
962
			null,
963
			'hidden',
964
			'csr'
965
		 ));
966
	 }
967

    
968
	$form->add($section);
969

    
970
	$form->addGlobal(new Form_Button(
971
		'save',
972
		'Update',
973
		null,
974
		'fa-save'
975
	))->addClass('btn-primary');
976

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

    
992
					<th class="col-sm-2"><?=gettext("Actions")?></th>
993
				</tr>
994
			</thead>
995
			<tbody>
996
<?php
997

    
998
$pluginparams = array();
999
$pluginparams['type'] = 'certificates';
1000
$pluginparams['event'] = 'used_certificates';
1001
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
1002
$i = 0;
1003
foreach ($a_cert as $i => $cert):
1004
	$name = htmlspecialchars($cert['descr']);
1005

    
1006
	if ($cert['crt']) {
1007
		$subj = cert_get_subject($cert['crt']);
1008
		$issuer = cert_get_issuer($cert['crt']);
1009
		$purpose = cert_get_purpose($cert['crt']);
1010
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
1011

    
1012
		if ($subj == $issuer) {
1013
			$caname = '<i>'. gettext("self-signed") .'</i>';
1014
		} else {
1015
			$caname = '<i>'. gettext("external").'</i>';
1016
		}
1017

    
1018
		$subj = htmlspecialchars($subj);
1019
	} else {
1020
		$subj = "";
1021
		$issuer = "";
1022
		$purpose = "";
1023
		$startdate = "";
1024
		$enddate = "";
1025
		$caname = "<em>" . gettext("private key only") . "</em>";
1026
	}
1027

    
1028
	if ($cert['csr']) {
1029
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
1030
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
1031
	}
1032

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

    
1124
<nav class="action-buttons">
1125
	<a href="?act=new" class="btn btn-success btn-sm">
1126
		<i class="fa fa-plus icon-embed-btn"></i>
1127
		<?=gettext("Add")?>
1128
	</a>
1129
</nav>
1130
<?php
1131
	include("foot.inc");
1132
	exit;
1133
}
1134

    
1135

    
1136
?>
1137
<script type="text/javascript">
1138
//<![CDATA[
1139
events.push(function() {
1140

    
1141
<?php if ($internal_ca_count): ?>
1142
	function internalca_change() {
1143

    
1144
		caref = $('#caref').val();
1145

    
1146
		switch (caref) {
1147
<?php
1148
			foreach ($a_ca as $ca):
1149
				if (!$ca['prv']) {
1150
					continue;
1151
				}
1152

    
1153
				$subject = cert_get_subject_array($ca['crt']);
1154

    
1155
?>
1156
				case "<?=$ca['refid'];?>":
1157
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1158
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1159
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1160
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1161
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1162
					$('#dn_organizationalunit').val("<?=$subject[6]['v'];?>");
1163
					break;
1164
<?php
1165
			endforeach;
1166
?>
1167
		}
1168
	}
1169

    
1170
	// ---------- Click checkbox handlers ---------------------------------------------------------
1171

    
1172
	$('#caref').on('change', function() {
1173
		internalca_change();
1174
	});
1175

    
1176
	// ---------- On initial page load ------------------------------------------------------------
1177

    
1178
	internalca_change();
1179

    
1180
	// Suppress "Delete row" button if there are fewer than two rows
1181
	checkLastRow();
1182

    
1183
<?php endif; ?>
1184

    
1185

    
1186
});
1187
//]]>
1188
</script>
1189
<?php
1190
include('foot.inc');
(193-193/225)