Project

General

Profile

Download (30.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
);
38

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

    
44
$altname_types = array("DNS", "IP", "email", "URI");
45
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512", "whirlpool");
46

    
47
if (is_numericint($_GET['userid'])) {
48
	$userid = $_GET['userid'];
49
}
50
if (isset($_POST['userid']) && is_numericint($_POST['userid'])) {
51
	$userid = $_POST['userid'];
52
}
53

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

    
62
if (is_numericint($_GET['id'])) {
63
	$id = $_GET['id'];
64
}
65
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
66
	$id = $_POST['id'];
67
}
68

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

    
73
$a_ca =& $config['ca'];
74

    
75
if (!is_array($config['cert'])) {
76
	$config['cert'] = array();
77
}
78

    
79
$a_cert =& $config['cert'];
80

    
81
$internal_ca_count = 0;
82
foreach ($a_ca as $ca) {
83
	if ($ca['prv']) {
84
		$internal_ca_count++;
85
	}
86
}
87

    
88
$act = $_GET['act'];
89

    
90
if ($_POST['act']) {
91
	$act = $_POST['act'];
92
}
93

    
94
if ($act == "del") {
95

    
96
	if (!isset($a_cert[$id])) {
97
		pfSenseHeader("system_certmanager.php");
98
		exit;
99
	}
100

    
101
	unset($a_cert[$id]);
102
	write_config();
103
	$savemsg = sprintf(gettext("Certificate %s successfully deleted."), htmlspecialchars($a_cert[$id]['descr']));
104
	pfSenseHeader("system_certmanager.php");
105
	exit;
106
}
107

    
108

    
109
if ($act == "new") {
110
	$pconfig['method'] = $_GET['method'];
111
	$pconfig['keylen'] = "2048";
112
	$pconfig['digest_alg'] = "sha256";
113
	$pconfig['csr_keylen'] = "2048";
114
	$pconfig['csr_digest_alg'] = "sha256";
115
	$pconfig['type'] = "user";
116
	$pconfig['lifetime'] = "3650";
117
}
118

    
119
if ($act == "exp") {
120

    
121
	if (!$a_cert[$id]) {
122
		pfSenseHeader("system_certmanager.php");
123
		exit;
124
	}
125

    
126
	$exp_name = urlencode("{$a_cert[$id]['descr']}.crt");
127
	$exp_data = base64_decode($a_cert[$id]['crt']);
128
	$exp_size = strlen($exp_data);
129

    
130
	header("Content-Type: application/octet-stream");
131
	header("Content-Disposition: attachment; filename={$exp_name}");
132
	header("Content-Length: $exp_size");
133
	echo $exp_data;
134
	exit;
135
}
136

    
137
if ($act == "req") {
138

    
139
	if (!$a_cert[$id]) {
140
		pfSenseHeader("system_certmanager.php");
141
		exit;
142
	}
143

    
144
	$exp_name = urlencode("{$a_cert[$id]['descr']}.req");
145
	$exp_data = base64_decode($a_cert[$id]['csr']);
146
	$exp_size = strlen($exp_data);
147

    
148
	header("Content-Type: application/octet-stream");
149
	header("Content-Disposition: attachment; filename={$exp_name}");
150
	header("Content-Length: $exp_size");
151
	echo $exp_data;
152
	exit;
153
}
154

    
155
if ($act == "key") {
156

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

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

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

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

    
179
	$exp_name = urlencode("{$a_cert[$id]['descr']}.p12");
180
	$args = array();
181
	$args['friendly_name'] = $a_cert[$id]['descr'];
182

    
183
	$ca = lookup_ca($a_cert[$id]['caref']);
184
	if ($ca) {
185
		$args['extracerts'] = openssl_x509_read(base64_decode($ca['crt']));
186
	}
187

    
188
	$res_crt = openssl_x509_read(base64_decode($a_cert[$id]['crt']));
189
	$res_key = openssl_pkey_get_private(array(0 => base64_decode($a_cert[$id]['prv']) , 1 => ""));
190

    
191
	$exp_data = "";
192
	openssl_pkcs12_export($res_crt, $exp_data, $res_key, null, $args);
193
	$exp_size = strlen($exp_data);
194

    
195
	header("Content-Type: application/octet-stream");
196
	header("Content-Disposition: attachment; filename={$exp_name}");
197
	header("Content-Length: $exp_size");
198
	echo $exp_data;
199
	exit;
200
}
201

    
202
if ($act == "csr") {
203
	if (!$a_cert[$id]) {
204
		pfSenseHeader("system_certmanager.php");
205
		exit;
206
	}
207

    
208
	$pconfig['descr'] = $a_cert[$id]['descr'];
209
	$pconfig['csr'] = base64_decode($a_cert[$id]['csr']);
210
}
211

    
212
if ($_POST) {
213
	// This is just the blank alternate name that is added for display purposes. We don't want to validate/save it
214
	if ($_POST['altname_value0'] == "") {
215
		unset($_POST['altname_type0']);
216
		unset($_POST['altname_value0']);
217
	}
218

    
219
	if ($_POST['save'] == gettext("Save")) {
220
		$input_errors = array();
221
		$pconfig = $_POST;
222

    
223
		/* input validation */
224
		if ($pconfig['method'] == "import") {
225
			$reqdfields = explode(" ",
226
				"descr cert key");
227
			$reqdfieldsn = array(
228
				gettext("Descriptive name"),
229
				gettext("Certificate data"),
230
				gettext("Key data"));
231
			if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
232
				$input_errors[] = gettext("This certificate does not appear to be valid.");
233
			}
234
		}
235

    
236
		if ($pconfig['method'] == "internal") {
237
			$reqdfields = explode(" ",
238
				"descr caref keylen type lifetime dn_country dn_state dn_city ".
239
				"dn_organization dn_email dn_commonname");
240
			$reqdfieldsn = array(
241
				gettext("Descriptive name"),
242
				gettext("Certificate authority"),
243
				gettext("Key length"),
244
				gettext("Certificate Type"),
245
				gettext("Lifetime"),
246
				gettext("Distinguished name Country Code"),
247
				gettext("Distinguished name State or Province"),
248
				gettext("Distinguished name City"),
249
				gettext("Distinguished name Organization"),
250
				gettext("Distinguished name Email Address"),
251
				gettext("Distinguished name Common Name"));
252
		}
253

    
254
		if ($pconfig['method'] == "external") {
255
			$reqdfields = explode(" ",
256
				"descr csr_keylen csr_dn_country csr_dn_state csr_dn_city ".
257
				"csr_dn_organization csr_dn_email csr_dn_commonname");
258
			$reqdfieldsn = array(
259
				gettext("Descriptive name"),
260
				gettext("Key length"),
261
				gettext("Distinguished name Country Code"),
262
				gettext("Distinguished name State or Province"),
263
				gettext("Distinguished name City"),
264
				gettext("Distinguished name Organization"),
265
				gettext("Distinguished name Email Address"),
266
				gettext("Distinguished name Common Name"));
267
		}
268

    
269
		if ($pconfig['method'] == "existing") {
270
			$reqdfields = array("certref");
271
			$reqdfieldsn = array(gettext("Existing Certificate Choice"));
272
		}
273

    
274
		$altnames = array();
275
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
276
		if ($pconfig['method'] != "import" && $pconfig['method'] != "existing") {
277
			/* subjectAltNames */
278
			foreach ($_POST as $key => $value) {
279
				$entry = '';
280
				if (!substr_compare('altname_type', $key, 0, 12)) {
281
					$entry = substr($key, 12);
282
					$field = 'type';
283
				} elseif (!substr_compare('altname_value', $key, 0, 13)) {
284
					$entry = substr($key, 13);
285
					$field = 'value';
286
				}
287

    
288
				if (ctype_digit($entry)) {
289
					$entry++;	// Pre-bootstrap code is one-indexed, but the bootstrap code is 0-indexed
290
					$altnames[$entry][$field] = $value;
291
				}
292
			}
293

    
294
			$pconfig['altnames']['item'] = $altnames;
295

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

    
328
			/* Make sure we do not have invalid characters in the fields for the certificate */
329

    
330
			if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
331
				array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
332
			}
333

    
334
			for ($i = 0; $i < count($reqdfields); $i++) {
335
				if (preg_match('/email/', $reqdfields[$i])) { /* dn_email or csr_dn_name */
336
					if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) {
337
						array_push($input_errors, gettext("The field 'Distinguished name Email Address' contains invalid characters."));
338
					}
339
				} else if (preg_match('/commonname/', $reqdfields[$i])) { /* dn_commonname or csr_dn_commonname */
340
					if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) {
341
						array_push($input_errors, gettext("The field 'Distinguished name Common Name' contains invalid characters."));
342
					}
343
				} else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST[$reqdfields[$i]])) {
344
					array_push($input_errors, sprintf(gettext("The field '%s' contains invalid characters."), $reqdfieldsn[$i]));
345
				}
346
			}
347

    
348
			if (($pconfig['method'] != "external") && isset($_POST["keylen"]) && !in_array($_POST["keylen"], $cert_keylens)) {
349
				array_push($input_errors, gettext("Please select a valid Key Length."));
350
			}
351
			if (($pconfig['method'] != "external") && !in_array($_POST["digest_alg"], $openssl_digest_algs)) {
352
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
353
			}
354

    
355
			if (($pconfig['method'] == "external") && isset($_POST["csr_keylen"]) && !in_array($_POST["csr_keylen"], $cert_keylens)) {
356
				array_push($input_errors, gettext("Please select a valid Key Length."));
357
			}
358
			if (($pconfig['method'] == "external") && !in_array($_POST["csr_digest_alg"], $openssl_digest_algs)) {
359
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
360
			}
361
		}
362

    
363
		/* save modifications */
364
		if (!$input_errors) {
365

    
366
			if ($pconfig['method'] == "existing") {
367
				$cert = lookup_cert($pconfig['certref']);
368
				if ($cert && $a_user) {
369
					$a_user[$userid]['cert'][] = $cert['refid'];
370
				}
371
			} else {
372
				$cert = array();
373
				$cert['refid'] = uniqid();
374
				if (isset($id) && $a_cert[$id]) {
375
					$cert = $a_cert[$id];
376
				}
377

    
378
				$cert['descr'] = $pconfig['descr'];
379

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

    
382
				if ($pconfig['method'] == "import") {
383
					cert_import($cert, $pconfig['cert'], $pconfig['key']);
384
				}
385

    
386
				if ($pconfig['method'] == "internal") {
387
					$dn = array(
388
						'countryName' => $pconfig['dn_country'],
389
						'stateOrProvinceName' => $pconfig['dn_state'],
390
						'localityName' => $pconfig['dn_city'],
391
						'organizationName' => $pconfig['dn_organization'],
392
						'emailAddress' => $pconfig['dn_email'],
393
						'commonName' => $pconfig['dn_commonname']);
394
					if (!empty($pconfig['dn_organizationalunit'])) {
395
						$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
396
					}
397
					if (count($altnames)) {
398
						$altnames_tmp = "";
399
						foreach ($altnames as $altname) {
400
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
401
						}
402

    
403
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
404
					}
405

    
406
					if (!cert_create($cert, $pconfig['caref'], $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['type'], $pconfig['digest_alg'])) {
407
						while ($ssl_err = openssl_error_string()) {
408
							$input_errors = array();
409
							array_push($input_errors, "openssl library returns: " . $ssl_err);
410
						}
411
					}
412
				}
413

    
414
				if ($pconfig['method'] == "external") {
415
					$dn = array(
416
						'countryName' => $pconfig['csr_dn_country'],
417
						'stateOrProvinceName' => $pconfig['csr_dn_state'],
418
						'localityName' => $pconfig['csr_dn_city'],
419
						'organizationName' => $pconfig['csr_dn_organization'],
420
						'emailAddress' => $pconfig['csr_dn_email'],
421
						'commonName' => $pconfig['csr_dn_commonname']);
422
					if (!empty($pconfig['csr_dn_organizationalunit'])) {
423
						$dn['organizationalUnitName'] = $pconfig['csr_dn_organizationalunit'];
424
					}
425
					if (count($altnames)) {
426
						$altnames_tmp = "";
427
						foreach ($altnames as $altname) {
428
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
429
						}
430
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
431
					}
432

    
433
					if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) {
434
						while ($ssl_err = openssl_error_string()) {
435
							$input_errors = array();
436
							array_push($input_errors, "openssl library returns: " . $ssl_err);
437
						}
438
					}
439
				}
440
				error_reporting($old_err_level);
441

    
442
				if (isset($id) && $a_cert[$id]) {
443
					$a_cert[$id] = $cert;
444
				} else {
445
					$a_cert[] = $cert;
446
				}
447

    
448
				if (isset($a_user) && isset($userid)) {
449
					$a_user[$userid]['cert'][] = $cert['refid'];
450
				}
451
			}
452

    
453
			if (!$input_errors) {
454
				write_config();
455
			}
456

    
457
			if ($userid) {
458
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
459
				exit;
460
			}
461
		}
462
	}
463

    
464
	if ($_POST['save'] == gettext("Update")) {
465
		unset($input_errors);
466
		$pconfig = $_POST;
467

    
468
		/* input validation */
469
		$reqdfields = explode(" ", "descr cert");
470
		$reqdfieldsn = array(
471
			gettext("Descriptive name"),
472
			gettext("Final Certificate data"));
473

    
474
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
475

    
476
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
477
			array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
478
		}
479

    
480
//		old way
481
		/* make sure this csr and certificate subjects match */
482
//		$subj_csr = csr_get_subject($pconfig['csr'], false);
483
//		$subj_cert = cert_get_subject($pconfig['cert'], false);
484
//
485
//		if (!isset($_POST['ignoresubjectmismatch']) && !($_POST['ignoresubjectmismatch'] == "yes")) {
486
//			if (strcmp($subj_csr, $subj_cert)) {
487
//				$input_errors[] = sprintf(gettext("The certificate subject '%s' does not match the signing request subject."), $subj_cert);
488
//				$subject_mismatch = true;
489
//			}
490
//		}
491
		$mod_csr = csr_get_modulus($pconfig['csr'], false);
492
		$mod_cert = cert_get_modulus($pconfig['cert'], false);
493

    
494
		if (strcmp($mod_csr, $mod_cert)) {
495
			// simply: if the moduli don't match, then the private key and public key won't match
496
			$input_errors[] = sprintf(gettext("The certificate modulus does not match the signing request modulus."), $subj_cert);
497
			$subject_mismatch = true;
498
		}
499

    
500
		/* save modifications */
501
		if (!$input_errors) {
502

    
503
			$cert = $a_cert[$id];
504

    
505
			$cert['descr'] = $pconfig['descr'];
506

    
507
			csr_complete($cert, $pconfig['cert']);
508

    
509
			$a_cert[$id] = $cert;
510

    
511
			write_config();
512

    
513
			pfSenseHeader("system_certmanager.php");
514
		}
515
	}
516
}
517

    
518
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
519

    
520
if (($act == "new" || ($_POST['save'] == gettext("Save") && $input_errors)) || ($act == "csr" || ($_POST['save'] == gettext("Update") && $input_errors))) {
521
	$pgtitle[] = gettext('Edit');
522
}
523
include("head.inc");
524

    
525
if ($input_errors) {
526
	print_input_errors($input_errors);
527
}
528

    
529
if ($savemsg) {
530
	print_info_box($savemsg, 'success');
531
}
532

    
533
$tab_array = array();
534
$tab_array[] = array(gettext("CAs"), false, "system_camanager.php");
535
$tab_array[] = array(gettext("Certificates"), true, "system_certmanager.php");
536
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
537
display_top_tabs($tab_array);
538

    
539
// Load valid country codes
540
$dn_cc = array();
541
if (file_exists("/etc/ca_countries")) {
542
	$dn_cc_file=file("/etc/ca_countries");
543
	foreach ($dn_cc_file as $line) {
544
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
545
			$dn_cc[$matches[1]] = $matches[1];
546
		}
547
	}
548
}
549

    
550
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
551
	$form = new Form();
552
	$form->setAction('system_certmanager.php?act=edit');
553

    
554
	if (isset($userid) && $a_user) {
555
		$form->addGlobal(new Form_Input(
556
			'userid',
557
			null,
558
			'hidden',
559
			$userid
560
		));
561
	}
562

    
563
	if (isset($id) && $a_cert[$id]) {
564
		$form->addGlobal(new Form_Input(
565
			'id',
566
			null,
567
			'hidden',
568
			$id
569
		));
570
	}
571

    
572
	$section = new Form_Section('Add a New Certificate');
573

    
574
	if (!isset($id)) {
575
		$section->addInput(new Form_Select(
576
			'method',
577
			'Method',
578
			$pconfig['method'],
579
			$cert_methods
580
		))->toggles();
581
	}
582

    
583
	$section->addInput(new Form_Input(
584
		'descr',
585
		'Descriptive name',
586
		'text',
587
		($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
588
	))->addClass('toggle-existing');
589

    
590
	$form->add($section);
591
	$section = new Form_Section('Import Certificate');
592
	$section->addClass('toggle-import collapse');
593

    
594
	$section->addInput(new Form_Textarea(
595
		'cert',
596
		'Certificate data',
597
		$pconfig['cert']
598
	))->setHelp('Paste a certificate in X.509 PEM format here.');
599

    
600
	$section->addInput(new Form_Textarea(
601
		'key',
602
		'Private key data',
603
		$pconfig['key']
604
	))->setHelp('Paste a private key in X.509 PEM format here.');
605

    
606
	$form->add($section);
607
	$section = new Form_Section('Internal Certificate');
608
	$section->addClass('toggle-internal collapse');
609

    
610
	if (!$internal_ca_count) {
611
		$section->addInput(new Form_StaticText(
612
			'Certificate authority',
613
			gettext('No internal Certificate Authorities have been defined. ').
614
			gettext('An internal CA must be defined in order to create an internal certificate. ').
615
			'<a href="system_camanager.php?act=new&amp;method=internal"> '. gettext("Create") .'</a>'.
616
			gettext(' an internal CA.')
617
		));
618
	} else {
619
		$allCas = array();
620
		foreach ($a_ca as $ca) {
621
			if (!$ca['prv']) {
622
				continue;
623
			}
624

    
625
			$allCas[ $ca['refid'] ] = $ca['descr'];
626
		}
627

    
628
		$section->addInput(new Form_Select(
629
			'caref',
630
			'Certificate authority',
631
			$pconfig['caref'],
632
			$allCas
633
		));
634
	}
635

    
636
	$section->addInput(new Form_Select(
637
		'keylen',
638
		'Key length',
639
		$pconfig['keylen'],
640
		array_combine($cert_keylens, $cert_keylens)
641
	));
642

    
643
	$section->addInput(new Form_Select(
644
		'digest_alg',
645
		'Digest Algorithm',
646
		$pconfig['digest_alg'],
647
		array_combine($openssl_digest_algs, $openssl_digest_algs)
648
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
649
		'SHA1 when possible.');
650

    
651
	$section->addInput(new Form_Select(
652
		'type',
653
		'Certificate Type',
654
		$pconfig['type'],
655
		$cert_types
656
	))->setHelp('Type of certificate to generate. Used for placing '.
657
		'restrictions on the usage of the generated certificate.');
658

    
659
	$section->addInput(new Form_Input(
660
		'lifetime',
661
		'Lifetime (days)',
662
		'number',
663
		$pconfig['lifetime']
664
	));
665

    
666
	$section->addInput(new Form_Select(
667
		'dn_country',
668
		'Country Code',
669
		$pconfig['dn_country'],
670
		$dn_cc
671
	));
672

    
673
	$section->addInput(new Form_Input(
674
		'dn_state',
675
		'State or Province',
676
		'text',
677
		$pconfig['dn_state'],
678
		['placeholder' => 'e.g. Texas']
679
	));
680

    
681
	$section->addInput(new Form_Input(
682
		'dn_city',
683
		'City',
684
		'text',
685
		$pconfig['dn_city'],
686
		['placeholder' => 'e.g. Austin']
687
	));
688

    
689
	$section->addInput(new Form_Input(
690
		'dn_organization',
691
		'Organization',
692
		'text',
693
		$pconfig['dn_organization'],
694
		['placeholder' => 'e.g. My Company Inc']
695
	));
696

    
697
	$section->addInput(new Form_Input(
698
		'dn_organizationalunit',
699
		'Organizational Unit',
700
		'text',
701
		$pconfig['dn_organizationalunit'],
702
		['placeholder' => 'e.g. My Department Name (optional)']
703
	));
704

    
705
	$section->addInput(new Form_Input(
706
		'dn_email',
707
		'Email Address',
708
		'text',
709
		$pconfig['dn_email'],
710
		['placeholder' => 'e.g. admin@mycompany.com']
711
	));
712

    
713
	$section->addInput(new Form_Input(
714
		'dn_commonname',
715
		'Common Name',
716
		'text',
717
		$pconfig['dn_commonname'],
718
		['placeholder' => 'e.g. www.example.com']
719
	));
720

    
721
	if (empty($pconfig['altnames']['item'])) {
722
		$pconfig['altnames']['item'] = array(
723
			array('type' => null, 'value' => null)
724
		);
725
	}
726

    
727
	$counter = 0;
728
	$numrows = count($pconfig['altnames']['item']) - 1;
729

    
730
	foreach ($pconfig['altnames']['item'] as $item) {
731

    
732
		$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
733

    
734
		$group->add(new Form_Select(
735
			'altname_type' . $counter,
736
			'Type',
737
			$item['type'],
738
			array(
739
				'DNS' => gettext('FQDN or Hostname'),
740
				'IP' => gettext('IP address'),
741
				'URI' => gettext('URI'),
742
				'email' => gettext('email address'),
743
			)
744
		))->setHelp(($counter == $numrows) ? 'Type':null);
745

    
746
		$group->add(new Form_Input(
747
			'altname_value' . $counter,
748
			null,
749
			'text',
750
			$item['value']
751
		))->setHelp(($counter == $numrows) ? 'Value':null);
752

    
753
		$group->add(new Form_Button(
754
			'deleterow' . $counter,
755
			'Delete',
756
			null,
757
			'fa-trash'
758
		))->addClass('btn-warning');
759

    
760
		$group->addClass('repeatable');
761

    
762
		$section->add($group);
763

    
764
		$counter++;
765
	}
766

    
767
	$section->addInput(new Form_Button(
768
		'addrow',
769
		'Add',
770
		null,
771
		'fa-plus'
772
	))->addClass('btn-success');
773

    
774
	$form->add($section);
775
	$section = new Form_Section('External Signing Request');
776
	$section->addClass('toggle-external collapse');
777

    
778
	$section->addInput(new Form_Select(
779
		'csr_keylen',
780
		'Key length',
781
		$pconfig['csr_keylen'],
782
		array_combine($cert_keylens, $cert_keylens)
783
	));
784

    
785
	$section->addInput(new Form_Select(
786
		'csr_digest_alg',
787
		'Digest Algorithm',
788
		$pconfig['csr_digest_alg'],
789
		array_combine($openssl_digest_algs, $openssl_digest_algs)
790
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
791
		'SHA1 when possible');
792

    
793
	$section->addInput(new Form_Select(
794
		'csr_dn_country',
795
		'Country Code',
796
		$pconfig['csr_dn_country'],
797
		$dn_cc
798
	));
799

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

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

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

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

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

    
840
	$section->addInput(new Form_Input(
841
		'csr_dn_commonname',
842
		'Common Name',
843
		'text',
844
		$pconfig['csr_dn_commonname'],
845
		['placeholder' => 'e.g. internal-ca']
846
	));
847

    
848
	$form->add($section);
849
	$section = new Form_Section('Choose an Existing Certificate');
850
	$section->addClass('toggle-existing collapse');
851

    
852
	$existCerts = array();
853

    
854
	foreach ($config['cert'] as $cert)	{
855
		if (is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
856
			if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert'])) {
857
				continue;
858
			}
859
		}
860

    
861
		$ca = lookup_ca($cert['caref']);
862
		if ($ca) {
863
			$cert['descr'] .= " (CA: {$ca['descr']})";
864
		}
865

    
866
		if (cert_in_use($cert['refid'])) {
867
			$cert['descr'] .= " <i>In Use</i>";
868
		}
869
		if (is_cert_revoked($cert)) {
870
			$cert['descr'] .= " <b>Revoked</b>";
871
		}
872

    
873
		$existCerts[ $cert['refid'] ] = $cert['descr'];
874
	}
875

    
876
	$section->addInput(new Form_Select(
877
		'certref',
878
		'Existing Certificates',
879
		$pconfig['certref'],
880
		$existCerts
881
	));
882

    
883
	$form->add($section);
884
	print $form;
885

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

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

    
892
	$section->addInput(new Form_Input(
893
		'descr',
894
		'Descriptive name',
895
		'text',
896
		$pconfig['descr']
897
	));
898

    
899
	$section->addInput(new Form_Textarea(
900
		'csr',
901
		'Signing request data',
902
		$pconfig['csr']
903
	))->setReadonly()
904
	  ->setWidth(7)
905
	  ->setHelp('Copy the certificate signing data from here and forward it to a certificate authority for signing.');
906

    
907
	$section->addInput(new Form_Textarea(
908
		'cert',
909
		'Final certificate data',
910
		$pconfig['cert']
911
	))->setWidth(7)
912
	  ->setHelp('Paste the certificate received from the certificate authority here.');
913

    
914
	 if (isset($id) && $a_cert[$id]) {
915
		 $section->addInput(new Form_Input(
916
			'id',
917
			null,
918
			'hidden',
919
			$id
920
		 ));
921

    
922
		 $section->addInput(new Form_Input(
923
			'act',
924
			null,
925
			'hidden',
926
			'csr'
927
		 ));
928
	 }
929

    
930
	$form->add($section);
931

    
932
	$form->addGlobal(new Form_Button(
933
		'save',
934
		'Update',
935
		null,
936
		'fa-save'
937
	))->addClass('btn-primary');
938

    
939
	print($form);
940
} else {
941
?>
942
<div class="panel panel-default">
943
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificates')?></h2></div>
944
	<div class="panel-body">
945
		<div class="table-responsive">
946
		<table class="table table-striped table-hover">
947
			<thead>
948
				<tr>
949
					<th><?=gettext("Name")?></th>
950
					<th><?=gettext("Issuer")?></th>
951
					<th><?=gettext("Distinguished Name")?></th>
952
					<th><?=gettext("In Use")?></th>
953

    
954
					<th class="col-sm-2"><?=gettext("Actions")?></th>
955
				</tr>
956
			</thead>
957
			<tbody>
958
<?php
959

    
960
$pluginparams = array();
961
$pluginparams['type'] = 'certificates';
962
$pluginparams['event'] = 'used_certificates';
963
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
964
$i = 0;
965
foreach ($a_cert as $i => $cert):
966
	$name = htmlspecialchars($cert['descr']);
967

    
968
	if ($cert['crt']) {
969
		$subj = cert_get_subject($cert['crt']);
970
		$issuer = cert_get_issuer($cert['crt']);
971
		$purpose = cert_get_purpose($cert['crt']);
972
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
973

    
974
		if ($subj == $issuer) {
975
			$caname = '<i>'. gettext("self-signed") .'</i>';
976
		} else {
977
			$caname = '<i>'. gettext("external").'</i>';
978
		}
979

    
980
		$subj = htmlspecialchars($subj);
981
	}
982

    
983
	if ($cert['csr']) {
984
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
985
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
986
	}
987

    
988
	$ca = lookup_ca($cert['caref']);
989
	if ($ca) {
990
		$caname = $ca['descr'];
991
	}
992
?>
993
				<tr>
994
					<td>
995
						<?=$name?><br />
996
						<?php if ($cert['type']): ?>
997
							<i><?=$cert_types[$cert['type']]?></i><br />
998
						<?php endif?>
999
						<?php if (is_array($purpose)): ?>
1000
							CA: <b><?=$purpose['ca']?></b>, <?=gettext("Server")?>: <b><?=$purpose['server']?></b>
1001
						<?php endif?>
1002
					</td>
1003
					<td><?=$caname?></td>
1004
					<td>
1005
						<?=$subj?>
1006
						<?php if (!$cert['csr']): ?>
1007
						<br />
1008
						<small>
1009
							<?=gettext("Valid From")?>: <b><?=$startdate ?></b><br /><?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
1010
						</small>
1011
						<?php endif?>
1012
					</td>
1013
					<td>
1014
						<?php if (is_cert_revoked($cert)): ?>
1015
							<i><?=gettext("Revoked")?></i>
1016
						<?php endif?>
1017
						<?php if (is_webgui_cert($cert['refid'])): ?>
1018
							<?=gettext("webConfigurator")?>
1019
						<?php endif?>
1020
						<?php if (is_user_cert($cert['refid'])): ?>
1021
							<?=gettext("User Cert")?>
1022
						<?php endif?>
1023
						<?php if (is_openvpn_server_cert($cert['refid'])): ?>
1024
							<?=gettext("OpenVPN Server")?>
1025
						<?php endif?>
1026
						<?php if (is_openvpn_client_cert($cert['refid'])): ?>
1027
							<?=gettext("OpenVPN Client")?>
1028
						<?php endif?>
1029
						<?php if (is_ipsec_cert($cert['refid'])): ?>
1030
							<?=gettext("IPsec Tunnel")?>
1031
						<?php endif?>
1032
						<?php if (is_captiveportal_cert($cert['refid'])): ?>
1033
							<?=gettext("Captive Portal")?>
1034
						<?php endif?>
1035
<?php
1036
							$refid = $cert['refid'];
1037
							if (is_array($certificates_used_by_packages)) {
1038
								foreach ($certificates_used_by_packages as $name => $package) {
1039
									if (isset($package['certificatelist'][$refid])) {
1040
										$hint = "" ;
1041
										if (is_array($package['certificatelist'][$refid])) {
1042
											foreach ($package['certificatelist'][$refid] as $cert_used) {
1043
												$hint = $hint . $cert_used['usedby']."\n";
1044
											}
1045
										}
1046
										$count = count($package['certificatelist'][$refid]);
1047
										echo "<div title='".htmlspecialchars($hint)."'>";
1048
										echo htmlspecialchars($package['pkgname'])." ($count)<br />";
1049
										echo "</div>";
1050
									}
1051
								}
1052
							}
1053
?>
1054
					</td>
1055
					<td>
1056
						<?php if (!$cert['csr']): ?>
1057
							<a href="system_certmanager.php?act=exp&amp;id=<?=$i?>" class="fa fa-certificate" title="<?=gettext("Export Certificate")?>"></a>
1058
							<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1059
							<a href="system_certmanager.php?act=p12&amp;id=<?=$i?>" class="fa fa-archive" title="<?=gettext("Export P12")?>"></a>
1060
						<?php else: ?>
1061
							<a href="system_certmanager.php?act=csr&amp;id=<?=$i?>" class="fa fa-pencil" title="<?=gettext("Update CSR")?>"></a>
1062
							<a href="system_certmanager.php?act=req&amp;id=<?=$i?>" class="fa fa-sign-in" title="<?=gettext("Export Request")?>"></a>
1063
							<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1064
						<?php endif?>
1065
						<?php if (!cert_in_use($cert['refid'])): ?>
1066
							<a href="system_certmanager.php?act=del&amp;id=<?=$i?>" class="fa fa-trash" title="<?=gettext("Delete Certificate")?>"></a>
1067
						<?php endif?>
1068
					</td>
1069
				</tr>
1070
<?php
1071
	$i++; 
1072
	endforeach; ?>
1073
			</tbody>
1074
		</table>
1075
		</div>
1076
	</div>
1077
</div>
1078

    
1079
<nav class="action-buttons">
1080
	<a href="?act=new" class="btn btn-success btn-sm">
1081
		<i class="fa fa-plus icon-embed-btn"></i>
1082
		<?=gettext("Add")?>
1083
	</a>
1084
</nav>
1085
<?php
1086
	include("foot.inc");
1087
	exit;
1088
}
1089

    
1090

    
1091
?>
1092
<script type="text/javascript">
1093
//<![CDATA[
1094
events.push(function() {
1095

    
1096
<?php if ($internal_ca_count): ?>
1097
	function internalca_change() {
1098

    
1099
		caref = $('#caref').val();
1100

    
1101
		switch (caref) {
1102
<?php
1103
			foreach ($a_ca as $ca):
1104
				if (!$ca['prv']) {
1105
					continue;
1106
				}
1107

    
1108
				$subject = cert_get_subject_array($ca['crt']);
1109

    
1110
?>
1111
				case "<?=$ca['refid'];?>":
1112
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1113
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1114
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1115
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1116
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1117
					$('#dn_organizationalunit').val("<?=$subject[6]['v'];?>");
1118
					break;
1119
<?php
1120
			endforeach;
1121
?>
1122
		}
1123
	}
1124

    
1125
	// ---------- Click checkbox handlers ---------------------------------------------------------
1126

    
1127
	$('#caref').on('change', function() {
1128
		internalca_change();
1129
	});
1130

    
1131
	// ---------- On initial page load ------------------------------------------------------------
1132

    
1133
	internalca_change();
1134

    
1135
	// Suppress "Delete row" button if there are fewer than two rows
1136
	checkLastRow();
1137

    
1138
<?php endif; ?>
1139

    
1140

    
1141
});
1142
//]]>
1143
</script>
1144
<?php
1145
include('foot.inc');
(193-193/225)