Project

General

Profile

Download (31.2 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
			if (cert_get_modulus($_POST['cert'], false) != prv_get_modulus($_POST['key'], false)) {
235
				$input_errors[] = gettext("The submitted private key does not match the submitted certificate data.");
236
			}
237
		}
238

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

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

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

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

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

    
297
			$pconfig['altnames']['item'] = $altnames;
298

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

    
331
			/* Make sure we do not have invalid characters in the fields for the certificate */
332

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

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

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

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

    
366
		/* save modifications */
367
		if (!$input_errors) {
368

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

    
381
				$cert['descr'] = $pconfig['descr'];
382

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

    
385
				if ($pconfig['method'] == "import") {
386
					cert_import($cert, $pconfig['cert'], $pconfig['key']);
387
				}
388

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

    
406
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
407
					}
408

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

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

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

    
445
				if (isset($id) && $a_cert[$id]) {
446
					$a_cert[$id] = $cert;
447
				} else {
448
					$a_cert[] = $cert;
449
				}
450

    
451
				if (isset($a_user) && isset($userid)) {
452
					$a_user[$userid]['cert'][] = $cert['refid'];
453
				}
454
			}
455

    
456
			if (!$input_errors) {
457
				write_config();
458
			}
459

    
460
			if ($userid && !$input_errors) {
461
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
462
				exit;
463
			}
464
		}
465
	}
466

    
467
	if ($_POST['save'] == gettext("Update")) {
468
		unset($input_errors);
469
		$pconfig = $_POST;
470

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

    
477
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
478

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

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

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

    
503
		/* save modifications */
504
		if (!$input_errors) {
505

    
506
			$cert = $a_cert[$id];
507

    
508
			$cert['descr'] = $pconfig['descr'];
509

    
510
			csr_complete($cert, $pconfig['cert']);
511

    
512
			$a_cert[$id] = $cert;
513

    
514
			write_config();
515

    
516
			pfSenseHeader("system_certmanager.php");
517
		}
518
	}
519
}
520

    
521
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
522

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

    
528
if ($input_errors) {
529
	print_input_errors($input_errors);
530
}
531

    
532
if ($savemsg) {
533
	print_info_box($savemsg, 'success');
534
}
535

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

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

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

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

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

    
575
	$section = new Form_Section('Add a New Certificate');
576

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

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

    
593
	$form->add($section);
594
	$section = new Form_Section('Import Certificate');
595
	$section->addClass('toggle-import collapse');
596

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

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

    
609
	$form->add($section);
610
	$section = new Form_Section('Internal Certificate');
611
	$section->addClass('toggle-internal collapse');
612

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

    
628
			$allCas[ $ca['refid'] ] = $ca['descr'];
629
		}
630

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

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

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

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

    
662
	$section->addInput(new Form_Input(
663
		'lifetime',
664
		'Lifetime (days)',
665
		'number',
666
		$pconfig['lifetime']
667
	));
668

    
669
	$section->addInput(new Form_Select(
670
		'dn_country',
671
		'Country Code',
672
		$pconfig['dn_country'],
673
		$dn_cc
674
	));
675

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

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

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

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

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

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

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

    
730
	$counter = 0;
731
	$numrows = count($pconfig['altnames']['item']) - 1;
732

    
733
	foreach ($pconfig['altnames']['item'] as $item) {
734

    
735
		$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
736

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

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

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

    
763
		$group->addClass('repeatable');
764

    
765
		$section->add($group);
766

    
767
		$counter++;
768
	}
769

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

    
777
	$form->add($section);
778
	$section = new Form_Section('External Signing Request');
779
	$section->addClass('toggle-external collapse');
780

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

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

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

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

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

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

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

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

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

    
851
	$form->add($section);
852
	$section = new Form_Section('Choose an Existing Certificate');
853
	$section->addClass('toggle-existing collapse');
854

    
855
	$existCerts = array();
856

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

    
864
		$ca = lookup_ca($cert['caref']);
865
		if ($ca) {
866
			$cert['descr'] .= " (CA: {$ca['descr']})";
867
		}
868

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

    
876
		$existCerts[ $cert['refid'] ] = $cert['descr'];
877
	}
878

    
879
	$section->addInput(new Form_Select(
880
		'certref',
881
		'Existing Certificates',
882
		$pconfig['certref'],
883
		$existCerts
884
	));
885

    
886
	$form->add($section);
887
	print $form;
888

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

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

    
895
	$section->addInput(new Form_Input(
896
		'descr',
897
		'Descriptive name',
898
		'text',
899
		$pconfig['descr']
900
	));
901

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

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

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

    
925
		 $section->addInput(new Form_Input(
926
			'act',
927
			null,
928
			'hidden',
929
			'csr'
930
		 ));
931
	 }
932

    
933
	$form->add($section);
934

    
935
	$form->addGlobal(new Form_Button(
936
		'save',
937
		'Update',
938
		null,
939
		'fa-save'
940
	))->addClass('btn-primary');
941

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

    
957
					<th class="col-sm-2"><?=gettext("Actions")?></th>
958
				</tr>
959
			</thead>
960
			<tbody>
961
<?php
962

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

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

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

    
983
		$subj = htmlspecialchars($subj);
984
	}
985

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

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

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

    
1093

    
1094
?>
1095
<script type="text/javascript">
1096
//<![CDATA[
1097
events.push(function() {
1098

    
1099
<?php if ($internal_ca_count): ?>
1100
	function internalca_change() {
1101

    
1102
		caref = $('#caref').val();
1103

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

    
1111
				$subject = cert_get_subject_array($ca['crt']);
1112

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

    
1128
	// ---------- Click checkbox handlers ---------------------------------------------------------
1129

    
1130
	$('#caref').on('change', function() {
1131
		internalca_change();
1132
	});
1133

    
1134
	// ---------- On initial page load ------------------------------------------------------------
1135

    
1136
	internalca_change();
1137

    
1138
	// Suppress "Delete row" button if there are fewer than two rows
1139
	checkLastRow();
1140

    
1141
<?php endif; ?>
1142

    
1143

    
1144
});
1145
//]]>
1146
</script>
1147
<?php
1148
include('foot.inc');
(193-193/225)