Project

General

Profile

Download (32.1 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", "4096");
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");
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
		/* if this is an AJAX caller then handle via JSON */
396
		if (isAjax() && is_array($input_errors)) {
397
			input_errors2Ajax($input_errors);
398
			exit;
399
		}
400

    
401
		/* save modifications */
402
		if (!$input_errors) {
403

    
404
			if ($pconfig['method'] == "existing") {
405
				$cert = lookup_cert($pconfig['certref']);
406
				if ($cert && $a_user) {
407
					$a_user[$userid]['cert'][] = $cert['refid'];
408
				}
409
			} else {
410
				$cert = array();
411
				$cert['refid'] = uniqid();
412
				if (isset($id) && $a_cert[$id]) {
413
					$cert = $a_cert[$id];
414
				}
415

    
416
				$cert['descr'] = $pconfig['descr'];
417

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

    
420
				if ($pconfig['method'] == "import") {
421
					cert_import($cert, $pconfig['cert'], $pconfig['key']);
422
				}
423

    
424
				if ($pconfig['method'] == "internal") {
425
					$dn = array(
426
						'countryName' => $pconfig['dn_country'],
427
						'stateOrProvinceName' => $pconfig['dn_state'],
428
						'localityName' => $pconfig['dn_city'],
429
						'organizationName' => $pconfig['dn_organization'],
430
						'emailAddress' => $pconfig['dn_email'],
431
						'commonName' => $pconfig['dn_commonname']);
432

    
433
					if (count($altnames)) {
434
						$altnames_tmp = "";
435
						foreach ($altnames as $altname) {
436
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
437
						}
438

    
439
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
440
					}
441

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

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

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

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

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

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

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

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

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

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

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

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

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

    
533
		/* if this is an AJAX caller then handle via JSON */
534
		if (isAjax() && is_array($input_errors)) {
535
			input_errors2Ajax($input_errors);
536
			exit;
537
		}
538

    
539
		/* save modifications */
540
		if (!$input_errors) {
541

    
542
			$cert = $a_cert[$id];
543

    
544
			$cert['descr'] = $pconfig['descr'];
545

    
546
			csr_complete($cert, $pconfig['cert']);
547

    
548
			$a_cert[$id] = $cert;
549

    
550
			write_config();
551

    
552
			pfSenseHeader("system_certmanager.php");
553
		}
554
	}
555
}
556

    
557
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
558

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
795
		$counter++;
796
	}
797

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

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

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

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

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

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

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

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

    
855
	$section->addInput(new Form_Input(
856
		'csr_dn_email',
857
		'Email Address',
858
		'text',
859
		$pconfig['csr_dn_email'],
860
		['placeholder' => 'e.g. admin@mycompany.com']
861
	));
862

    
863
	$section->addInput(new Form_Input(
864
		'csr_dn_commonname',
865
		'Common Name',
866
		'text',
867
		$pconfig['csr_dn_commonname'],
868
		['placeholder' => 'e.g. internal-ca']
869
	));
870

    
871
	$form->add($section);
872
	$section = new Form_Section('Choose an Existing Certificate');
873
	$section->addClass('toggle-existing collapse');
874

    
875
	$existCerts = array();
876

    
877
	foreach ($config['cert'] as $cert)	{
878
		if (is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
879
			if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert'])) {
880
				continue;
881
			}
882
		}
883

    
884
		$ca = lookup_ca($cert['caref']);
885
		if ($ca) {
886
			$cert['descr'] .= " (CA: {$ca['descr']})";
887
		}
888

    
889
		if (cert_in_use($cert['refid'])) {
890
			$cert['descr'] .= " <i>In Use</i>";
891
		}
892
		if (is_cert_revoked($cert)) {
893
			$cert['descr'] .= " <b>Revoked</b>";
894
		}
895

    
896
		$existCerts[ $cert['refid'] ] = $cert['descr'];
897
	}
898

    
899
	$section->addInput(new Form_Select(
900
		'certref',
901
		'Existing Certificates',
902
		$pconfig['certref'],
903
		$existCerts
904
	));
905

    
906
	$form->add($section);
907
	print $form;
908

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

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

    
915
	$section->addInput(new Form_Input(
916
		'descr',
917
		'Descriptive name',
918
		'text',
919
		$pconfig['descr']
920
	));
921

    
922
	$section->addInput(new Form_Textarea(
923
		'csr',
924
		'Signing request data',
925
		$pconfig['csr']
926
	))->setReadonly()
927
	  ->setWidth(7)
928
	  ->setHelp('Copy the certificate signing data from here and forward it to a certificate authority for signing.');
929

    
930
	$section->addInput(new Form_Textarea(
931
		'cert',
932
		'Final certificate data',
933
		$pconfig['cert']
934
	))->setWidth(7)
935
	  ->setHelp('Paste the certificate received from the certificate authority here.');
936

    
937
	 if (isset($id) && $a_cert[$id]) {
938
		 $section->addInput(new Form_Input(
939
			'id',
940
			null,
941
			'hidden',
942
			$id
943
		 ));
944

    
945
		 $section->addInput(new Form_Input(
946
			'act',
947
			null,
948
			'hidden',
949
			'csr'
950
		 ));
951
	 }
952

    
953
	$form->add($section);
954

    
955
	$form->addGlobal(new Form_Button(
956
		'save',
957
		'Update',
958
		null,
959
		'fa-save'
960
	))->addClass('btn-primary');
961

    
962
	print($form);
963
} else {
964
?>
965
<div class="panel panel-default">
966
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificates')?></h2></div>
967
	<div class="panel-body">
968
		<div class="table-responsive">
969
		<table class="table table-striped table-hover">
970
			<thead>
971
				<tr>
972
					<th><?=gettext("Name")?></th>
973
					<th><?=gettext("Issuer")?></th>
974
					<th><?=gettext("Distinguished Name")?></th>
975
					<th><?=gettext("In Use")?></th>
976

    
977
					<th class="col-sm-2"><?=gettext("Actions")?></th>
978
				</tr>
979
			</thead>
980
			<tbody>
981
<?php
982

    
983
$pluginparams = array();
984
$pluginparams['type'] = 'certificates';
985
$pluginparams['event'] = 'used_certificates';
986
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
987
$i = 0;
988
foreach ($a_cert as $i => $cert):
989
	$name = htmlspecialchars($cert['descr']);
990

    
991
	if ($cert['crt']) {
992
		$subj = cert_get_subject($cert['crt']);
993
		$issuer = cert_get_issuer($cert['crt']);
994
		$purpose = cert_get_purpose($cert['crt']);
995
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
996

    
997
		if ($subj == $issuer) {
998
			$caname = '<i>'. gettext("self-signed") .'</i>';
999
		} else {
1000
			$caname = '<i>'. gettext("external").'</i>';
1001
		}
1002

    
1003
		$subj = htmlspecialchars($subj);
1004
	}
1005

    
1006
	if ($cert['csr']) {
1007
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
1008
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
1009
	}
1010

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

    
1102
<nav class="action-buttons">
1103
	<a href="?act=new" class="btn btn-success btn-sm">
1104
		<i class="fa fa-plus icon-embed-btn"></i>
1105
		<?=gettext("Add")?>
1106
	</a>
1107
</nav>
1108
<?php
1109
	include("foot.inc");
1110
	exit;
1111
}
1112

    
1113

    
1114
?>
1115
<script type="text/javascript">
1116
//<![CDATA[
1117
events.push(function() {
1118

    
1119
<?php if ($internal_ca_count): ?>
1120
	function internalca_change() {
1121

    
1122
		caref = $('#caref').val();
1123

    
1124
		switch (caref) {
1125
<?php
1126
			foreach ($a_ca as $ca):
1127
				if (!$ca['prv']) {
1128
					continue;
1129
				}
1130

    
1131
				$subject = cert_get_subject_array($ca['crt']);
1132

    
1133
?>
1134
				case "<?=$ca['refid'];?>":
1135
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1136
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1137
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1138
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1139
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1140
					break;
1141
<?php
1142
			endforeach;
1143
?>
1144
		}
1145
	}
1146

    
1147
	// ---------- Click checkbox handlers ---------------------------------------------------------
1148

    
1149
	$('#caref').on('change', function() {
1150
		internalca_change();
1151
	});
1152

    
1153
	// ---------- On initial page load ------------------------------------------------------------
1154

    
1155
	internalca_change();
1156

    
1157
	// Suppress "Delete row" button if there are fewer than two rows
1158
	checkLastRow();
1159

    
1160
<?php endif; ?>
1161

    
1162

    
1163
});
1164
//]]>
1165
</script>
1166
<?php
1167
include('foot.inc');
(193-193/225)