Project

General

Profile

Download (34.9 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") && (!strstr($_POST['keypaste'], "BEGIN PRIVATE KEY") || !strstr($_POST['keypaste'], "END PRIVATE KEY"))) {
227
				$input_errors[] = gettext("This CSR key does not appear to be valid.");
228
			}
229
		}
230

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

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

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

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

    
284
		$altnames = array();
285
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
286

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

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

    
305
			$pconfig['altnames']['item'] = $altnames;
306

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

    
339
			/* Make sure we do not have invalid characters in the fields for the certificate */
340

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

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

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

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

    
374
		/* save modifications */
375
		if (!$input_errors) {
376

    
377
			if ($pconfig['method'] == "existing") {
378
				$cert = lookup_cert($pconfig['certref']);
379
				if ($cert && $a_user) {
380
					$a_user[$userid]['cert'][] = $cert['refid'];
381
				}
382
			} else if ($pconfig['method'] == "sign") { // Sign a CSR
383
				if ($pconfig['csrtosign'] === "new") {
384
					$csr = $pconfig['csrpaste'];
385
				} else {
386
					$csr = base64_decode($config['cert'][$pconfig['csrtosign']]['csr']);
387
				}
388

    
389
				$old_err_level = error_reporting(0);
390

    
391
				$ca = base64_decode($config['ca'][$pconfig['catosignwith']]['crt']);
392
				$key = base64_decode($config['ca'][$pconfig['catosignwith']]['prv']);
393
				$duration = $pconfig['duration'];
394
				$caref = $config['ca'][$pconfig['catosignwith']]['refid'];
395
				$type = (cert_get_purpose($config['cert'][$pconfig['csrtosign']]['csr'])['server'] === "Yes") ? "server":"user";
396

    
397
				openssl_x509_export(openssl_csr_sign($csr, $ca, $key, $duration, ['x509_extensions' => 'v3_req']), $n509);
398

    
399
				$newcert = array();
400
				$newcert['refid'] = uniqid();
401
				$newcert['caref'] = $caref;
402
				$newcert['descr'] = $pconfig['descr'];
403
				$newcert['type'] = $type;
404
				$newcert['crt'] = base64_encode($n509);
405

    
406
				if ($pconfig['csrtosign'] === "new") {
407
					$newcert['prv'] = $pconfig['keypaste'];
408
				} else {
409
					$newcert['prv'] = $config['cert'][$pconfig['csrtosign']]['prv'];
410
				}
411

    
412
				$config['cert'][] = $newcert;
413

    
414
				error_reporting($old_err_level);
415

    
416
			} else {
417
				$cert = array();
418
				$cert['refid'] = uniqid();
419
				if (isset($id) && $a_cert[$id]) {
420
					$cert = $a_cert[$id];
421
				}
422

    
423
				$cert['descr'] = $pconfig['descr'];
424

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

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

    
431
				if ($pconfig['method'] == "internal") {
432
					$dn = array(
433
						'countryName' => $pconfig['dn_country'],
434
						'stateOrProvinceName' => $pconfig['dn_state'],
435
						'localityName' => $pconfig['dn_city'],
436
						'organizationName' => $pconfig['dn_organization'],
437
						'emailAddress' => $pconfig['dn_email'],
438
						'commonName' => $pconfig['dn_commonname']);
439
					if (!empty($pconfig['dn_organizationalunit'])) {
440
						$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
441
					}
442
					if (count($altnames)) {
443
						$altnames_tmp = "";
444
						foreach ($altnames as $altname) {
445
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
446
						}
447

    
448
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
449
					}
450

    
451
					if (!cert_create($cert, $pconfig['caref'], $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['type'], $pconfig['digest_alg'])) {
452
						$input_errors = array();
453
						while ($ssl_err = openssl_error_string()) {
454
							if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
455
								array_push($input_errors, "openssl library returns: " . $ssl_err);
456
							}
457
						}
458
					}
459
				}
460

    
461
				if ($pconfig['method'] == "external") {
462
					$dn = array(
463
						'countryName' => $pconfig['csr_dn_country'],
464
						'stateOrProvinceName' => $pconfig['csr_dn_state'],
465
						'localityName' => $pconfig['csr_dn_city'],
466
						'organizationName' => $pconfig['csr_dn_organization'],
467
						'emailAddress' => $pconfig['csr_dn_email'],
468
						'commonName' => $pconfig['csr_dn_commonname']);
469
					if (!empty($pconfig['csr_dn_organizationalunit'])) {
470
						$dn['organizationalUnitName'] = $pconfig['csr_dn_organizationalunit'];
471
					}
472
					if (count($altnames)) {
473
						$altnames_tmp = "";
474
						foreach ($altnames as $altname) {
475
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
476
						}
477
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
478
					}
479

    
480
					if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) {
481
						$input_errors = array();
482
						while ($ssl_err = openssl_error_string()) {
483
							if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
484
								array_push($input_errors, "openssl library returns: " . $ssl_err);
485
							}
486
						}
487
					}
488
				}
489

    
490
				error_reporting($old_err_level);
491

    
492
				if (isset($id) && $a_cert[$id]) {
493
					$a_cert[$id] = $cert;
494
				} else {
495
					$a_cert[] = $cert;
496
				}
497

    
498
				if (isset($a_user) && isset($userid)) {
499
					$a_user[$userid]['cert'][] = $cert['refid'];
500
				}
501
			}
502

    
503
			if (!$input_errors) {
504
				write_config();
505
			}
506

    
507
			if ($userid && !$input_errors) {
508
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
509
				exit;
510
			}
511
		}
512
	}
513

    
514
	if ($_POST['save'] == gettext("Update")) {
515
		unset($input_errors);
516
		$pconfig = $_POST;
517

    
518
		/* input validation */
519
		$reqdfields = explode(" ", "descr cert");
520
		$reqdfieldsn = array(
521
			gettext("Descriptive name"),
522
			gettext("Final Certificate data"));
523

    
524
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
525

    
526
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
527
			array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
528
		}
529

    
530
//		old way
531
		/* make sure this csr and certificate subjects match */
532
//		$subj_csr = csr_get_subject($pconfig['csr'], false);
533
//		$subj_cert = cert_get_subject($pconfig['cert'], false);
534
//
535
//		if (!isset($_POST['ignoresubjectmismatch']) && !($_POST['ignoresubjectmismatch'] == "yes")) {
536
//			if (strcmp($subj_csr, $subj_cert)) {
537
//				$input_errors[] = sprintf(gettext("The certificate subject '%s' does not match the signing request subject."), $subj_cert);
538
//				$subject_mismatch = true;
539
//			}
540
//		}
541
		$mod_csr = csr_get_modulus($pconfig['csr'], false);
542
		$mod_cert = cert_get_modulus($pconfig['cert'], false);
543

    
544
		if (strcmp($mod_csr, $mod_cert)) {
545
			// simply: if the moduli don't match, then the private key and public key won't match
546
			$input_errors[] = sprintf(gettext("The certificate modulus does not match the signing request modulus."), $subj_cert);
547
			$subject_mismatch = true;
548
		}
549

    
550
		/* save modifications */
551
		if (!$input_errors) {
552

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

    
555
			$cert['descr'] = $pconfig['descr'];
556

    
557
			csr_complete($cert, $pconfig['cert']);
558

    
559
			$a_cert[$id] = $cert;
560

    
561
			write_config();
562

    
563
			pfSenseHeader("system_certmanager.php");
564
		}
565
	}
566
}
567

    
568
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
569
$pglinks = array("", "system_camanager.php", "system_certmanager.php");
570

    
571
if (($act == "new" || ($_POST['save'] == gettext("Save") && $input_errors)) || ($act == "csr" || ($_POST['save'] == gettext("Update") && $input_errors))) {
572
	$pgtitle[] = gettext('Edit');
573
	$pglinks[] = "@self";
574
}
575
include("head.inc");
576

    
577
if ($input_errors) {
578
	print_input_errors($input_errors);
579
}
580

    
581
if ($savemsg) {
582
	print_info_box($savemsg, 'success');
583
}
584

    
585
$tab_array = array();
586
$tab_array[] = array(gettext("CAs"), false, "system_camanager.php");
587
$tab_array[] = array(gettext("Certificates"), true, "system_certmanager.php");
588
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
589
display_top_tabs($tab_array);
590

    
591
// Load valid country codes
592
$dn_cc = array();
593
if (file_exists("/etc/ca_countries")) {
594
	$dn_cc_file=file("/etc/ca_countries");
595
	foreach ($dn_cc_file as $line) {
596
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
597
			$dn_cc[$matches[1]] = $matches[1];
598
		}
599
	}
600
}
601

    
602
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
603
	$form = new Form();
604
	$form->setAction('system_certmanager.php?act=edit');
605

    
606
	if (isset($userid) && $a_user) {
607
		$form->addGlobal(new Form_Input(
608
			'userid',
609
			null,
610
			'hidden',
611
			$userid
612
		));
613
	}
614

    
615
	if (isset($id) && $a_cert[$id]) {
616
		$form->addGlobal(new Form_Input(
617
			'id',
618
			null,
619
			'hidden',
620
			$id
621
		));
622
	}
623

    
624
	$section = new Form_Section('Add/Sign a New Certificate');
625

    
626
	if (!isset($id)) {
627
		$section->addInput(new Form_Select(
628
			'method',
629
			'*Method',
630
			$pconfig['method'],
631
			$cert_methods
632
		))->toggles();
633
	}
634

    
635
	$section->addInput(new Form_Input(
636
		'descr',
637
		'*Descriptive name',
638
		'text',
639
		($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
640
	))->addClass('toggle-existing');
641

    
642
	$form->add($section);
643

    
644
	function list_cas() {
645
		global $a_ca;
646
		$idx = 0;
647
		$allCas = array();
648

    
649
		foreach ($a_ca as $ca) {
650
			if ($ca['prv']) {
651
				$allCas[$idx] = $ca['descr'];
652
			}
653

    
654
			$idx++;
655
		}
656

    
657
		return $allCas;
658
	}
659

    
660
	function list_csrs() {
661
		global $config;
662
		$allCsrs = array();
663
		$idx = 0;
664

    
665
		foreach ($config['cert'] as $cert) {
666
			if ($cert['csr']) {
667
				$allCsrs[$idx] = $cert['descr'];
668
			}
669

    
670
			$idx++;
671
		}
672

    
673
		return ['new' => gettext('New')] + $allCsrs;
674
	}
675

    
676
	$section = new Form_Section('Sign CSR');
677
	$section->addClass('toggle-sign collapse');
678

    
679
	$section->AddInput(new Form_Select(
680
		'catosignwith',
681
		'*CA to sign with',
682
		$pconfig['catosignwith'],
683
		list_cas()
684
	));
685

    
686
	$section->AddInput(new Form_Select(
687
		'csrtosign',
688
		'*CSR to sign',
689
		isset($pconfig['csrtosign']) ? $pconfig['csrtosign'] : 'new',
690
		list_csrs()
691
	));
692

    
693
	$section->addInput(new Form_Input(
694
		'duration',
695
		'*Certificate duration (days)',
696
		'number',
697
		$pconfig['duration'] ? $pconfig['duration']:'365'
698
	));
699

    
700
	$section->addInput(new Form_Textarea(
701
		'csrpaste',
702
		'CSR data',
703
		$pconfig['csrpaste']
704
	))->setHelp('Paste a Certificate Signing Request in X.509 PEM format here.');
705

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

    
712
	$form->add($section);
713

    
714
	$section = new Form_Section('Import Certificate');
715
	$section->addClass('toggle-import collapse');
716

    
717
	$section->addInput(new Form_Textarea(
718
		'cert',
719
		'*Certificate data',
720
		$pconfig['cert']
721
	))->setHelp('Paste a certificate in X.509 PEM format here.');
722

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

    
729
	$form->add($section);
730
	$section = new Form_Section('Internal Certificate');
731
	$section->addClass('toggle-internal collapse');
732

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

    
747
			$allCas[ $ca['refid'] ] = $ca['descr'];
748
		}
749

    
750
		$section->addInput(new Form_Select(
751
			'caref',
752
			'*Certificate authority',
753
			$pconfig['caref'],
754
			$allCas
755
		));
756
	}
757

    
758
	$section->addInput(new Form_Select(
759
		'keylen',
760
		'*Key length',
761
		$pconfig['keylen'],
762
		array_combine($cert_keylens, $cert_keylens)
763
	));
764

    
765
	$section->addInput(new Form_Select(
766
		'digest_alg',
767
		'*Digest Algorithm',
768
		$pconfig['digest_alg'],
769
		array_combine($openssl_digest_algs, $openssl_digest_algs)
770
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
771
		'SHA1 when possible.');
772

    
773
	$section->addInput(new Form_Select(
774
		'type',
775
		'*Certificate Type',
776
		$pconfig['type'],
777
		$cert_types
778
	))->setHelp('Type of certificate to generate. Used for placing '.
779
		'restrictions on the usage of the generated certificate.');
780

    
781
	$section->addInput(new Form_Input(
782
		'lifetime',
783
		'*Lifetime (days)',
784
		'number',
785
		$pconfig['lifetime']
786
	));
787

    
788
	$section->addInput(new Form_Select(
789
		'dn_country',
790
		'*Country Code',
791
		$pconfig['dn_country'],
792
		$dn_cc
793
	));
794

    
795
	$section->addInput(new Form_Input(
796
		'dn_state',
797
		'*State or Province',
798
		'text',
799
		$pconfig['dn_state'],
800
		['placeholder' => 'e.g. Texas']
801
	));
802

    
803
	$section->addInput(new Form_Input(
804
		'dn_city',
805
		'*City',
806
		'text',
807
		$pconfig['dn_city'],
808
		['placeholder' => 'e.g. Austin']
809
	));
810

    
811
	$section->addInput(new Form_Input(
812
		'dn_organization',
813
		'*Organization',
814
		'text',
815
		$pconfig['dn_organization'],
816
		['placeholder' => 'e.g. My Company Inc']
817
	));
818

    
819
	$section->addInput(new Form_Input(
820
		'dn_organizationalunit',
821
		'Organizational Unit',
822
		'text',
823
		$pconfig['dn_organizationalunit'],
824
		['placeholder' => 'e.g. My Department Name (optional)']
825
	));
826

    
827
	$section->addInput(new Form_Input(
828
		'dn_email',
829
		'*Email Address',
830
		'text',
831
		$pconfig['dn_email'],
832
		['placeholder' => 'e.g. admin@mycompany.com']
833
	));
834

    
835
	$section->addInput(new Form_Input(
836
		'dn_commonname',
837
		'*Common Name',
838
		'text',
839
		$pconfig['dn_commonname'],
840
		['placeholder' => 'e.g. www.example.com']
841
	));
842

    
843
	if (empty($pconfig['altnames']['item'])) {
844
		$pconfig['altnames']['item'] = array(
845
			array('type' => null, 'value' => null)
846
		);
847
	}
848

    
849
	$counter = 0;
850
	$numrows = count($pconfig['altnames']['item']) - 1;
851

    
852
	foreach ($pconfig['altnames']['item'] as $item) {
853

    
854
		$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
855

    
856
		$group->add(new Form_Select(
857
			'altname_type' . $counter,
858
			'Type',
859
			$item['type'],
860
			array(
861
				'DNS' => gettext('FQDN or Hostname'),
862
				'IP' => gettext('IP address'),
863
				'URI' => gettext('URI'),
864
				'email' => gettext('email address'),
865
			)
866
		))->setHelp(($counter == $numrows) ? 'Type':null);
867

    
868
		$group->add(new Form_Input(
869
			'altname_value' . $counter,
870
			null,
871
			'text',
872
			$item['value']
873
		))->setHelp(($counter == $numrows) ? 'Value':null);
874

    
875
		$group->add(new Form_Button(
876
			'deleterow' . $counter,
877
			'Delete',
878
			null,
879
			'fa-trash'
880
		))->addClass('btn-warning');
881

    
882
		$group->addClass('repeatable');
883

    
884
		$section->add($group);
885

    
886
		$counter++;
887
	}
888

    
889
	$section->addInput(new Form_Button(
890
		'addrow',
891
		'Add',
892
		null,
893
		'fa-plus'
894
	))->addClass('btn-success');
895

    
896

    
897
	$form->add($section);
898
	$section = new Form_Section('External Signing Request');
899
	$section->addClass('toggle-external collapse');
900

    
901
	$section->addInput(new Form_Select(
902
		'csr_keylen',
903
		'*Key length',
904
		$pconfig['csr_keylen'],
905
		array_combine($cert_keylens, $cert_keylens)
906
	));
907

    
908
	$section->addInput(new Form_Select(
909
		'csr_digest_alg',
910
		'*Digest Algorithm',
911
		$pconfig['csr_digest_alg'],
912
		array_combine($openssl_digest_algs, $openssl_digest_algs)
913
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
914
		'SHA1 when possible');
915

    
916
	$section->addInput(new Form_Select(
917
		'csr_dn_country',
918
		'*Country Code',
919
		$pconfig['csr_dn_country'],
920
		$dn_cc
921
	));
922

    
923
	$section->addInput(new Form_Input(
924
		'csr_dn_state',
925
		'*State or Province',
926
		'text',
927
		$pconfig['csr_dn_state'],
928
		['placeholder' => 'e.g. Texas']
929
	));
930

    
931
	$section->addInput(new Form_Input(
932
		'csr_dn_city',
933
		'*City',
934
		'text',
935
		$pconfig['csr_dn_city'],
936
		['placeholder' => 'e.g. Austin']
937
	));
938

    
939
	$section->addInput(new Form_Input(
940
		'csr_dn_organization',
941
		'*Organization',
942
		'text',
943
		$pconfig['csr_dn_organization'],
944
		['placeholder' => 'e.g. My Company Inc']
945
	));
946

    
947
	$section->addInput(new Form_Input(
948
		'csr_dn_organizationalunit',
949
		'Organizational Unit',
950
		'text',
951
		$pconfig['csr_dn_organizationalunit'],
952
		['placeholder' => 'e.g. My Department Name (optional)']
953
	));
954

    
955
	$section->addInput(new Form_Input(
956
		'csr_dn_email',
957
		'*Email Address',
958
		'text',
959
		$pconfig['csr_dn_email'],
960
		['placeholder' => 'e.g. admin@mycompany.com']
961
	));
962

    
963
	$section->addInput(new Form_Input(
964
		'csr_dn_commonname',
965
		'*Common Name',
966
		'text',
967
		$pconfig['csr_dn_commonname'],
968
		['placeholder' => 'e.g. internal-ca']
969
	));
970

    
971
	$form->add($section);
972
	$section = new Form_Section('Choose an Existing Certificate');
973
	$section->addClass('toggle-existing collapse');
974

    
975
	$existCerts = array();
976

    
977
	foreach ($config['cert'] as $cert)	{
978
		if (is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
979
			if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert'])) {
980
				continue;
981
			}
982
		}
983

    
984
		$ca = lookup_ca($cert['caref']);
985
		if ($ca) {
986
			$cert['descr'] .= " (CA: {$ca['descr']})";
987
		}
988

    
989
		if (cert_in_use($cert['refid'])) {
990
			$cert['descr'] .= " (In Use)";
991
		}
992
		if (is_cert_revoked($cert)) {
993
			$cert['descr'] .= " (Revoked)";
994
		}
995

    
996
		$existCerts[ $cert['refid'] ] = $cert['descr'];
997
	}
998

    
999
	$section->addInput(new Form_Select(
1000
		'certref',
1001
		'*Existing Certificates',
1002
		$pconfig['certref'],
1003
		$existCerts
1004
	));
1005

    
1006
	$form->add($section);
1007
	print $form;
1008

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

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

    
1015
	$section->addInput(new Form_Input(
1016
		'descr',
1017
		'*Descriptive name',
1018
		'text',
1019
		$pconfig['descr']
1020
	));
1021

    
1022
	$section->addInput(new Form_Textarea(
1023
		'csr',
1024
		'Signing request data',
1025
		$pconfig['csr']
1026
	))->setReadonly()
1027
	  ->setWidth(7)
1028
	  ->setHelp('Copy the certificate signing data from here and forward it to a certificate authority for signing.');
1029

    
1030
	$section->addInput(new Form_Textarea(
1031
		'cert',
1032
		'*Final certificate data',
1033
		$pconfig['cert']
1034
	))->setWidth(7)
1035
	  ->setHelp('Paste the certificate received from the certificate authority here.');
1036

    
1037
	 if (isset($id) && $a_cert[$id]) {
1038
		 $section->addInput(new Form_Input(
1039
			'id',
1040
			null,
1041
			'hidden',
1042
			$id
1043
		 ));
1044

    
1045
		 $section->addInput(new Form_Input(
1046
			'act',
1047
			null,
1048
			'hidden',
1049
			'csr'
1050
		 ));
1051
	 }
1052

    
1053
	$form->add($section);
1054

    
1055
	$form->addGlobal(new Form_Button(
1056
		'save',
1057
		'Update',
1058
		null,
1059
		'fa-save'
1060
	))->addClass('btn-primary');
1061

    
1062
	print($form);
1063
} else {
1064
?>
1065
<div class="panel panel-default">
1066
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificates')?></h2></div>
1067
	<div class="panel-body">
1068
		<div class="table-responsive">
1069
		<table class="table table-striped table-hover">
1070
			<thead>
1071
				<tr>
1072
					<th><?=gettext("Name")?></th>
1073
					<th><?=gettext("Issuer")?></th>
1074
					<th><?=gettext("Distinguished Name")?></th>
1075
					<th><?=gettext("In Use")?></th>
1076

    
1077
					<th class="col-sm-2"><?=gettext("Actions")?></th>
1078
				</tr>
1079
			</thead>
1080
			<tbody>
1081
<?php
1082

    
1083
$pluginparams = array();
1084
$pluginparams['type'] = 'certificates';
1085
$pluginparams['event'] = 'used_certificates';
1086
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
1087
$i = 0;
1088
foreach ($a_cert as $i => $cert):
1089
	$name = htmlspecialchars($cert['descr']);
1090

    
1091
	if ($cert['crt']) {
1092
		$subj = cert_get_subject($cert['crt']);
1093
		$issuer = cert_get_issuer($cert['crt']);
1094
		$purpose = cert_get_purpose($cert['crt']);
1095
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
1096

    
1097
		if ($subj == $issuer) {
1098
			$caname = '<i>'. gettext("self-signed") .'</i>';
1099
		} else {
1100
			$caname = '<i>'. gettext("external").'</i>';
1101
		}
1102

    
1103
		$subj = htmlspecialchars($subj);
1104
	} else {
1105
		$subj = "";
1106
		$issuer = "";
1107
		$purpose = "";
1108
		$startdate = "";
1109
		$enddate = "";
1110
		$caname = "<em>" . gettext("private key only") . "</em>";
1111
	}
1112

    
1113
	if ($cert['csr']) {
1114
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
1115
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
1116
	}
1117

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

    
1209
<nav class="action-buttons">
1210
	<a href="?act=new" class="btn btn-success btn-sm">
1211
		<i class="fa fa-plus icon-embed-btn"></i>
1212
		<?=gettext("Add/Sign")?>
1213
	</a>
1214
</nav>
1215
<?php
1216
	include("foot.inc");
1217
	exit;
1218
}
1219

    
1220

    
1221
?>
1222
<script type="text/javascript">
1223
//<![CDATA[
1224
events.push(function() {
1225

    
1226
<?php if ($internal_ca_count): ?>
1227
	function internalca_change() {
1228

    
1229
		caref = $('#caref').val();
1230

    
1231
		switch (caref) {
1232
<?php
1233
			foreach ($a_ca as $ca):
1234
				if (!$ca['prv']) {
1235
					continue;
1236
				}
1237

    
1238
				$subject = cert_get_subject_array($ca['crt']);
1239
?>
1240
				case "<?=$ca['refid'];?>":
1241
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1242
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1243
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1244
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1245
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1246
					$('#dn_organizationalunit').val("<?=$subject[6]['v'];?>");
1247
					break;
1248
<?php
1249
			endforeach;
1250
?>
1251
		}
1252
	}
1253

    
1254
	function set_csr_ro() {
1255
		var newcsr = ($('#csrtosign').val() == "new");
1256

    
1257
		$('#csrpaste').attr('readonly', !newcsr);
1258
		$('#keypaste').attr('readonly', !newcsr);
1259
		setRequired('csrpaste', newcsr);
1260
		setRequired('keypaste', newcsr);
1261
	}
1262

    
1263
	// ---------- Click checkbox handlers ---------------------------------------------------------
1264

    
1265
	$('#caref').on('change', function() {
1266
		internalca_change();
1267
	});
1268

    
1269
	$('#csrtosign').change(function () {
1270
		set_csr_ro();
1271
	});
1272

    
1273
	// ---------- On initial page load ------------------------------------------------------------
1274

    
1275
	internalca_change();
1276
	set_csr_ro();
1277

    
1278
	// Suppress "Delete row" button if there are fewer than two rows
1279
	checkLastRow();
1280

    
1281
<?php endif; ?>
1282

    
1283

    
1284
});
1285
//]]>
1286
</script>
1287
<?php
1288
include('foot.inc');
(191-191/223)