Project

General

Profile

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

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

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

    
33
$cert_methods = array(
34
	"import" => gettext("Import an existing Certificate"),
35
	"internal" => gettext("Create an internal Certificate"),
36
	"external" => gettext("Create a Certificate Signing Request"),
37
);
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 (isset($_POST['userid']) && is_numericint($_POST['userid'])) {
48
	$userid = $_POST['userid'];
49
}
50

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

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

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

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

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

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

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

    
82
$act = $_POST['act'];
83

    
84

    
85
if ($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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
174
	$ca = lookup_ca($a_cert[$id]['caref']);
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'] == "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'] == "import") {
216
			$reqdfields = explode(" ",
217
				"descr cert key");
218
			$reqdfieldsn = array(
219
				gettext("Descriptive name"),
220
				gettext("Certificate data"),
221
				gettext("Key data"));
222
			if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
223
				$input_errors[] = gettext("This certificate does not appear to be valid.");
224
			}
225
			if (cert_get_modulus($_POST['cert'], false) != prv_get_modulus($_POST['key'], false)) {
226
				$input_errors[] = gettext("The submitted private key does not match the submitted certificate data.");
227
			}
228
		}
229

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

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

    
263
		if ($pconfig['method'] == "existing") {
264
			$reqdfields = array("certref");
265
			$reqdfieldsn = array(gettext("Existing Certificate Choice"));
266
		}
267

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

    
282
				if (ctype_digit($entry)) {
283
					$entry++;	// Pre-bootstrap code is one-indexed, but the bootstrap code is 0-indexed
284
					$altnames[$entry][$field] = $value;
285
				}
286
			}
287

    
288
			$pconfig['altnames']['item'] = $altnames;
289

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

    
322
			/* Make sure we do not have invalid characters in the fields for the certificate */
323

    
324
			if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
325
				array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
326
			}
327

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

    
342
			if (($pconfig['method'] != "external") && isset($_POST["keylen"]) && !in_array($_POST["keylen"], $cert_keylens)) {
343
				array_push($input_errors, gettext("Please select a valid Key Length."));
344
			}
345
			if (($pconfig['method'] != "external") && !in_array($_POST["digest_alg"], $openssl_digest_algs)) {
346
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
347
			}
348

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

    
357
		/* save modifications */
358
		if (!$input_errors) {
359

    
360
			if ($pconfig['method'] == "existing") {
361
				$cert = lookup_cert($pconfig['certref']);
362
				if ($cert && $a_user) {
363
					$a_user[$userid]['cert'][] = $cert['refid'];
364
				}
365
			} else {
366
				$cert = array();
367
				$cert['refid'] = uniqid();
368
				if (isset($id) && $a_cert[$id]) {
369
					$cert = $a_cert[$id];
370
				}
371

    
372
				$cert['descr'] = $pconfig['descr'];
373

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

    
376
				if ($pconfig['method'] == "import") {
377
					cert_import($cert, $pconfig['cert'], $pconfig['key']);
378
				}
379

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

    
397
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
398
					}
399

    
400
					if (!cert_create($cert, $pconfig['caref'], $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['type'], $pconfig['digest_alg'])) {
401
						while ($ssl_err = openssl_error_string()) {
402
							$input_errors = array();
403
							array_push($input_errors, "openssl library returns: " . $ssl_err);
404
						}
405
					}
406
				}
407

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

    
427
					if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) {
428
						while ($ssl_err = openssl_error_string()) {
429
							$input_errors = array();
430
							array_push($input_errors, "openssl library returns: " . $ssl_err);
431
						}
432
					}
433
				}
434
				error_reporting($old_err_level);
435

    
436
				if (isset($id) && $a_cert[$id]) {
437
					$a_cert[$id] = $cert;
438
				} else {
439
					$a_cert[] = $cert;
440
				}
441

    
442
				if (isset($a_user) && isset($userid)) {
443
					$a_user[$userid]['cert'][] = $cert['refid'];
444
				}
445
			}
446

    
447
			if (!$input_errors) {
448
				write_config();
449
			}
450

    
451
			if ($userid && !$input_errors) {
452
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
453
				exit;
454
			}
455
		}
456
	}
457

    
458
	if ($_POST['save'] == gettext("Update")) {
459
		unset($input_errors);
460
		$pconfig = $_POST;
461

    
462
		/* input validation */
463
		$reqdfields = explode(" ", "descr cert");
464
		$reqdfieldsn = array(
465
			gettext("Descriptive name"),
466
			gettext("Final Certificate data"));
467

    
468
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
469

    
470
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
471
			array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
472
		}
473

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

    
488
		if (strcmp($mod_csr, $mod_cert)) {
489
			// simply: if the moduli don't match, then the private key and public key won't match
490
			$input_errors[] = sprintf(gettext("The certificate modulus does not match the signing request modulus."), $subj_cert);
491
			$subject_mismatch = true;
492
		}
493

    
494
		/* save modifications */
495
		if (!$input_errors) {
496

    
497
			$cert = $a_cert[$id];
498

    
499
			$cert['descr'] = $pconfig['descr'];
500

    
501
			csr_complete($cert, $pconfig['cert']);
502

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

    
505
			write_config();
506

    
507
			pfSenseHeader("system_certmanager.php");
508
		}
509
	}
510
}
511

    
512
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
513
$pglinks = array("", "system_camanager.php", "system_certmanager.php");
514

    
515
if (($act == "new" || ($_POST['save'] == gettext("Save") && $input_errors)) || ($act == "csr" || ($_POST['save'] == gettext("Update") && $input_errors))) {
516
	$pgtitle[] = gettext('Edit');
517
	$pglinks[] = "@self";
518
}
519
include("head.inc");
520

    
521
if ($input_errors) {
522
	print_input_errors($input_errors);
523
}
524

    
525
if ($savemsg) {
526
	print_info_box($savemsg, 'success');
527
}
528

    
529
$tab_array = array();
530
$tab_array[] = array(gettext("CAs"), false, "system_camanager.php");
531
$tab_array[] = array(gettext("Certificates"), true, "system_certmanager.php");
532
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
533
display_top_tabs($tab_array);
534

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

    
546
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
547
	$form = new Form();
548
	$form->setAction('system_certmanager.php?act=edit');
549

    
550
	if (isset($userid) && $a_user) {
551
		$form->addGlobal(new Form_Input(
552
			'userid',
553
			null,
554
			'hidden',
555
			$userid
556
		));
557
	}
558

    
559
	if (isset($id) && $a_cert[$id]) {
560
		$form->addGlobal(new Form_Input(
561
			'id',
562
			null,
563
			'hidden',
564
			$id
565
		));
566
	}
567

    
568
	$section = new Form_Section('Add a New Certificate');
569

    
570
	if (!isset($id)) {
571
		$section->addInput(new Form_Select(
572
			'method',
573
			'*Method',
574
			$pconfig['method'],
575
			$cert_methods
576
		))->toggles();
577
	}
578

    
579
	$section->addInput(new Form_Input(
580
		'descr',
581
		'*Descriptive name',
582
		'text',
583
		($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
584
	))->addClass('toggle-existing');
585

    
586
	$form->add($section);
587
	$section = new Form_Section('Import Certificate');
588
	$section->addClass('toggle-import collapse');
589

    
590
	$section->addInput(new Form_Textarea(
591
		'cert',
592
		'*Certificate data',
593
		$pconfig['cert']
594
	))->setHelp('Paste a certificate in X.509 PEM format here.');
595

    
596
	$section->addInput(new Form_Textarea(
597
		'key',
598
		'*Private key data',
599
		$pconfig['key']
600
	))->setHelp('Paste a private key in X.509 PEM format here.');
601

    
602
	$form->add($section);
603
	$section = new Form_Section('Internal Certificate');
604
	$section->addClass('toggle-internal collapse');
605

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

    
620
			$allCas[ $ca['refid'] ] = $ca['descr'];
621
		}
622

    
623
		$section->addInput(new Form_Select(
624
			'caref',
625
			'*Certificate authority',
626
			$pconfig['caref'],
627
			$allCas
628
		));
629
	}
630

    
631
	$section->addInput(new Form_Select(
632
		'keylen',
633
		'*Key length',
634
		$pconfig['keylen'],
635
		array_combine($cert_keylens, $cert_keylens)
636
	));
637

    
638
	$section->addInput(new Form_Select(
639
		'digest_alg',
640
		'*Digest Algorithm',
641
		$pconfig['digest_alg'],
642
		array_combine($openssl_digest_algs, $openssl_digest_algs)
643
	))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
644
		'SHA1 when possible.');
645

    
646
	$section->addInput(new Form_Select(
647
		'type',
648
		'*Certificate Type',
649
		$pconfig['type'],
650
		$cert_types
651
	))->setHelp('Type of certificate to generate. Used for placing '.
652
		'restrictions on the usage of the generated certificate.');
653

    
654
	$section->addInput(new Form_Input(
655
		'lifetime',
656
		'*Lifetime (days)',
657
		'number',
658
		$pconfig['lifetime']
659
	));
660

    
661
	$section->addInput(new Form_Select(
662
		'dn_country',
663
		'*Country Code',
664
		$pconfig['dn_country'],
665
		$dn_cc
666
	));
667

    
668
	$section->addInput(new Form_Input(
669
		'dn_state',
670
		'*State or Province',
671
		'text',
672
		$pconfig['dn_state'],
673
		['placeholder' => 'e.g. Texas']
674
	));
675

    
676
	$section->addInput(new Form_Input(
677
		'dn_city',
678
		'*City',
679
		'text',
680
		$pconfig['dn_city'],
681
		['placeholder' => 'e.g. Austin']
682
	));
683

    
684
	$section->addInput(new Form_Input(
685
		'dn_organization',
686
		'*Organization',
687
		'text',
688
		$pconfig['dn_organization'],
689
		['placeholder' => 'e.g. My Company Inc']
690
	));
691

    
692
	$section->addInput(new Form_Input(
693
		'dn_organizationalunit',
694
		'Organizational Unit',
695
		'text',
696
		$pconfig['dn_organizationalunit'],
697
		['placeholder' => 'e.g. My Department Name (optional)']
698
	));
699

    
700
	$section->addInput(new Form_Input(
701
		'dn_email',
702
		'*Email Address',
703
		'text',
704
		$pconfig['dn_email'],
705
		['placeholder' => 'e.g. admin@mycompany.com']
706
	));
707

    
708
	$section->addInput(new Form_Input(
709
		'dn_commonname',
710
		'*Common Name',
711
		'text',
712
		$pconfig['dn_commonname'],
713
		['placeholder' => 'e.g. www.example.com']
714
	));
715

    
716
	if (empty($pconfig['altnames']['item'])) {
717
		$pconfig['altnames']['item'] = array(
718
			array('type' => null, 'value' => null)
719
		);
720
	}
721

    
722
	$counter = 0;
723
	$numrows = count($pconfig['altnames']['item']) - 1;
724

    
725
	foreach ($pconfig['altnames']['item'] as $item) {
726

    
727
		$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
728

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

    
741
		$group->add(new Form_Input(
742
			'altname_value' . $counter,
743
			null,
744
			'text',
745
			$item['value']
746
		))->setHelp(($counter == $numrows) ? 'Value':null);
747

    
748
		$group->add(new Form_Button(
749
			'deleterow' . $counter,
750
			'Delete',
751
			null,
752
			'fa-trash'
753
		))->addClass('btn-warning');
754

    
755
		$group->addClass('repeatable');
756

    
757
		$section->add($group);
758

    
759
		$counter++;
760
	}
761

    
762
	$section->addInput(new Form_Button(
763
		'addrow',
764
		'Add',
765
		null,
766
		'fa-plus'
767
	))->addClass('btn-success');
768

    
769

    
770
	$form->add($section);
771
	$section = new Form_Section('External Signing Request');
772
	$section->addClass('toggle-external collapse');
773

    
774
	$section->addInput(new Form_Select(
775
		'csr_keylen',
776
		'*Key length',
777
		$pconfig['csr_keylen'],
778
		array_combine($cert_keylens, $cert_keylens)
779
	));
780

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

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

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

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

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

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

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

    
836
	$section->addInput(new Form_Input(
837
		'csr_dn_commonname',
838
		'*Common Name',
839
		'text',
840
		$pconfig['csr_dn_commonname'],
841
		['placeholder' => 'e.g. internal-ca']
842
	));
843

    
844
	$form->add($section);
845
	$section = new Form_Section('Choose an Existing Certificate');
846
	$section->addClass('toggle-existing collapse');
847

    
848
	$existCerts = array();
849

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

    
857
		$ca = lookup_ca($cert['caref']);
858
		if ($ca) {
859
			$cert['descr'] .= " (CA: {$ca['descr']})";
860
		}
861

    
862
		if (cert_in_use($cert['refid'])) {
863
			$cert['descr'] .= " <i>In Use</i>";
864
		}
865
		if (is_cert_revoked($cert)) {
866
			$cert['descr'] .= " <b>Revoked</b>";
867
		}
868

    
869
		$existCerts[ $cert['refid'] ] = $cert['descr'];
870
	}
871

    
872
	$section->addInput(new Form_Select(
873
		'certref',
874
		'*Existing Certificates',
875
		$pconfig['certref'],
876
		$existCerts
877
	));
878

    
879
	$form->add($section);
880
	print $form;
881

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

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

    
888
	$section->addInput(new Form_Input(
889
		'descr',
890
		'*Descriptive name',
891
		'text',
892
		$pconfig['descr']
893
	));
894

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

    
903
	$section->addInput(new Form_Textarea(
904
		'cert',
905
		'*Final certificate data',
906
		$pconfig['cert']
907
	))->setWidth(7)
908
	  ->setHelp('Paste the certificate received from the certificate authority here.');
909

    
910
	 if (isset($id) && $a_cert[$id]) {
911
		 $section->addInput(new Form_Input(
912
			'id',
913
			null,
914
			'hidden',
915
			$id
916
		 ));
917

    
918
		 $section->addInput(new Form_Input(
919
			'act',
920
			null,
921
			'hidden',
922
			'csr'
923
		 ));
924
	 }
925

    
926
	$form->add($section);
927

    
928
	$form->addGlobal(new Form_Button(
929
		'save',
930
		'Update',
931
		null,
932
		'fa-save'
933
	))->addClass('btn-primary');
934

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

    
950
					<th class="col-sm-2"><?=gettext("Actions")?></th>
951
				</tr>
952
			</thead>
953
			<tbody>
954
<?php
955

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

    
964
	if ($cert['crt']) {
965
		$subj = cert_get_subject($cert['crt']);
966
		$issuer = cert_get_issuer($cert['crt']);
967
		$purpose = cert_get_purpose($cert['crt']);
968
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
969

    
970
		if ($subj == $issuer) {
971
			$caname = '<i>'. gettext("self-signed") .'</i>';
972
		} else {
973
			$caname = '<i>'. gettext("external").'</i>';
974
		}
975

    
976
		$subj = htmlspecialchars($subj);
977
	} else {
978
		$subj = "";
979
		$issuer = "";
980
		$purpose = "";
981
		$startdate = "";
982
		$enddate = "";
983
		$caname = "<em>" . gettext("private key only") . "</em>";
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 (!empty($startdate) || !empty($enddate)): ?>
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")?>" usepost></a>
1061
							<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>" usepost></a>
1062
							<a href="system_certmanager.php?act=p12&amp;id=<?=$i?>" class="fa fa-archive" title="<?=gettext("Export P12")?>" usepost></a>
1063
						<?php else: ?>
1064
							<a href="system_certmanager.php?act=csr&amp;id=<?=$i?>" class="fa fa-pencil" title="<?=gettext("Update CSR")?>" usep></a>
1065
							<a href="system_certmanager.php?act=req&amp;id=<?=$i?>" class="fa fa-sign-in" title="<?=gettext("Export Request")?>" usepost></a>
1066
							<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>" usepost></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")?>" usepost></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" usepost>
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');
(191-191/223)