Project

General

Profile

Download (32.5 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
	"ca" => "Certificate Authority",
76
	"server" => "Server Certificate",
77
	"user" => "User Certificate");
78

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

    
82
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
83

    
84
if (is_numericint($_GET['userid'])) {
85
	$userid = $_GET['userid'];
86
}
87
if (isset($_POST['userid']) && is_numericint($_POST['userid'])) {
88
	$userid = $_POST['userid'];
89
}
90

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

    
99
if (is_numericint($_GET['id'])) {
100
	$id = $_GET['id'];
101
}
102
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
103
	$id = $_POST['id'];
104
}
105

    
106
if (!is_array($config['ca'])) {
107
	$config['ca'] = array();
108
}
109

    
110
$a_ca =& $config['ca'];
111

    
112
if (!is_array($config['cert'])) {
113
	$config['cert'] = array();
114
}
115

    
116
$a_cert =& $config['cert'];
117

    
118
$internal_ca_count = 0;
119
foreach ($a_ca as $ca) {
120
	if ($ca['prv']) {
121
		$internal_ca_count++;
122
	}
123
}
124

    
125
$act = $_GET['act'];
126

    
127
if ($_POST['act']) {
128
	$act = $_POST['act'];
129
}
130

    
131
if ($act == "del") {
132

    
133
	if (!isset($a_cert[$id])) {
134
		pfSenseHeader("system_certmanager.php");
135
		exit;
136
	}
137

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

    
145

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

    
156
if ($act == "exp") {
157

    
158
	if (!$a_cert[$id]) {
159
		pfSenseHeader("system_certmanager.php");
160
		exit;
161
	}
162

    
163
	$exp_name = urlencode("{$a_cert[$id]['descr']}.crt");
164
	$exp_data = base64_decode($a_cert[$id]['crt']);
165
	$exp_size = strlen($exp_data);
166

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

    
174
if ($act == "req") {
175

    
176
	if (!$a_cert[$id]) {
177
		pfSenseHeader("system_certmanager.php");
178
		exit;
179
	}
180

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

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

    
192
if ($act == "key") {
193

    
194
	if (!$a_cert[$id]) {
195
		pfSenseHeader("system_certmanager.php");
196
		exit;
197
	}
198

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

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

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

    
216
	$exp_name = urlencode("{$a_cert[$id]['descr']}.p12");
217
	$args = array();
218
	$args['friendly_name'] = $a_cert[$id]['descr'];
219

    
220
	$ca = lookup_ca($a_cert[$id]['caref']);
221
	if ($ca) {
222
		$args['extracerts'] = openssl_x509_read(base64_decode($ca['crt']));
223
	}
224

    
225
	$res_crt = openssl_x509_read(base64_decode($a_cert[$id]['crt']));
226
	$res_key = openssl_pkey_get_private(array(0 => base64_decode($a_cert[$id]['prv']) , 1 => ""));
227

    
228
	$exp_data = "";
229
	openssl_pkcs12_export($res_crt, $exp_data, $res_key, null, $args);
230
	$exp_size = strlen($exp_data);
231

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

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

    
245
	$pconfig['descr'] = $a_cert[$id]['descr'];
246
	$pconfig['csr'] = base64_decode($a_cert[$id]['csr']);
247
}
248

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

    
256
	if ($_POST['save'] == gettext("Save")) {
257
		$input_errors = array();
258
		$pconfig = $_POST;
259

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

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

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

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

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

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

    
331
			$pconfig['altnames']['item'] = $altnames;
332

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

    
365
			/* Make sure we do not have invalid characters in the fields for the certificate */
366

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

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

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

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

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

    
406
		/* save modifications */
407
		if (!$input_errors) {
408

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

    
421
				$cert['descr'] = $pconfig['descr'];
422

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

    
425
				if ($pconfig['method'] == "import") {
426
					cert_import($cert, $pconfig['cert'], $pconfig['key']);
427
				}
428

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

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

    
444
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
445
					}
446

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

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

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

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

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

    
491
			if (!$input_errors) {
492
				write_config();
493
			}
494

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

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

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

    
512
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
513

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

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

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

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

    
544
		/* save modifications */
545
		if (!$input_errors) {
546

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

    
549
			$cert['descr'] = $pconfig['descr'];
550

    
551
			csr_complete($cert, $pconfig['cert']);
552

    
553
			$a_cert[$id] = $cert;
554

    
555
			write_config();
556

    
557
			pfSenseHeader("system_certmanager.php");
558
		}
559
	}
560
}
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

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

    
595
	$section = new Form_Section('Complete Signing Request');
596

    
597
	if (isset($id) && $a_cert[$id]) {
598
		$form->addGlobal(new Form_Input(
599
			'id',
600
			null,
601
			'hidden',
602
			$id
603
		));
604
	}
605

    
606
	$section->addInput(new Form_Input(
607
		'descr',
608
		'Descriptive name',
609
		'text',
610
		$pconfig['descr']
611
	));
612

    
613
	$section->addInput(new Form_Textarea(
614
		'csr',
615
		'Signing request data',
616
		$pconfig['csr']
617
	))->setReadonly()->setHelp('Copy the certificate signing data from here and '.
618
		'forward it to your certificate authority for signing.');
619

    
620
	$section->addInput(new Form_Textarea(
621
		'cert',
622
		'Final certificate data',
623
		$pconfig['cert']
624
	))->setHelp('Paste the certificate received from your certificate authority here.');
625

    
626
	$form->add($section);
627
	print $form;
628

    
629
	include("foot.inc");
630
	exit;
631
}
632

    
633
$form->setAction('system_certmanager.php?act=edit');
634

    
635
if (isset($userid) && $a_user) {
636
	$form->addGlobal(new Form_Input(
637
		'userid',
638
		null,
639
		'hidden',
640
		$userid
641
	));
642
}
643

    
644
if (isset($id) && $a_cert[$id]) {
645
	$form->addGlobal(new Form_Input(
646
		'id',
647
		null,
648
		'hidden',
649
		$id
650
	));
651
}
652

    
653
$section = new Form_Section('Add a New Certificate');
654

    
655
if (!isset($id)) {
656
	$section->addInput(new Form_Select(
657
		'method',
658
		'Method',
659
		$pconfig['method'],
660
		$cert_methods
661
	))->toggles();
662
}
663

    
664
$section->addInput(new Form_Input(
665
	'descr',
666
	'Descriptive name',
667
	'text',
668
	($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
669
))->addClass('toggle-existing');
670

    
671
$form->add($section);
672
$section = new Form_Section('Import Certificate');
673
$section->addClass('toggle-import collapse');
674

    
675
$section->addInput(new Form_Textarea(
676
	'cert',
677
	'Certificate data',
678
	$pconfig['cert']
679
))->setHelp('Paste a certificate in X.509 PEM format here.');
680

    
681
$section->addInput(new Form_Textarea(
682
	'key',
683
	'Private key data',
684
	$pconfig['key']
685
))->setHelp('Paste a private key in X.509 PEM format here.');
686

    
687
$form->add($section);
688
$section = new Form_Section('Internal Certificate');
689
$section->addClass('toggle-internal collapse');
690

    
691
if (!$internal_ca_count) {
692
	$section->addInput(new Form_StaticText(
693
		'Certificate authority',
694
		gettext('No internal Certificate Authorities have been defined. You must ').
695
		'<a href="system_camanager.php?act=new&amp;method=internal"> '. gettext(" create") .'</a>'.
696
		gettext(' an internal CA before creating an internal certificate.')
697
	));
698
} else {
699
	$allCas = array();
700
	foreach ($a_ca as $ca) {
701
		if (!$ca['prv']) {
702
			continue;
703
		}
704

    
705
		$allCas[ $ca['refid'] ] = $ca['descr'];
706
	}
707

    
708
	$section->addInput(new Form_Select(
709
		'caref',
710
		'Certificate authority',
711
		$pconfig['caref'],
712
		$allCas
713
	));
714
}
715

    
716
$section->addInput(new Form_Select(
717
	'keylen',
718
	'Key length',
719
	$pconfig['keylen'],
720
	array_combine($cert_keylens, $cert_keylens)
721
));
722

    
723
$section->addInput(new Form_Select(
724
	'digest_alg',
725
	'Digest Algorithm',
726
	$pconfig['digest_alg'],
727
	array_combine($openssl_digest_algs, $openssl_digest_algs)
728
))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
729
	'SHA1 when possible.');
730

    
731
$section->addInput(new Form_Select(
732
	'type',
733
	'Certificate Type',
734
	$pconfig['type'],
735
	$cert_types
736
))->setHelp('Type of certificate to generate. Used for placing '.
737
	'restrictions on the usage of the generated certificate.');
738

    
739
$section->addInput(new Form_Input(
740
	'lifetime',
741
	'Lifetime (days)',
742
	'number',
743
	$pconfig['lifetime']
744
));
745

    
746
$section->addInput(new Form_Select(
747
	'dn_country',
748
	'Country Code',
749
	$pconfig['dn_country'],
750
	$dn_cc
751
));
752

    
753
$section->addInput(new Form_Input(
754
	'dn_state',
755
	'State or Province',
756
	'text',
757
	$pconfig['dn_state'],
758
	['placeholder' => 'e.g. Texas']
759
));
760

    
761
$section->addInput(new Form_Input(
762
	'dn_city',
763
	'City',
764
	'text',
765
	$pconfig['dn_city'],
766
	['placeholder' => 'e.g. Austin']
767
));
768

    
769
$section->addInput(new Form_Input(
770
	'dn_organization',
771
	'Organization',
772
	'text',
773
	$pconfig['dn_organization'],
774
	['placeholder' => 'e.g. My Company Inc.']
775
));
776

    
777
$section->addInput(new Form_Input(
778
	'dn_email',
779
	'Email Address',
780
	'text',
781
	$pconfig['dn_email'],
782
	['placeholder' => 'e.g. admin@mycompany.com']
783
));
784

    
785
$section->addInput(new Form_Input(
786
	'dn_commonname',
787
	'Common Name',
788
	'text',
789
	$pconfig['dn_commonname'],
790
	['placeholder' => 'e.g. www.example.com']
791
));
792

    
793
if (empty($pconfig['altnames']['item'])) {
794
	$pconfig['altnames']['item'] = array(
795
		array('type' => null, 'value' => null)
796
	);
797
}
798

    
799
$counter = 0;
800
$numrows = count($pconfig['altnames']['item']) - 1;
801

    
802
foreach ($pconfig['altnames']['item'] as $item) {
803

    
804
	$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
805

    
806
	$group->add(new Form_Select(
807
		'altname_type' . $counter,
808
		'Type',
809
		$item['type'],
810
		array(
811
			'DNS' => gettext('FQDN or Hostname'),
812
			'IP' => gettext('IP address'),
813
			'URI' => gettext('URI'),
814
			'email' => gettext('email address'),
815
		)
816
	))->setHelp(($counter == $numrows) ? 'Type':null);
817

    
818
	$group->add(new Form_Input(
819
		'altname_value' . $counter,
820
		null,
821
		'text',
822
		$item['value']
823
	))->setHelp(($counter == $numrows) ? 'Value':null);
824

    
825
	$group->add(new Form_Button(
826
		'deleterow' . $counter,
827
		'Delete'
828
	))->removeClass('btn-primary')->addClass('btn-warning');
829

    
830
	$group->addClass('repeatable');
831

    
832
	$section->add($group);
833

    
834
	$counter++;
835
}
836

    
837
$section->addInput(new Form_Button(
838
	'addrow',
839
	'Add'
840
))->removeClass('btn-primary')->addClass('btn-success');
841

    
842
$form->add($section);
843
$section = new Form_Section('External Signing Request');
844
$section->addClass('toggle-external collapse');
845

    
846
$section->addInput(new Form_Select(
847
	'csr_keylen',
848
	'Key length',
849
	$pconfig['csr_keylen'],
850
	array_combine($cert_keylens, $cert_keylens)
851
));
852

    
853
$section->addInput(new Form_Select(
854
	'csr_digest_alg',
855
	'Digest Algorithm',
856
	$pconfig['csr_digest_alg'],
857
	array_combine($openssl_digest_algs, $openssl_digest_algs)
858
))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
859
	'SHA1 when possible');
860

    
861
$section->addInput(new Form_Select(
862
	'csr_dn_country',
863
	'Country Code',
864
	$pconfig['csr_dn_country'],
865
	$dn_cc
866
));
867

    
868
$section->addInput(new Form_Input(
869
	'csr_dn_state',
870
	'State or Province',
871
	'text',
872
	$pconfig['csr_dn_state'],
873
	['placeholder' => 'e.g. Texas']
874
));
875

    
876
$section->addInput(new Form_Input(
877
	'csr_dn_city',
878
	'City',
879
	'text',
880
	$pconfig['csr_dn_city'],
881
	['placeholder' => 'e.g. Austin']
882
));
883

    
884
$section->addInput(new Form_Input(
885
	'csr_dn_organization',
886
	'Organization',
887
	'text',
888
	$pconfig['csr_dn_organization'],
889
	['placeholder' => 'e.g. My Company Inc.']
890
));
891

    
892
$section->addInput(new Form_Input(
893
	'csr_dn_email',
894
	'Email Address',
895
	'text',
896
	$pconfig['csr_dn_email'],
897
	['placeholder' => 'e.g. admin@mycompany.com']
898
));
899

    
900
$section->addInput(new Form_Input(
901
	'csr_dn_commonname',
902
	'Common Name',
903
	'text',
904
	$pconfig['csr_dn_commonname'],
905
	['placeholder' => 'e.g. internal-ca']
906
));
907

    
908
$form->add($section);
909
$section = new Form_Section('Choose an Existing Certificate');
910
$section->addClass('toggle-existing collapse');
911

    
912
$existCerts = array();
913

    
914
foreach ($config['cert'] as $cert)	{
915
	if (is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
916
		if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert'])) {
917
			continue;
918
		}
919
	}
920

    
921
	$ca = lookup_ca($cert['caref']);
922
	if ($ca) {
923
		$cert['descr'] .= " (CA: {$ca['descr']})";
924
	}
925

    
926
	if (cert_in_use($cert['refid'])) {
927
		$cert['descr'] .= " <i>In Use</i>";
928
	}
929
	if (is_cert_revoked($cert)) {
930
		$cert['descr'] .= " <b>Revoked</b>";
931
	}
932

    
933
	$existCerts[ $cert['refid'] ] = $cert['descr'];
934
}
935

    
936

    
937
$section->addInput(new Form_Select(
938
	'certref',
939
	'Existing Certificates',
940
	$pconfig['certref'],
941
	$existCerts
942
));
943

    
944
$form->add($section);
945
print $form;
946

    
947
} else if ($act == "csr" || (($_POST['save'] == gettext("Update")) && $input_errors)) {
948
	$form = new Form(new Form_Button(
949
		'save',
950
		'Update'
951
	));
952

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

    
955
	$section->addInput(new Form_Input(
956
		'descr',
957
		'Descriptive name',
958
		'text',
959
		$pconfig['descr']
960
	));
961

    
962
	$section->addInput(new Form_Textarea(
963
		'csr',
964
		'Signing request data',
965
		$pconfig['csr']
966
	))->setReadonly()
967
	  ->setWidth(7)
968
	  ->setHelp('Copy the certificate signing data from here and forward it to your certificate authority for signing.');
969

    
970
	$section->addInput(new Form_Textarea(
971
		'cert',
972
		'Final certificate data',
973
		$pconfig['cert']
974
	))->setWidth(7)
975
	  ->setHelp('Paste the certificate received from your certificate authority here.');
976

    
977
	 if (isset($id) && $a_cert[$id]) {
978
		 $section->addInput(new Form_Input(
979
			'id',
980
			null,
981
			'hidden',
982
			$id
983
		 ));
984

    
985
		 $section->addInput(new Form_Input(
986
			'act',
987
			null,
988
			'hidden',
989
			'csr'
990
		 ));
991
	 }
992

    
993
	$form->add($section);
994
	print($form);
995
} else {
996
?>
997
<div class="panel panel-default">
998
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificates')?></h2></div>
999
	<div class="panel-body">
1000
		<div class="table-responsive">
1001
		<table class="table table-striped table-hover">
1002
			<thead>
1003
				<tr>
1004
					<th><?=gettext("Name")?></th>
1005
					<th><?=gettext("Issuer")?></th>
1006
					<th><?=gettext("Distinguished Name")?></th>
1007
					<th><?=gettext("In Use")?></th>
1008

    
1009
					<th class="col-sm-2"><?=gettext("Actions")?></th>
1010
				</tr>
1011
			</thead>
1012
			<tbody>
1013
<?php
1014

    
1015
$pluginparams = array();
1016
$pluginparams['type'] = 'certificates';
1017
$pluginparams['event'] = 'used_certificates';
1018
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
1019
$i = 0;
1020
foreach ($a_cert as $i => $cert):
1021
	$name = htmlspecialchars($cert['descr']);
1022

    
1023
	if ($cert['crt']) {
1024
		$subj = cert_get_subject($cert['crt']);
1025
		$issuer = cert_get_issuer($cert['crt']);
1026
		$purpose = cert_get_purpose($cert['crt']);
1027
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
1028

    
1029
		if ($subj == $issuer) {
1030
			$caname = '<i>'. gettext("self-signed") .'</i>';
1031
		} else {
1032
			$caname = '<i>'. gettext("external").'</i>';
1033
		}
1034

    
1035
		$subj = htmlspecialchars($subj);
1036
	}
1037

    
1038
	if ($cert['csr']) {
1039
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
1040
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
1041
	}
1042

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

    
1134
<nav class="action-buttons">
1135
	<a href="?act=new" class="btn btn-success btn-sm">
1136
		<i class="fa fa-plus icon-embed-btn"></i>
1137
		<?=gettext("Add")?>
1138
	</a>
1139
</nav>
1140
<?php
1141
	include("foot.inc");
1142
	exit;
1143
}
1144

    
1145

    
1146
?>
1147
<script type="text/javascript">
1148
//<![CDATA[
1149
events.push(function() {
1150

    
1151
<?php if ($internal_ca_count): ?>
1152
	function internalca_change() {
1153

    
1154
		caref = $('#caref').val();
1155

    
1156
		switch (caref) {
1157
<?php
1158
			foreach ($a_ca as $ca):
1159
				if (!$ca['prv']) {
1160
					continue;
1161
				}
1162

    
1163
				$subject = cert_get_subject_array($ca['crt']);
1164

    
1165
?>
1166
				case "<?=$ca['refid'];?>":
1167
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1168
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1169
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1170
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1171
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1172
					break;
1173
<?php
1174
			endforeach;
1175
?>
1176
		}
1177
	}
1178

    
1179
	// ---------- Click checkbox handlers ---------------------------------------------------------
1180

    
1181
	$('#caref').on('change', function() {
1182
		internalca_change();
1183
	});
1184

    
1185
	// ---------- On initial page load ------------------------------------------------------------
1186

    
1187
	internalca_change();
1188

    
1189
	// Suppress "Delete row" button if there are fewer than two rows
1190
	checkLastRow();
1191

    
1192
<?php endif; ?>
1193

    
1194

    
1195
});
1196
//]]>
1197
</script>
1198
<?php
1199
include('foot.inc');
(196-196/229)