Project

General

Profile

Download (31.3 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
$pglinks = array("", "system_camanager.php", "system_certmanager.php");
523

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

    
530
if ($input_errors) {
531
	print_input_errors($input_errors);
532
}
533

    
534
if ($savemsg) {
535
	print_info_box($savemsg, 'success');
536
}
537

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

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

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

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

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

    
577
	$section = new Form_Section('Add a New Certificate');
578

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

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

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

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

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

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

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

    
630
			$allCas[ $ca['refid'] ] = $ca['descr'];
631
		}
632

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
735
	foreach ($pconfig['altnames']['item'] as $item) {
736

    
737
		$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
738

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

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

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

    
765
		$group->addClass('repeatable');
766

    
767
		$section->add($group);
768

    
769
		$counter++;
770
	}
771

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

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

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

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

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

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

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

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

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

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

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

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

    
857
	$existCerts = array();
858

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

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

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

    
878
		$existCerts[ $cert['refid'] ] = $cert['descr'];
879
	}
880

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

    
888
	$form->add($section);
889
	print $form;
890

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

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

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

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

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

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

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

    
935
	$form->add($section);
936

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

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

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

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

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

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

    
985
		$subj = htmlspecialchars($subj);
986
	}
987

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

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

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

    
1095

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

    
1101
<?php if ($internal_ca_count): ?>
1102
	function internalca_change() {
1103

    
1104
		caref = $('#caref').val();
1105

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

    
1113
				$subject = cert_get_subject_array($ca['crt']);
1114

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

    
1130
	// ---------- Click checkbox handlers ---------------------------------------------------------
1131

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

    
1136
	// ---------- On initial page load ------------------------------------------------------------
1137

    
1138
	internalca_change();
1139

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

    
1143
<?php endif; ?>
1144

    
1145

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