Project

General

Profile

Download (35.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * system_certmanager.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
7
 * Copyright (c) 2008 Shrew Soft Inc
8
 * All rights reserved.
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 * http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22

    
23
##|+PRIV
24
##|*IDENT=page-system-certmanager
25
##|*NAME=System: Certificate Manager
26
##|*DESCR=Allow access to the 'System: Certificate Manager' page.
27
##|*MATCH=system_certmanager.php*
28
##|-PRIV
29

    
30
require_once("guiconfig.inc");
31
require_once("certs.inc");
32

    
33
$cert_methods = array(
34
	"import" => gettext("Import an existing Certificate"),
35
	"internal" => gettext("Create an internal Certificate"),
36
	"external" => gettext("Create a Certificate Signing Request"),
37
	"sign" => gettext("Sign a Certificate Signing Request")
38
);
39

    
40
$cert_keylens = array("512", "1024", "2048", "3072", "4096", "7680", "8192", "15360", "16384");
41
$cert_types = array(
42
	"server" => "Server Certificate",
43
	"user" => "User Certificate");
44

    
45
$altname_types = array("DNS", "IP", "email", "URI");
46
global $openssl_digest_algs;
47

    
48
if (isset($_REQUEST['userid']) && is_numericint($_REQUEST['userid'])) {
49
	$userid = $_REQUEST['userid'];
50
}
51

    
52
if (isset($userid)) {
53
	$cert_methods["existing"] = gettext("Choose an existing certificate");
54
	if (!is_array($config['system']['user'])) {
55
		$config['system']['user'] = array();
56
	}
57
	$a_user =& $config['system']['user'];
58
}
59

    
60
if (isset($_REQUEST['id']) && is_numericint($_REQUEST['id'])) {
61
	$id = $_REQUEST['id'];
62
}
63

    
64
if (!is_array($config['ca'])) {
65
	$config['ca'] = array();
66
}
67

    
68
$a_ca =& $config['ca'];
69

    
70
if (!is_array($config['cert'])) {
71
	$config['cert'] = array();
72
}
73

    
74
$a_cert =& $config['cert'];
75

    
76
$internal_ca_count = 0;
77
foreach ($a_ca as $ca) {
78
	if ($ca['prv']) {
79
		$internal_ca_count++;
80
	}
81
}
82

    
83
$act = $_REQUEST['act'];
84

    
85
if ($_POST['act'] == "del") {
86

    
87
	if (!isset($a_cert[$id])) {
88
		pfSenseHeader("system_certmanager.php");
89
		exit;
90
	}
91

    
92
	unset($a_cert[$id]);
93
	write_config();
94
	$savemsg = sprintf(gettext("Certificate %s successfully deleted."), htmlspecialchars($a_cert[$id]['descr']));
95
	pfSenseHeader("system_certmanager.php");
96
	exit;
97
}
98

    
99
if ($act == "new") {
100
	$pconfig['method'] = $_POST['method'];
101
	$pconfig['keylen'] = "2048";
102
	$pconfig['digest_alg'] = "sha256";
103
	$pconfig['csr_keylen'] = "2048";
104
	$pconfig['csr_digest_alg'] = "sha256";
105
	$pconfig['type'] = "user";
106
	$pconfig['lifetime'] = "3650";
107
}
108

    
109
if ($act == "exp") {
110

    
111
	if (!$a_cert[$id]) {
112
		pfSenseHeader("system_certmanager.php");
113
		exit;
114
	}
115

    
116
	$exp_name = urlencode("{$a_cert[$id]['descr']}.crt");
117
	$exp_data = base64_decode($a_cert[$id]['crt']);
118
	$exp_size = strlen($exp_data);
119

    
120
	header("Content-Type: application/octet-stream");
121
	header("Content-Disposition: attachment; filename={$exp_name}");
122
	header("Content-Length: $exp_size");
123
	echo $exp_data;
124
	exit;
125
}
126

    
127
if ($act == "req") {
128

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

    
134
	$exp_name = urlencode("{$a_cert[$id]['descr']}.req");
135
	$exp_data = base64_decode($a_cert[$id]['csr']);
136
	$exp_size = strlen($exp_data);
137

    
138
	header("Content-Type: application/octet-stream");
139
	header("Content-Disposition: attachment; filename={$exp_name}");
140
	header("Content-Length: $exp_size");
141
	echo $exp_data;
142
	exit;
143
}
144

    
145
if ($act == "key") {
146

    
147
	if (!$a_cert[$id]) {
148
		pfSenseHeader("system_certmanager.php");
149
		exit;
150
	}
151

    
152
	$exp_name = urlencode("{$a_cert[$id]['descr']}.key");
153
	$exp_data = base64_decode($a_cert[$id]['prv']);
154
	$exp_size = strlen($exp_data);
155

    
156
	header("Content-Type: application/octet-stream");
157
	header("Content-Disposition: attachment; filename={$exp_name}");
158
	header("Content-Length: $exp_size");
159
	echo $exp_data;
160
	exit;
161
}
162

    
163
if ($act == "p12") {
164
	if (!$a_cert[$id]) {
165
		pfSenseHeader("system_certmanager.php");
166
		exit;
167
	}
168

    
169
	$exp_name = urlencode("{$a_cert[$id]['descr']}.p12");
170
	$args = array();
171
	$args['friendly_name'] = $a_cert[$id]['descr'];
172

    
173
	$ca = lookup_ca($a_cert[$id]['caref']);
174

    
175
	if ($ca) {
176
		$args['extracerts'] = openssl_x509_read(base64_decode($ca['crt']));
177
	}
178

    
179
	$res_crt = openssl_x509_read(base64_decode($a_cert[$id]['crt']));
180
	$res_key = openssl_pkey_get_private(array(0 => base64_decode($a_cert[$id]['prv']) , 1 => ""));
181

    
182
	$exp_data = "";
183
	openssl_pkcs12_export($res_crt, $exp_data, $res_key, null, $args);
184
	$exp_size = strlen($exp_data);
185

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

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

    
199
	$pconfig['descr'] = $a_cert[$id]['descr'];
200
	$pconfig['csr'] = base64_decode($a_cert[$id]['csr']);
201
}
202

    
203
if ($_POST['save']) {
204
	// This is just the blank alternate name that is added for display purposes. We don't want to validate/save it
205
	if ($_POST['altname_value0'] == "") {
206
		unset($_POST['altname_type0']);
207
		unset($_POST['altname_value0']);
208
	}
209

    
210
	if ($_POST['save'] == gettext("Save")) {
211
		$input_errors = array();
212
		$pconfig = $_POST;
213

    
214
		/* input validation */
215
		if ($pconfig['method'] == "sign") {
216
			$reqdfields = explode(" ",
217
				"descr catosignwith");
218
			$reqdfieldsn = array(
219
				gettext("Descriptive name"),
220
				gettext("CA to sign with"));
221

    
222
			if (($_POST['csrtosign'] === "new") && (!strstr($_POST['csrpaste'], "BEGIN CERTIFICATE REQUEST") || !strstr($_POST['csrpaste'], "END CERTIFICATE REQUEST"))) {
223
				$input_errors[] = gettext("This signing request does not appear to be valid.");
224
			}
225

    
226
			if ( (($_POST['csrtosign'] === "new") && (strlen($_POST['keypaste']) > 0)) && (!strstr($_POST['keypaste'], "BEGIN PRIVATE KEY") || !strstr($_POST['keypaste'], "END PRIVATE KEY"))) {
227
				$input_errors[] = gettext("This private does not appear to be valid.");
228
				$input_errors[] = gettext("Key data field should be blank, or a valid x509 private key");
229
			}
230

    
231
		}
232

    
233
		if ($pconfig['method'] == "import") {
234
			$reqdfields = explode(" ",
235
				"descr cert key");
236
			$reqdfieldsn = array(
237
				gettext("Descriptive name"),
238
				gettext("Certificate data"),
239
				gettext("Key data"));
240
			if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
241
				$input_errors[] = gettext("This certificate does not appear to be valid.");
242
			}
243

    
244
			if (cert_get_modulus($_POST['cert'], false) != prv_get_modulus($_POST['key'], false)) {
245
				$input_errors[] = gettext("The submitted private key does not match the submitted certificate data.");
246
			}
247
		}
248

    
249
		if ($pconfig['method'] == "internal") {
250
			$reqdfields = explode(" ",
251
				"descr caref keylen type lifetime dn_country dn_state dn_city ".
252
				"dn_organization dn_email dn_commonname");
253
			$reqdfieldsn = array(
254
				gettext("Descriptive name"),
255
				gettext("Certificate authority"),
256
				gettext("Key length"),
257
				gettext("Certificate Type"),
258
				gettext("Lifetime"),
259
				gettext("Distinguished name Country Code"),
260
				gettext("Distinguished name State or Province"),
261
				gettext("Distinguished name City"),
262
				gettext("Distinguished name Organization"),
263
				gettext("Distinguished name Email Address"),
264
				gettext("Distinguished name Common Name"));
265
		}
266

    
267
		if ($pconfig['method'] == "external") {
268
			$reqdfields = explode(" ",
269
				"descr csr_keylen csr_dn_country csr_dn_state csr_dn_city ".
270
				"csr_dn_organization csr_dn_email csr_dn_commonname");
271
			$reqdfieldsn = array(
272
				gettext("Descriptive name"),
273
				gettext("Key length"),
274
				gettext("Distinguished name Country Code"),
275
				gettext("Distinguished name State or Province"),
276
				gettext("Distinguished name City"),
277
				gettext("Distinguished name Organization"),
278
				gettext("Distinguished name Email Address"),
279
				gettext("Distinguished name Common Name"));
280
		}
281

    
282
		if ($pconfig['method'] == "existing") {
283
			$reqdfields = array("certref");
284
			$reqdfieldsn = array(gettext("Existing Certificate Choice"));
285
		}
286

    
287
		$altnames = array();
288
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
289

    
290
		if ($pconfig['method'] != "import" && $pconfig['method'] != "existing") {
291
			/* subjectAltNames */
292
			foreach ($_POST as $key => $value) {
293
				$entry = '';
294
				if (!substr_compare('altname_type', $key, 0, 12)) {
295
					$entry = substr($key, 12);
296
					$field = 'type';
297
				} elseif (!substr_compare('altname_value', $key, 0, 13)) {
298
					$entry = substr($key, 13);
299
					$field = 'value';
300
				}
301

    
302
				if (ctype_digit($entry)) {
303
					$entry++;	// Pre-bootstrap code is one-indexed, but the bootstrap code is 0-indexed
304
					$altnames[$entry][$field] = $value;
305
				}
306
			}
307

    
308
			$pconfig['altnames']['item'] = $altnames;
309

    
310
			/* Input validation for subjectAltNames */
311
			foreach ($altnames as $idx => $altname) {
312
				switch ($altname['type']) {
313
					case "DNS":
314
						if (!is_hostname($altname['value'], true)) {
315
							array_push($input_errors, "DNS subjectAltName values must be valid hostnames, FQDNs or wildcard domains.");
316
						}
317
						break;
318
					case "IP":
319
						if (!is_ipaddr($altname['value'])) {
320
							array_push($input_errors, "IP subjectAltName values must be valid IP Addresses");
321
						}
322
						break;
323
					case "email":
324
						if (empty($altname['value'])) {
325
							array_push($input_errors, "An e-mail address must be provided for this type of subjectAltName");
326
						}
327
						if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $altname['value'])) {
328
							array_push($input_errors, "The e-mail provided in a subjectAltName contains invalid characters.");
329
						}
330
						break;
331
					case "URI":
332
						/* Close enough? */
333
						if (!is_URL($altname['value'])) {
334
							$input_errors[] = "URI subjectAltName types must be a valid URI";
335
						}
336
						break;
337
					default:
338
						$input_errors[] = "Unrecognized subjectAltName type.";
339
				}
340
			}
341

    
342
			/* Make sure we do not have invalid characters in the fields for the certificate */
343

    
344
			if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
345
				array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
346
			}
347

    
348
			for ($i = 0; $i < count($reqdfields); $i++) {
349
				if (preg_match('/email/', $reqdfields[$i])) { /* dn_email or csr_dn_name */
350
					if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) {
351
						array_push($input_errors, gettext("The field 'Distinguished name Email Address' contains invalid characters."));
352
					}
353
				} else if (preg_match('/commonname/', $reqdfields[$i])) { /* dn_commonname or csr_dn_commonname */
354
					if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) {
355
						array_push($input_errors, gettext("The field 'Distinguished name Common Name' contains invalid characters."));
356
					}
357
				} else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST[$reqdfields[$i]])) {
358
					array_push($input_errors, sprintf(gettext("The field '%s' contains invalid characters."), $reqdfieldsn[$i]));
359
				}
360
			}
361

    
362
			if (($pconfig['method'] != "external") && isset($_POST["keylen"]) && !in_array($_POST["keylen"], $cert_keylens)) {
363
				array_push($input_errors, gettext("Please select a valid Key Length."));
364
			}
365
			if (($pconfig['method'] != "external") && !in_array($_POST["digest_alg"], $openssl_digest_algs)) {
366
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
367
			}
368

    
369
			if (($pconfig['method'] == "external") && isset($_POST["csr_keylen"]) && !in_array($_POST["csr_keylen"], $cert_keylens)) {
370
				array_push($input_errors, gettext("Please select a valid Key Length."));
371
			}
372
			if (($pconfig['method'] == "external") && !in_array($_POST["csr_digest_alg"], $openssl_digest_algs)) {
373
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
374
			}
375
		}
376

    
377
		/* save modifications */
378
		if (!$input_errors) {
379

    
380
			if ($pconfig['method'] == "existing") {
381
				$cert = lookup_cert($pconfig['certref']);
382
				if ($cert && $a_user) {
383
					$a_user[$userid]['cert'][] = $cert['refid'];
384
				}
385
			} else if ($pconfig['method'] == "sign") { // Sign a CSR
386
				$csrid = lookup_cert($pconfig['csrtosign']);
387
				$caid =  lookup_ca($pconfig['catosignwith']);
388

    
389
				// Read the CSR from $config, or if a new one, from the textarea
390
				if ($pconfig['csrtosign'] === "new") {
391
					$csr = $pconfig['csrpaste'];
392
				} else {
393
					$csr = base64_decode($csrid['csr']);
394
				}
395

    
396
				$old_err_level = error_reporting(0);
397

    
398
				// Gather the information required for signed cert
399
				$ca = base64_decode($caid['crt']);
400
				$key = base64_decode($caid['prv']);
401
				$duration = $pconfig['duration'];
402
				$caref = $pconfig['catosignwith'];
403
				$type = (cert_get_purpose($csrid)['server'] === "Yes") ? "server":"user";
404

    
405
				// Sign the new cert and export it in x509 format
406
				openssl_x509_export(openssl_csr_sign($csr, $ca, $key, $duration, ['x509_extensions' => 'v3_req']), $n509);
407

    
408
				// Gather the details required to save the new cert
409
				$newcert = array();
410
				$newcert['refid'] = uniqid();
411
				$newcert['caref'] = $caref;
412
				$newcert['descr'] = $pconfig['descr'];
413
				$newcert['type'] = $type;
414
				$newcert['crt'] = base64_encode($n509);
415

    
416
				if ($pconfig['csrtosign'] === "new") {
417
					$newcert['prv'] = base64_encode($pconfig['keypaste']);
418
				} else {
419
					$newcert['prv'] = $csrid['prv'];
420
				}
421

    
422
				// Add it to the config file
423
				$config['cert'][] = $newcert;
424

    
425
				error_reporting($old_err_level);
426

    
427
			} else {
428
				$cert = array();
429
				$cert['refid'] = uniqid();
430
				if (isset($id) && $a_cert[$id]) {
431
					$cert = $a_cert[$id];
432
				}
433

    
434
				$cert['descr'] = $pconfig['descr'];
435

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

    
438
				if ($pconfig['method'] == "import") {
439
					cert_import($cert, $pconfig['cert'], $pconfig['key']);
440
				}
441

    
442
				if ($pconfig['method'] == "internal") {
443
					$dn = array(
444
						'countryName' => $pconfig['dn_country'],
445
						'stateOrProvinceName' => $pconfig['dn_state'],
446
						'localityName' => $pconfig['dn_city'],
447
						'organizationName' => $pconfig['dn_organization'],
448
						'emailAddress' => $pconfig['dn_email'],
449
						'commonName' => $pconfig['dn_commonname']);
450
					if (!empty($pconfig['dn_organizationalunit'])) {
451
						$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
452
					}
453
					if (count($altnames)) {
454
						$altnames_tmp = "";
455
						foreach ($altnames as $altname) {
456
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
457
						}
458

    
459
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
460
					}
461

    
462
					if (!cert_create($cert, $pconfig['caref'], $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['type'], $pconfig['digest_alg'])) {
463
						$input_errors = array();
464
						while ($ssl_err = openssl_error_string()) {
465
							if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
466
								array_push($input_errors, "openssl library returns: " . $ssl_err);
467
							}
468
						}
469
					}
470
				}
471

    
472
				if ($pconfig['method'] == "external") {
473
					$dn = array(
474
						'countryName' => $pconfig['csr_dn_country'],
475
						'stateOrProvinceName' => $pconfig['csr_dn_state'],
476
						'localityName' => $pconfig['csr_dn_city'],
477
						'organizationName' => $pconfig['csr_dn_organization'],
478
						'emailAddress' => $pconfig['csr_dn_email'],
479
						'commonName' => $pconfig['csr_dn_commonname']);
480
					if (!empty($pconfig['csr_dn_organizationalunit'])) {
481
						$dn['organizationalUnitName'] = $pconfig['csr_dn_organizationalunit'];
482
					}
483
					if (count($altnames)) {
484
						$altnames_tmp = "";
485
						foreach ($altnames as $altname) {
486
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
487
						}
488
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
489
					}
490

    
491
					if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) {
492
						$input_errors = array();
493
						while ($ssl_err = openssl_error_string()) {
494
							if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
495
								array_push($input_errors, "openssl library returns: " . $ssl_err);
496
							}
497
						}
498
					}
499
				}
500

    
501
				error_reporting($old_err_level);
502

    
503
				if (isset($id) && $a_cert[$id]) {
504
					$a_cert[$id] = $cert;
505
				} else {
506
					$a_cert[] = $cert;
507
				}
508

    
509
				if (isset($a_user) && isset($userid)) {
510
					$a_user[$userid]['cert'][] = $cert['refid'];
511
				}
512
			}
513

    
514
			if (!$input_errors) {
515
				write_config();
516
			}
517

    
518
			if ($userid && !$input_errors) {
519
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
520
				exit;
521
			}
522
		}
523
	}
524

    
525
	if ($_POST['save'] == gettext("Update")) {
526
		unset($input_errors);
527
		$pconfig = $_POST;
528

    
529
		/* input validation */
530
		$reqdfields = explode(" ", "descr cert");
531
		$reqdfieldsn = array(
532
			gettext("Descriptive name"),
533
			gettext("Final Certificate data"));
534

    
535
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
536

    
537
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
538
			array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
539
		}
540

    
541
//		old way
542
		/* make sure this csr and certificate subjects match */
543
//		$subj_csr = csr_get_subject($pconfig['csr'], false);
544
//		$subj_cert = cert_get_subject($pconfig['cert'], false);
545
//
546
//		if (!isset($_POST['ignoresubjectmismatch']) && !($_POST['ignoresubjectmismatch'] == "yes")) {
547
//			if (strcmp($subj_csr, $subj_cert)) {
548
//				$input_errors[] = sprintf(gettext("The certificate subject '%s' does not match the signing request subject."), $subj_cert);
549
//				$subject_mismatch = true;
550
//			}
551
//		}
552
		$mod_csr = csr_get_modulus($pconfig['csr'], false);
553
		$mod_cert = cert_get_modulus($pconfig['cert'], false);
554

    
555
		if (strcmp($mod_csr, $mod_cert)) {
556
			// simply: if the moduli don't match, then the private key and public key won't match
557
			$input_errors[] = sprintf(gettext("The certificate modulus does not match the signing request modulus."), $subj_cert);
558
			$subject_mismatch = true;
559
		}
560

    
561
		/* save modifications */
562
		if (!$input_errors) {
563

    
564
			$cert = $a_cert[$id];
565

    
566
			$cert['descr'] = $pconfig['descr'];
567

    
568
			csr_complete($cert, $pconfig['cert']);
569

    
570
			$a_cert[$id] = $cert;
571

    
572
			write_config();
573

    
574
			pfSenseHeader("system_certmanager.php");
575
		}
576
	}
577
}
578

    
579
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
580
$pglinks = array("", "system_camanager.php", "system_certmanager.php");
581

    
582
if (($act == "new" || ($_POST['save'] == gettext("Save") && $input_errors)) || ($act == "csr" || ($_POST['save'] == gettext("Update") && $input_errors))) {
583
	$pgtitle[] = gettext('Edit');
584
	$pglinks[] = "@self";
585
}
586
include("head.inc");
587

    
588
if ($input_errors) {
589
	print_input_errors($input_errors);
590
}
591

    
592
if ($savemsg) {
593
	print_info_box($savemsg, 'success');
594
}
595

    
596
$tab_array = array();
597
$tab_array[] = array(gettext("CAs"), false, "system_camanager.php");
598
$tab_array[] = array(gettext("Certificates"), true, "system_certmanager.php");
599
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
600
display_top_tabs($tab_array);
601

    
602
// Load valid country codes
603
$dn_cc = array();
604
if (file_exists("/etc/ca_countries")) {
605
	$dn_cc_file=file("/etc/ca_countries");
606
	foreach ($dn_cc_file as $line) {
607
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
608
			$dn_cc[$matches[1]] = $matches[1];
609
		}
610
	}
611
}
612

    
613
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
614
	$form = new Form();
615
	$form->setAction('system_certmanager.php?act=edit');
616

    
617
	if (isset($userid) && $a_user) {
618
		$form->addGlobal(new Form_Input(
619
			'userid',
620
			null,
621
			'hidden',
622
			$userid
623
		));
624
	}
625

    
626
	if (isset($id) && $a_cert[$id]) {
627
		$form->addGlobal(new Form_Input(
628
			'id',
629
			null,
630
			'hidden',
631
			$id
632
		));
633
	}
634

    
635
	$section = new Form_Section('Add/Sign a New Certificate');
636

    
637
	if (!isset($id)) {
638
		$section->addInput(new Form_Select(
639
			'method',
640
			'*Method',
641
			$pconfig['method'],
642
			$cert_methods
643
		))->toggles();
644
	}
645

    
646
	$section->addInput(new Form_Input(
647
		'descr',
648
		'*Descriptive name',
649
		'text',
650
		($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
651
	))->addClass('toggle-existing');
652

    
653
	$form->add($section);
654

    
655
	// Return an array containing the IDs od all CAs
656
	function list_cas() {
657
		global $a_ca;
658
		$allCas = array();
659

    
660
		foreach ($a_ca as $ca) {
661
			if ($ca['prv']) {
662
				$allCas[$ca['refid']] = $ca['descr'];
663
			}
664
		}
665

    
666
		return $allCas;
667
	}
668

    
669
	// Return an array containing the IDs od all CSRs
670
	function list_csrs() {
671
		global $config;
672
		$allCsrs = array();
673

    
674
		foreach ($config['cert'] as $cert) {
675
			if ($cert['csr']) {
676
				$allCsrs[$cert['refid']] = $cert['descr'];
677
			}
678
		}
679

    
680
		return ['new' => gettext('New CSR (Paste below)')] + $allCsrs;
681
	}
682

    
683
	$section = new Form_Section('Sign CSR');
684
	$section->addClass('toggle-sign collapse');
685

    
686
	$section->AddInput(new Form_Select(
687
		'catosignwith',
688
		'*CA to sign with',
689
		$pconfig['catosignwith'],
690
		list_cas()
691
	));
692

    
693
	$section->AddInput(new Form_Select(
694
		'csrtosign',
695
		'*CSR to sign',
696
		isset($pconfig['csrtosign']) ? $pconfig['csrtosign'] : 'new',
697
		list_csrs()
698
	));
699

    
700
	$section->addInput(new Form_Input(
701
		'duration',
702
		'*Certificate duration (days)',
703
		'number',
704
		$pconfig['duration'] ? $pconfig['duration']:'3650'
705
	));
706

    
707
	$section->addInput(new Form_Textarea(
708
		'csrpaste',
709
		'CSR data',
710
		$pconfig['csrpaste']
711
	))->setHelp('Paste a Certificate Signing Request in X.509 PEM format here.');
712

    
713
	$section->addInput(new Form_Textarea(
714
		'keypaste',
715
		'Key data',
716
		$pconfig['keypaste']
717
	))->setHelp('Optionally paste a private key here. The key will be associated with the newly signed certificate in pfSense');
718

    
719
	$form->add($section);
720

    
721
	$section = new Form_Section('Import Certificate');
722
	$section->addClass('toggle-import collapse');
723

    
724
	$section->addInput(new Form_Textarea(
725
		'cert',
726
		'*Certificate data',
727
		$pconfig['cert']
728
	))->setHelp('Paste a certificate in X.509 PEM format here.');
729

    
730
	$section->addInput(new Form_Textarea(
731
		'key',
732
		'*Private key data',
733
		$pconfig['key']
734
	))->setHelp('Paste a private key in X.509 PEM format here.');
735

    
736
	$form->add($section);
737
	$section = new Form_Section('Internal Certificate');
738
	$section->addClass('toggle-internal collapse');
739

    
740
	if (!$internal_ca_count) {
741
		$section->addInput(new Form_StaticText(
742
			'*Certificate authority',
743
			gettext('No internal Certificate Authorities have been defined. ') .
744
			gettext('An internal CA must be defined in order to create an internal certificate. ') .
745
			sprintf(gettext('%1$sCreate%2$s an internal CA.'), '<a href="system_camanager.php?act=new&amp;method=internal"> ', '</a>')
746
		));
747
	} else {
748
		$allCas = array();
749
		foreach ($a_ca as $ca) {
750
			if (!$ca['prv']) {
751
				continue;
752
			}
753

    
754
			$allCas[ $ca['refid'] ] = $ca['descr'];
755
		}
756

    
757
		$section->addInput(new Form_Select(
758
			'caref',
759
			'*Certificate authority',
760
			$pconfig['caref'],
761
			$allCas
762
		));
763
	}
764

    
765
	$section->addInput(new Form_Select(
766
		'keylen',
767
		'*Key length',
768
		$pconfig['keylen'],
769
		array_combine($cert_keylens, $cert_keylens)
770
	));
771

    
772
	$section->addInput(new Form_Select(
773
		'digest_alg',
774
		'*Digest Algorithm',
775
		$pconfig['digest_alg'],
776
		array_combine($openssl_digest_algs, $openssl_digest_algs)
777
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
778
		'SHA1 when possible.');
779

    
780
	$section->addInput(new Form_Select(
781
		'type',
782
		'*Certificate Type',
783
		$pconfig['type'],
784
		$cert_types
785
	))->setHelp('Type of certificate to generate. Used for placing '.
786
		'restrictions on the usage of the generated certificate.');
787

    
788
	$section->addInput(new Form_Input(
789
		'lifetime',
790
		'*Lifetime (days)',
791
		'number',
792
		$pconfig['lifetime']
793
	));
794

    
795
	$section->addInput(new Form_Select(
796
		'dn_country',
797
		'*Country Code',
798
		$pconfig['dn_country'],
799
		$dn_cc
800
	));
801

    
802
	$section->addInput(new Form_Input(
803
		'dn_state',
804
		'*State or Province',
805
		'text',
806
		$pconfig['dn_state'],
807
		['placeholder' => 'e.g. Texas']
808
	));
809

    
810
	$section->addInput(new Form_Input(
811
		'dn_city',
812
		'*City',
813
		'text',
814
		$pconfig['dn_city'],
815
		['placeholder' => 'e.g. Austin']
816
	));
817

    
818
	$section->addInput(new Form_Input(
819
		'dn_organization',
820
		'*Organization',
821
		'text',
822
		$pconfig['dn_organization'],
823
		['placeholder' => 'e.g. My Company Inc']
824
	));
825

    
826
	$section->addInput(new Form_Input(
827
		'dn_organizationalunit',
828
		'Organizational Unit',
829
		'text',
830
		$pconfig['dn_organizationalunit'],
831
		['placeholder' => 'e.g. My Department Name (optional)']
832
	));
833

    
834
	$section->addInput(new Form_Input(
835
		'dn_email',
836
		'*Email Address',
837
		'text',
838
		$pconfig['dn_email'],
839
		['placeholder' => 'e.g. admin@mycompany.com']
840
	));
841

    
842
	$section->addInput(new Form_Input(
843
		'dn_commonname',
844
		'*Common Name',
845
		'text',
846
		$pconfig['dn_commonname'],
847
		['placeholder' => 'e.g. www.example.com']
848
	));
849

    
850
	if (empty($pconfig['altnames']['item'])) {
851
		$pconfig['altnames']['item'] = array(
852
			array('type' => null, 'value' => null)
853
		);
854
	}
855

    
856
	$counter = 0;
857
	$numrows = count($pconfig['altnames']['item']) - 1;
858

    
859
	foreach ($pconfig['altnames']['item'] as $item) {
860

    
861
		$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
862

    
863
		$group->add(new Form_Select(
864
			'altname_type' . $counter,
865
			'Type',
866
			$item['type'],
867
			array(
868
				'DNS' => gettext('FQDN or Hostname'),
869
				'IP' => gettext('IP address'),
870
				'URI' => gettext('URI'),
871
				'email' => gettext('email address'),
872
			)
873
		))->setHelp(($counter == $numrows) ? 'Type':null);
874

    
875
		$group->add(new Form_Input(
876
			'altname_value' . $counter,
877
			null,
878
			'text',
879
			$item['value']
880
		))->setHelp(($counter == $numrows) ? 'Value':null);
881

    
882
		$group->add(new Form_Button(
883
			'deleterow' . $counter,
884
			'Delete',
885
			null,
886
			'fa-trash'
887
		))->addClass('btn-warning');
888

    
889
		$group->addClass('repeatable');
890

    
891
		$section->add($group);
892

    
893
		$counter++;
894
	}
895

    
896
	$section->addInput(new Form_Button(
897
		'addrow',
898
		'Add',
899
		null,
900
		'fa-plus'
901
	))->addClass('btn-success');
902

    
903

    
904
	$form->add($section);
905
	$section = new Form_Section('External Signing Request');
906
	$section->addClass('toggle-external collapse');
907

    
908
	$section->addInput(new Form_Select(
909
		'csr_keylen',
910
		'*Key length',
911
		$pconfig['csr_keylen'],
912
		array_combine($cert_keylens, $cert_keylens)
913
	));
914

    
915
	$section->addInput(new Form_Select(
916
		'csr_digest_alg',
917
		'*Digest Algorithm',
918
		$pconfig['csr_digest_alg'],
919
		array_combine($openssl_digest_algs, $openssl_digest_algs)
920
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
921
		'SHA1 when possible');
922

    
923
	$section->addInput(new Form_Select(
924
		'csr_dn_country',
925
		'*Country Code',
926
		$pconfig['csr_dn_country'],
927
		$dn_cc
928
	));
929

    
930
	$section->addInput(new Form_Input(
931
		'csr_dn_state',
932
		'*State or Province',
933
		'text',
934
		$pconfig['csr_dn_state'],
935
		['placeholder' => 'e.g. Texas']
936
	));
937

    
938
	$section->addInput(new Form_Input(
939
		'csr_dn_city',
940
		'*City',
941
		'text',
942
		$pconfig['csr_dn_city'],
943
		['placeholder' => 'e.g. Austin']
944
	));
945

    
946
	$section->addInput(new Form_Input(
947
		'csr_dn_organization',
948
		'*Organization',
949
		'text',
950
		$pconfig['csr_dn_organization'],
951
		['placeholder' => 'e.g. My Company Inc']
952
	));
953

    
954
	$section->addInput(new Form_Input(
955
		'csr_dn_organizationalunit',
956
		'Organizational Unit',
957
		'text',
958
		$pconfig['csr_dn_organizationalunit'],
959
		['placeholder' => 'e.g. My Department Name (optional)']
960
	));
961

    
962
	$section->addInput(new Form_Input(
963
		'csr_dn_email',
964
		'*Email Address',
965
		'text',
966
		$pconfig['csr_dn_email'],
967
		['placeholder' => 'e.g. admin@mycompany.com']
968
	));
969

    
970
	$section->addInput(new Form_Input(
971
		'csr_dn_commonname',
972
		'*Common Name',
973
		'text',
974
		$pconfig['csr_dn_commonname'],
975
		['placeholder' => 'e.g. internal-ca']
976
	));
977

    
978
	$form->add($section);
979
	$section = new Form_Section('Choose an Existing Certificate');
980
	$section->addClass('toggle-existing collapse');
981

    
982
	$existCerts = array();
983

    
984
	foreach ($config['cert'] as $cert)	{
985
		if (is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
986
			if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert'])) {
987
				continue;
988
			}
989
		}
990

    
991
		$ca = lookup_ca($cert['caref']);
992
		if ($ca) {
993
			$cert['descr'] .= " (CA: {$ca['descr']})";
994
		}
995

    
996
		if (cert_in_use($cert['refid'])) {
997
			$cert['descr'] .= " (In Use)";
998
		}
999
		if (is_cert_revoked($cert)) {
1000
			$cert['descr'] .= " (Revoked)";
1001
		}
1002

    
1003
		$existCerts[ $cert['refid'] ] = $cert['descr'];
1004
	}
1005

    
1006
	$section->addInput(new Form_Select(
1007
		'certref',
1008
		'*Existing Certificates',
1009
		$pconfig['certref'],
1010
		$existCerts
1011
	));
1012

    
1013
	$form->add($section);
1014
	print $form;
1015

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

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

    
1022
	$section->addInput(new Form_Input(
1023
		'descr',
1024
		'*Descriptive name',
1025
		'text',
1026
		$pconfig['descr']
1027
	));
1028

    
1029
	$section->addInput(new Form_Textarea(
1030
		'csr',
1031
		'Signing request data',
1032
		$pconfig['csr']
1033
	))->setReadonly()
1034
	  ->setWidth(7)
1035
	  ->setHelp('Copy the certificate signing data from here and forward it to a certificate authority for signing.');
1036

    
1037
	$section->addInput(new Form_Textarea(
1038
		'cert',
1039
		'*Final certificate data',
1040
		$pconfig['cert']
1041
	))->setWidth(7)
1042
	  ->setHelp('Paste the certificate received from the certificate authority here.');
1043

    
1044
	 if (isset($id) && $a_cert[$id]) {
1045
		 $section->addInput(new Form_Input(
1046
			'id',
1047
			null,
1048
			'hidden',
1049
			$id
1050
		 ));
1051

    
1052
		 $section->addInput(new Form_Input(
1053
			'act',
1054
			null,
1055
			'hidden',
1056
			'csr'
1057
		 ));
1058
	 }
1059

    
1060
	$form->add($section);
1061

    
1062
	$form->addGlobal(new Form_Button(
1063
		'save',
1064
		'Update',
1065
		null,
1066
		'fa-save'
1067
	))->addClass('btn-primary');
1068

    
1069
	print($form);
1070
} else {
1071
?>
1072
<div class="panel panel-default">
1073
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificates')?></h2></div>
1074
	<div class="panel-body">
1075
		<div class="table-responsive">
1076
		<table class="table table-striped table-hover">
1077
			<thead>
1078
				<tr>
1079
					<th><?=gettext("Name")?></th>
1080
					<th><?=gettext("Issuer")?></th>
1081
					<th><?=gettext("Distinguished Name")?></th>
1082
					<th><?=gettext("In Use")?></th>
1083

    
1084
					<th class="col-sm-2"><?=gettext("Actions")?></th>
1085
				</tr>
1086
			</thead>
1087
			<tbody>
1088
<?php
1089

    
1090
$pluginparams = array();
1091
$pluginparams['type'] = 'certificates';
1092
$pluginparams['event'] = 'used_certificates';
1093
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
1094
$i = 0;
1095
foreach ($a_cert as $i => $cert):
1096
	$name = htmlspecialchars($cert['descr']);
1097

    
1098
	if ($cert['crt']) {
1099
		$subj = cert_get_subject($cert['crt']);
1100
		$issuer = cert_get_issuer($cert['crt']);
1101
		$purpose = cert_get_purpose($cert['crt']);
1102
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
1103

    
1104
		if ($subj == $issuer) {
1105
			$caname = '<i>'. gettext("self-signed") .'</i>';
1106
		} else {
1107
			$caname = '<i>'. gettext("external").'</i>';
1108
		}
1109

    
1110
		$subj = htmlspecialchars($subj);
1111
	} else {
1112
		$subj = "";
1113
		$issuer = "";
1114
		$purpose = "";
1115
		$startdate = "";
1116
		$enddate = "";
1117
		$caname = "<em>" . gettext("private key only") . "</em>";
1118
	}
1119

    
1120
	if ($cert['csr']) {
1121
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
1122
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
1123
	}
1124

    
1125
	$ca = lookup_ca($cert['caref']);
1126
	if ($ca) {
1127
		$caname = $ca['descr'];
1128
	}
1129
?>
1130
				<tr>
1131
					<td>
1132
						<?=$name?><br />
1133
						<?php if ($cert['type']): ?>
1134
							<i><?=$cert_types[$cert['type']]?></i><br />
1135
						<?php endif?>
1136
						<?php if (is_array($purpose)): ?>
1137
							CA: <b><?=$purpose['ca']?></b>, <?=gettext("Server")?>: <b><?=$purpose['server']?></b>
1138
						<?php endif?>
1139
					</td>
1140
					<td><?=$caname?></td>
1141
					<td>
1142
						<?=$subj?>
1143
						<?php if (!empty($startdate) || !empty($enddate)): ?>
1144
						<br />
1145
						<small>
1146
							<?=gettext("Valid From")?>: <b><?=$startdate ?></b><br /><?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
1147
						</small>
1148
						<?php endif?>
1149
					</td>
1150
					<td>
1151
						<?php if (is_cert_revoked($cert)): ?>
1152
							<i><?=gettext("Revoked")?></i>
1153
						<?php endif?>
1154
						<?php if (is_webgui_cert($cert['refid'])): ?>
1155
							<?=gettext("webConfigurator")?>
1156
						<?php endif?>
1157
						<?php if (is_user_cert($cert['refid'])): ?>
1158
							<?=gettext("User Cert")?>
1159
						<?php endif?>
1160
						<?php if (is_openvpn_server_cert($cert['refid'])): ?>
1161
							<?=gettext("OpenVPN Server")?>
1162
						<?php endif?>
1163
						<?php if (is_openvpn_client_cert($cert['refid'])): ?>
1164
							<?=gettext("OpenVPN Client")?>
1165
						<?php endif?>
1166
						<?php if (is_ipsec_cert($cert['refid'])): ?>
1167
							<?=gettext("IPsec Tunnel")?>
1168
						<?php endif?>
1169
						<?php if (is_captiveportal_cert($cert['refid'])): ?>
1170
							<?=gettext("Captive Portal")?>
1171
						<?php endif?>
1172
<?php
1173
							$refid = $cert['refid'];
1174
							if (is_array($certificates_used_by_packages)) {
1175
								foreach ($certificates_used_by_packages as $name => $package) {
1176
									if (isset($package['certificatelist'][$refid])) {
1177
										$hint = "" ;
1178
										if (is_array($package['certificatelist'][$refid])) {
1179
											foreach ($package['certificatelist'][$refid] as $cert_used) {
1180
												$hint = $hint . $cert_used['usedby']."\n";
1181
											}
1182
										}
1183
										$count = count($package['certificatelist'][$refid]);
1184
										echo "<div title='".htmlspecialchars($hint)."'>";
1185
										echo htmlspecialchars($package['pkgname'])." ($count)<br />";
1186
										echo "</div>";
1187
									}
1188
								}
1189
							}
1190
?>
1191
					</td>
1192
					<td>
1193
						<?php if (!$cert['csr']): ?>
1194
							<a href="system_certmanager.php?act=exp&amp;id=<?=$i?>" class="fa fa-certificate" title="<?=gettext("Export Certificate")?>"></a>
1195
							<?php if ($cert['prv']): ?>
1196
								<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1197
							<?php endif?>
1198
							<a href="system_certmanager.php?act=p12&amp;id=<?=$i?>" class="fa fa-archive" title="<?=gettext("Export P12")?>"></a>
1199
						<?php else: ?>
1200
							<a href="system_certmanager.php?act=csr&amp;id=<?=$i?>" class="fa fa-pencil" title="<?=gettext("Update CSR")?>"></a>
1201
							<a href="system_certmanager.php?act=req&amp;id=<?=$i?>" class="fa fa-sign-in" title="<?=gettext("Export Request")?>"></a>
1202
							<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1203
						<?php endif?>
1204
						<?php if (!cert_in_use($cert['refid'])): ?>
1205
							<a href="system_certmanager.php?act=del&amp;id=<?=$i?>" class="fa fa-trash" title="<?=gettext("Delete Certificate")?>" usepost></a>
1206
						<?php endif?>
1207
					</td>
1208
				</tr>
1209
<?php
1210
	$i++;
1211
	endforeach; ?>
1212
			</tbody>
1213
		</table>
1214
		</div>
1215
	</div>
1216
</div>
1217

    
1218
<nav class="action-buttons">
1219
	<a href="?act=new" class="btn btn-success btn-sm">
1220
		<i class="fa fa-plus icon-embed-btn"></i>
1221
		<?=gettext("Add/Sign")?>
1222
	</a>
1223
</nav>
1224
<?php
1225
	include("foot.inc");
1226
	exit;
1227
}
1228

    
1229

    
1230
?>
1231
<script type="text/javascript">
1232
//<![CDATA[
1233
events.push(function() {
1234

    
1235
<?php if ($internal_ca_count): ?>
1236
	function internalca_change() {
1237

    
1238
		caref = $('#caref').val();
1239

    
1240
		switch (caref) {
1241
<?php
1242
			foreach ($a_ca as $ca):
1243
				if (!$ca['prv']) {
1244
					continue;
1245
				}
1246

    
1247
				$subject = cert_get_subject_array($ca['crt']);
1248
?>
1249
				case "<?=$ca['refid'];?>":
1250
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1251
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1252
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1253
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1254
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1255
					$('#dn_organizationalunit').val("<?=$subject[6]['v'];?>");
1256
					break;
1257
<?php
1258
			endforeach;
1259
?>
1260
		}
1261
	}
1262

    
1263
	function set_csr_ro() {
1264
		var newcsr = ($('#csrtosign').val() == "new");
1265

    
1266
		$('#csrpaste').attr('readonly', !newcsr);
1267
		$('#keypaste').attr('readonly', !newcsr);
1268
		setRequired('csrpaste', newcsr);
1269
	}
1270

    
1271
	// ---------- Click checkbox handlers ---------------------------------------------------------
1272

    
1273
	$('#caref').on('change', function() {
1274
		internalca_change();
1275
	});
1276

    
1277
	$('#csrtosign').change(function () {
1278
		set_csr_ro();
1279
	});
1280

    
1281
	// ---------- On initial page load ------------------------------------------------------------
1282

    
1283
	internalca_change();
1284
	set_csr_ro();
1285

    
1286
	// Suppress "Delete row" button if there are fewer than two rows
1287
	checkLastRow();
1288

    
1289
<?php endif; ?>
1290

    
1291

    
1292
});
1293
//]]>
1294
</script>
1295
<?php
1296
include('foot.inc');
(191-191/223)