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_once("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, "An e-mail address must be provided 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. ').
655
			gettext('An internal CA must be defined in order to create an internal certificate. ').
656
			'<a href="system_camanager.php?act=new&amp;method=internal"> '. gettext("Create") .'</a>'.
657
			gettext(' an internal CA.')
658
		));
659
	} else {
660
		$allCas = array();
661
		foreach ($a_ca as $ca) {
662
			if (!$ca['prv']) {
663
				continue;
664
			}
665

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
797
		$counter++;
798
	}
799

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

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

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

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

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

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

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

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

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

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

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

    
877
	$existCerts = array();
878

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1115

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

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

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

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

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

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

    
1149
	// ---------- Click checkbox handlers ---------------------------------------------------------
1150

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

    
1155
	// ---------- On initial page load ------------------------------------------------------------
1156

    
1157
	internalca_change();
1158

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

    
1162
<?php endif; ?>
1163

    
1164

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