Project

General

Profile

Download (32.7 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
if (is_numericint($_GET['userid'])) {
83
	$userid = $_GET['userid'];
84
}
85
if (isset($_POST['userid']) && is_numericint($_POST['userid'])) {
86
	$userid = $_POST['userid'];
87
}
88

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

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

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

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

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

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

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

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

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

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

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

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

    
143

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
553
			write_config();
554

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

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

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

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

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

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

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

    
592
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
593
$form = new Form;
594

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

    
598
	$section = new Form_Section('Complete Signing Request');
599

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

    
609
	$section->addInput(new Form_Input(
610
		'descr',
611
		'Descriptive name',
612
		'text',
613
		$pconfig['descr']
614
	));
615

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

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

    
629
	$form->add($section);
630
	print $form;
631

    
632
	include("foot.inc");
633
	exit;
634
}
635

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

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

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

    
656
$section = new Form_Section('Add a New Certificate');
657

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

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

    
674
$form->add($section);
675
$section = new Form_Section('Import Certificate');
676
$section->addClass('toggle-import collapse');
677

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

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

    
690
$form->add($section);
691
$section = new Form_Section('Internal Certificate');
692
$section->addClass('toggle-internal collapse');
693

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

    
708
		$allCas[ $ca['refid'] ] = $ca['descr'];
709
	}
710

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

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

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

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

    
742
$section->addInput(new Form_Input(
743
	'lifetime',
744
	'Lifetime (days)',
745
	'number',
746
	$pconfig['lifetime']
747
));
748

    
749
$section->addInput(new Form_Select(
750
	'dn_country',
751
	'Country Code',
752
	$pconfig['dn_country'],
753
	$dn_cc
754
));
755

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

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

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

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

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

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

    
802
$counter = 0;
803
$numrows = count($pconfig['altnames']['item']) - 1;
804

    
805
foreach ($pconfig['altnames']['item'] as $item) {
806

    
807
	$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
808

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

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

    
828
	$group->add(new Form_Button(
829
		'deleterow' . $counter,
830
		'Delete'
831
	))->removeClass('btn-primary')->addClass('btn-warning');
832

    
833
	$group->addClass('repeatable');
834

    
835
	$section->add($group);
836

    
837
	$counter++;
838
}
839

    
840
$section->addInput(new Form_Button(
841
	'addrow',
842
	'Add'
843
))->removeClass('btn-primary')->addClass('btn-success');
844

    
845
$form->add($section);
846
$section = new Form_Section('External Signing Request');
847
$section->addClass('toggle-external collapse');
848

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

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

    
864
$section->addInput(new Form_Select(
865
	'csr_dn_country',
866
	'Country Code',
867
	$pconfig['csr_dn_country'],
868
	$dn_cc
869
));
870

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

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

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

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

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

    
911
$form->add($section);
912
$section = new Form_Section('Choose an Existing Certificate');
913
$section->addClass('toggle-existing collapse');
914

    
915
$existCerts = array();
916

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

    
924
	$ca = lookup_ca($cert['caref']);
925
	if ($ca) {
926
		$cert['descr'] .= " (CA: {$ca['descr']})";
927
	}
928

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

    
936
	$existCerts[ $cert['refid'] ] = $cert['descr'];
937
}
938

    
939

    
940
$section->addInput(new Form_Select(
941
	'certref',
942
	'Existing Certificates',
943
	$pconfig['certref'],
944
	$existCerts
945
));
946

    
947
$form->add($section);
948
print $form;
949

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

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

    
958
	$section->addInput(new Form_Input(
959
		'descr',
960
		'Descriptive name',
961
		'text',
962
		$pconfig['descr']
963
	));
964

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

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

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

    
988
		 $section->addInput(new Form_Input(
989
			'act',
990
			null,
991
			'hidden',
992
			'csr'
993
		 ));
994
	 }
995

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

    
1012
					<th class="col-sm-2"><?=gettext("Actions")?></th>
1013
				</tr>
1014
			</thead>
1015
			<tbody>
1016
<?php
1017

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

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

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

    
1038
		$subj = htmlspecialchars($subj);
1039
	}
1040

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

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

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

    
1148

    
1149
?>
1150
<script type="text/javascript">
1151
//<![CDATA[
1152
events.push(function() {
1153

    
1154
<?php if ($internal_ca_count): ?>
1155
	function internalca_change() {
1156

    
1157
		caref = $('#caref').val();
1158

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

    
1166
				$subject = cert_get_subject_array($ca['crt']);
1167

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

    
1182
	// ---------- Click checkbox handlers ---------------------------------------------------------
1183

    
1184
	$('#caref').on('change', function() {
1185
		internalca_change();
1186
	});
1187

    
1188
	// ---------- On initial page load ------------------------------------------------------------
1189

    
1190
	internalca_change();
1191

    
1192
	// Suppress "Delete row" button if there are fewer than two rows
1193
	checkLastRow();
1194

    
1195
<?php endif; ?>
1196

    
1197

    
1198
});
1199
//]]>
1200
</script>
1201
<?php
1202
include('foot.inc');
(196-196/229)