Project

General

Profile

Download (32.2 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	system_certmanager.php
4
*/
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *	Copyright (c)  2008 Shrew Soft Inc.
8
 *
9
 *	Redistribution and use in source and binary forms, with or without modification,
10
 *	are permitted provided that the following conditions are met:
11
 *
12
 *	1. Redistributions of source code must retain the above copyright notice,
13
 *		this list of conditions and the following disclaimer.
14
 *
15
 *	2. Redistributions in binary form must reproduce the above copyright
16
 *		notice, this list of conditions and the following disclaimer in
17
 *		the documentation and/or other materials provided with the
18
 *		distribution.
19
 *
20
 *	3. All advertising materials mentioning features or use of this software
21
 *		must display the following acknowledgment:
22
 *		"This product includes software developed by the pfSense Project
23
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
24
 *
25
 *	4. The names "pfSense" and "pfSense Project" must not be used to
26
 *		 endorse or promote products derived from this software without
27
 *		 prior written permission. For written permission, please contact
28
 *		 coreteam@pfsense.org.
29
 *
30
 *	5. Products derived from this software may not be called "pfSense"
31
 *		nor may "pfSense" appear in their names without prior written
32
 *		permission of the Electric Sheep Fencing, LLC.
33
 *
34
 *	6. Redistributions of any form whatsoever must retain the following
35
 *		acknowledgment:
36
 *
37
 *	"This product includes software developed by the pfSense Project
38
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
39
 *
40
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
41
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
44
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
52
 *
53
 *	====================================================================
54
 *
55
 */
56

    
57
##|+PRIV
58
##|*IDENT=page-system-certmanager
59
##|*NAME=System: Certificate Manager
60
##|*DESCR=Allow access to the 'System: Certificate Manager' page.
61
##|*MATCH=system_certmanager.php*
62
##|-PRIV
63

    
64
require("guiconfig.inc");
65
require_once("certs.inc");
66

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

    
73
$cert_keylens = array("512", "1024", "2048", "4096");
74
$cert_types = array(
75
	"server" => "Server Certificate",
76
	"user" => "User Certificate");
77

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

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

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

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

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

    
107
$a_ca =& $config['ca'];
108

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

    
113
$a_cert =& $config['cert'];
114

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

    
122
$act = $_GET['act'];
123

    
124
if ($_POST['act']) {
125
	$act = $_POST['act'];
126
}
127

    
128
if ($act == "del") {
129

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

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

    
142

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

    
153
if ($act == "exp") {
154

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

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

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

    
171
if ($act == "req") {
172

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

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

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

    
189
if ($act == "key") {
190

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
328
			$pconfig['altnames']['item'] = $altnames;
329

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

    
362
			/* Make sure we do not have invalid characters in the fields for the certificate */
363

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

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

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

    
389
			if (($pconfig['method'] == "external") && isset($_POST["csr_keylen"]) && !in_array($_POST["csr_keylen"], $cert_keylens)) {
390
				array_push($input_errors, gettext("Please select a valid Key Length."));
391
			}
392
			if (($pconfig['method'] == "external") && !in_array($_POST["csr_digest_alg"], $openssl_digest_algs)) {
393
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
394
			}
395
		}
396

    
397
		/* if this is an AJAX caller then handle via JSON */
398
		if (isAjax() && is_array($input_errors)) {
399
			input_errors2Ajax($input_errors);
400
			exit;
401
		}
402

    
403
		/* save modifications */
404
		if (!$input_errors) {
405

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

    
418
				$cert['descr'] = $pconfig['descr'];
419

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
541
		/* save modifications */
542
		if (!$input_errors) {
543

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

    
546
			$cert['descr'] = $pconfig['descr'];
547

    
548
			csr_complete($cert, $pconfig['cert']);
549

    
550
			$a_cert[$id] = $cert;
551

    
552
			write_config();
553

    
554
			pfSenseHeader("system_certmanager.php");
555
		}
556
	}
557
}
558

    
559
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
560

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

    
566
if ($input_errors) {
567
	print_input_errors($input_errors);
568
}
569

    
570
if ($savemsg) {
571
	print_info_box($savemsg, 'success');
572
}
573

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

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

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

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

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

    
613
	$section = new Form_Section('Add a New Certificate');
614

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
796
		$counter++;
797
	}
798

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

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

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

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

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

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

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

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

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

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

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

    
876
	$existCerts = array();
877

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1114

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

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

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

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

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

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

    
1148
	// ---------- Click checkbox handlers ---------------------------------------------------------
1149

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

    
1154
	// ---------- On initial page load ------------------------------------------------------------
1155

    
1156
	internalca_change();
1157

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

    
1161
<?php endif; ?>
1162

    
1163

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