Project

General

Profile

Download (31.9 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 Electric Sheep Fencing, LLC
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

    
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

    
460
					if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) {
461
						while ($ssl_err = openssl_error_string()) {
462
							$input_errors = array();
463
							array_push($input_errors, "openssl library returns: " . $ssl_err);
464
						}
465
					}
466
				}
467
				error_reporting($old_err_level);
468

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

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

    
480
			if (!$input_errors) {
481
				write_config();
482
			}
483

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

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

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

    
501
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
502

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

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

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

    
527
		/* save modifications */
528
		if (!$input_errors) {
529

    
530
			$cert = $a_cert[$id];
531

    
532
			$cert['descr'] = $pconfig['descr'];
533

    
534
			csr_complete($cert, $pconfig['cert']);
535

    
536
			$a_cert[$id] = $cert;
537

    
538
			write_config();
539

    
540
			pfSenseHeader("system_certmanager.php");
541
		}
542
	}
543
}
544

    
545
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
546

    
547
if (($act == "new" || ($_POST['save'] == gettext("Save") && $input_errors)) || ($act == "csr" || ($_POST['save'] == gettext("Update") && $input_errors))) {
548
	$pgtitle[] = gettext('Edit');
549
}
550
include("head.inc");
551

    
552
if ($input_errors) {
553
	print_input_errors($input_errors);
554
}
555

    
556
if ($savemsg) {
557
	print_info_box($savemsg, 'success');
558
}
559

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

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

    
577
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
578
	$form = new Form();
579
	$form->setAction('system_certmanager.php?act=edit');
580

    
581
	if (isset($userid) && $a_user) {
582
		$form->addGlobal(new Form_Input(
583
			'userid',
584
			null,
585
			'hidden',
586
			$userid
587
		));
588
	}
589

    
590
	if (isset($id) && $a_cert[$id]) {
591
		$form->addGlobal(new Form_Input(
592
			'id',
593
			null,
594
			'hidden',
595
			$id
596
		));
597
	}
598

    
599
	$section = new Form_Section('Add a New Certificate');
600

    
601
	if (!isset($id)) {
602
		$section->addInput(new Form_Select(
603
			'method',
604
			'Method',
605
			$pconfig['method'],
606
			$cert_methods
607
		))->toggles();
608
	}
609

    
610
	$section->addInput(new Form_Input(
611
		'descr',
612
		'Descriptive name',
613
		'text',
614
		($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
615
	))->addClass('toggle-existing');
616

    
617
	$form->add($section);
618
	$section = new Form_Section('Import Certificate');
619
	$section->addClass('toggle-import collapse');
620

    
621
	$section->addInput(new Form_Textarea(
622
		'cert',
623
		'Certificate data',
624
		$pconfig['cert']
625
	))->setHelp('Paste a certificate in X.509 PEM format here.');
626

    
627
	$section->addInput(new Form_Textarea(
628
		'key',
629
		'Private key data',
630
		$pconfig['key']
631
	))->setHelp('Paste a private key in X.509 PEM format here.');
632

    
633
	$form->add($section);
634
	$section = new Form_Section('Internal Certificate');
635
	$section->addClass('toggle-internal collapse');
636

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

    
652
			$allCas[ $ca['refid'] ] = $ca['descr'];
653
		}
654

    
655
		$section->addInput(new Form_Select(
656
			'caref',
657
			'Certificate authority',
658
			$pconfig['caref'],
659
			$allCas
660
		));
661
	}
662

    
663
	$section->addInput(new Form_Select(
664
		'keylen',
665
		'Key length',
666
		$pconfig['keylen'],
667
		array_combine($cert_keylens, $cert_keylens)
668
	));
669

    
670
	$section->addInput(new Form_Select(
671
		'digest_alg',
672
		'Digest Algorithm',
673
		$pconfig['digest_alg'],
674
		array_combine($openssl_digest_algs, $openssl_digest_algs)
675
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
676
		'SHA1 when possible.');
677

    
678
	$section->addInput(new Form_Select(
679
		'type',
680
		'Certificate Type',
681
		$pconfig['type'],
682
		$cert_types
683
	))->setHelp('Type of certificate to generate. Used for placing '.
684
		'restrictions on the usage of the generated certificate.');
685

    
686
	$section->addInput(new Form_Input(
687
		'lifetime',
688
		'Lifetime (days)',
689
		'number',
690
		$pconfig['lifetime']
691
	));
692

    
693
	$section->addInput(new Form_Select(
694
		'dn_country',
695
		'Country Code',
696
		$pconfig['dn_country'],
697
		$dn_cc
698
	));
699

    
700
	$section->addInput(new Form_Input(
701
		'dn_state',
702
		'State or Province',
703
		'text',
704
		$pconfig['dn_state'],
705
		['placeholder' => 'e.g. Texas']
706
	));
707

    
708
	$section->addInput(new Form_Input(
709
		'dn_city',
710
		'City',
711
		'text',
712
		$pconfig['dn_city'],
713
		['placeholder' => 'e.g. Austin']
714
	));
715

    
716
	$section->addInput(new Form_Input(
717
		'dn_organization',
718
		'Organization',
719
		'text',
720
		$pconfig['dn_organization'],
721
		['placeholder' => 'e.g. My Company Inc.']
722
	));
723

    
724
	$section->addInput(new Form_Input(
725
		'dn_email',
726
		'Email Address',
727
		'text',
728
		$pconfig['dn_email'],
729
		['placeholder' => 'e.g. admin@mycompany.com']
730
	));
731

    
732
	$section->addInput(new Form_Input(
733
		'dn_commonname',
734
		'Common Name',
735
		'text',
736
		$pconfig['dn_commonname'],
737
		['placeholder' => 'e.g. www.example.com']
738
	));
739

    
740
	if (empty($pconfig['altnames']['item'])) {
741
		$pconfig['altnames']['item'] = array(
742
			array('type' => null, 'value' => null)
743
		);
744
	}
745

    
746
	$counter = 0;
747
	$numrows = count($pconfig['altnames']['item']) - 1;
748

    
749
	foreach ($pconfig['altnames']['item'] as $item) {
750

    
751
		$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
752

    
753
		$group->add(new Form_Select(
754
			'altname_type' . $counter,
755
			'Type',
756
			$item['type'],
757
			array(
758
				'DNS' => gettext('FQDN or Hostname'),
759
				'IP' => gettext('IP address'),
760
				'URI' => gettext('URI'),
761
				'email' => gettext('email address'),
762
			)
763
		))->setHelp(($counter == $numrows) ? 'Type':null);
764

    
765
		$group->add(new Form_Input(
766
			'altname_value' . $counter,
767
			null,
768
			'text',
769
			$item['value']
770
		))->setHelp(($counter == $numrows) ? 'Value':null);
771

    
772
		$group->add(new Form_Button(
773
			'deleterow' . $counter,
774
			'Delete',
775
			null,
776
			'fa-trash'
777
		))->addClass('btn-warning');
778

    
779
		$group->addClass('repeatable');
780

    
781
		$section->add($group);
782

    
783
		$counter++;
784
	}
785

    
786
	$section->addInput(new Form_Button(
787
		'addrow',
788
		'Add',
789
		null,
790
		'fa-plus'
791
	))->addClass('btn-success');
792

    
793
	$form->add($section);
794
	$section = new Form_Section('External Signing Request');
795
	$section->addClass('toggle-external collapse');
796

    
797
	$section->addInput(new Form_Select(
798
		'csr_keylen',
799
		'Key length',
800
		$pconfig['csr_keylen'],
801
		array_combine($cert_keylens, $cert_keylens)
802
	));
803

    
804
	$section->addInput(new Form_Select(
805
		'csr_digest_alg',
806
		'Digest Algorithm',
807
		$pconfig['csr_digest_alg'],
808
		array_combine($openssl_digest_algs, $openssl_digest_algs)
809
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
810
		'SHA1 when possible');
811

    
812
	$section->addInput(new Form_Select(
813
		'csr_dn_country',
814
		'Country Code',
815
		$pconfig['csr_dn_country'],
816
		$dn_cc
817
	));
818

    
819
	$section->addInput(new Form_Input(
820
		'csr_dn_state',
821
		'State or Province',
822
		'text',
823
		$pconfig['csr_dn_state'],
824
		['placeholder' => 'e.g. Texas']
825
	));
826

    
827
	$section->addInput(new Form_Input(
828
		'csr_dn_city',
829
		'City',
830
		'text',
831
		$pconfig['csr_dn_city'],
832
		['placeholder' => 'e.g. Austin']
833
	));
834

    
835
	$section->addInput(new Form_Input(
836
		'csr_dn_organization',
837
		'Organization',
838
		'text',
839
		$pconfig['csr_dn_organization'],
840
		['placeholder' => 'e.g. My Company Inc.']
841
	));
842

    
843
	$section->addInput(new Form_Input(
844
		'csr_dn_email',
845
		'Email Address',
846
		'text',
847
		$pconfig['csr_dn_email'],
848
		['placeholder' => 'e.g. admin@mycompany.com']
849
	));
850

    
851
	$section->addInput(new Form_Input(
852
		'csr_dn_commonname',
853
		'Common Name',
854
		'text',
855
		$pconfig['csr_dn_commonname'],
856
		['placeholder' => 'e.g. internal-ca']
857
	));
858

    
859
	$form->add($section);
860
	$section = new Form_Section('Choose an Existing Certificate');
861
	$section->addClass('toggle-existing collapse');
862

    
863
	$existCerts = array();
864

    
865
	foreach ($config['cert'] as $cert)	{
866
		if (is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
867
			if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert'])) {
868
				continue;
869
			}
870
		}
871

    
872
		$ca = lookup_ca($cert['caref']);
873
		if ($ca) {
874
			$cert['descr'] .= " (CA: {$ca['descr']})";
875
		}
876

    
877
		if (cert_in_use($cert['refid'])) {
878
			$cert['descr'] .= " <i>In Use</i>";
879
		}
880
		if (is_cert_revoked($cert)) {
881
			$cert['descr'] .= " <b>Revoked</b>";
882
		}
883

    
884
		$existCerts[ $cert['refid'] ] = $cert['descr'];
885
	}
886

    
887
	$section->addInput(new Form_Select(
888
		'certref',
889
		'Existing Certificates',
890
		$pconfig['certref'],
891
		$existCerts
892
	));
893

    
894
	$form->add($section);
895
	print $form;
896

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

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

    
903
	$section->addInput(new Form_Input(
904
		'descr',
905
		'Descriptive name',
906
		'text',
907
		$pconfig['descr']
908
	));
909

    
910
	$section->addInput(new Form_Textarea(
911
		'csr',
912
		'Signing request data',
913
		$pconfig['csr']
914
	))->setReadonly()
915
	  ->setWidth(7)
916
	  ->setHelp('Copy the certificate signing data from here and forward it to a certificate authority for signing.');
917

    
918
	$section->addInput(new Form_Textarea(
919
		'cert',
920
		'Final certificate data',
921
		$pconfig['cert']
922
	))->setWidth(7)
923
	  ->setHelp('Paste the certificate received from the certificate authority here.');
924

    
925
	 if (isset($id) && $a_cert[$id]) {
926
		 $section->addInput(new Form_Input(
927
			'id',
928
			null,
929
			'hidden',
930
			$id
931
		 ));
932

    
933
		 $section->addInput(new Form_Input(
934
			'act',
935
			null,
936
			'hidden',
937
			'csr'
938
		 ));
939
	 }
940

    
941
	$form->add($section);
942

    
943
	$form->addGlobal(new Form_Button(
944
		'save',
945
		'Update',
946
		null,
947
		'fa-save'
948
	))->addClass('btn-primary');
949

    
950
	print($form);
951
} else {
952
?>
953
<div class="panel panel-default">
954
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificates')?></h2></div>
955
	<div class="panel-body">
956
		<div class="table-responsive">
957
		<table class="table table-striped table-hover">
958
			<thead>
959
				<tr>
960
					<th><?=gettext("Name")?></th>
961
					<th><?=gettext("Issuer")?></th>
962
					<th><?=gettext("Distinguished Name")?></th>
963
					<th><?=gettext("In Use")?></th>
964

    
965
					<th class="col-sm-2"><?=gettext("Actions")?></th>
966
				</tr>
967
			</thead>
968
			<tbody>
969
<?php
970

    
971
$pluginparams = array();
972
$pluginparams['type'] = 'certificates';
973
$pluginparams['event'] = 'used_certificates';
974
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
975
$i = 0;
976
foreach ($a_cert as $i => $cert):
977
	$name = htmlspecialchars($cert['descr']);
978

    
979
	if ($cert['crt']) {
980
		$subj = cert_get_subject($cert['crt']);
981
		$issuer = cert_get_issuer($cert['crt']);
982
		$purpose = cert_get_purpose($cert['crt']);
983
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
984

    
985
		if ($subj == $issuer) {
986
			$caname = '<i>'. gettext("self-signed") .'</i>';
987
		} else {
988
			$caname = '<i>'. gettext("external").'</i>';
989
		}
990

    
991
		$subj = htmlspecialchars($subj);
992
	}
993

    
994
	if ($cert['csr']) {
995
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
996
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
997
	}
998

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

    
1090
<nav class="action-buttons">
1091
	<a href="?act=new" class="btn btn-success btn-sm">
1092
		<i class="fa fa-plus icon-embed-btn"></i>
1093
		<?=gettext("Add")?>
1094
	</a>
1095
</nav>
1096
<?php
1097
	include("foot.inc");
1098
	exit;
1099
}
1100

    
1101

    
1102
?>
1103
<script type="text/javascript">
1104
//<![CDATA[
1105
events.push(function() {
1106

    
1107
<?php if ($internal_ca_count): ?>
1108
	function internalca_change() {
1109

    
1110
		caref = $('#caref').val();
1111

    
1112
		switch (caref) {
1113
<?php
1114
			foreach ($a_ca as $ca):
1115
				if (!$ca['prv']) {
1116
					continue;
1117
				}
1118

    
1119
				$subject = cert_get_subject_array($ca['crt']);
1120

    
1121
?>
1122
				case "<?=$ca['refid'];?>":
1123
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1124
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1125
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1126
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1127
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1128
					break;
1129
<?php
1130
			endforeach;
1131
?>
1132
		}
1133
	}
1134

    
1135
	// ---------- Click checkbox handlers ---------------------------------------------------------
1136

    
1137
	$('#caref').on('change', function() {
1138
		internalca_change();
1139
	});
1140

    
1141
	// ---------- On initial page load ------------------------------------------------------------
1142

    
1143
	internalca_change();
1144

    
1145
	// Suppress "Delete row" button if there are fewer than two rows
1146
	checkLastRow();
1147

    
1148
<?php endif; ?>
1149

    
1150

    
1151
});
1152
//]]>
1153
</script>
1154
<?php
1155
include('foot.inc');
(195-195/227)