Project

General

Profile

Download (31 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	system_certmanager.php
4
*/
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *	Copyright (c)  2008 Shrew Soft Inc.
8
 *
9
 *	Redistribution and use in source and binary forms, with or without modification,
10
 *	are permitted provided that the following conditions are met:
11
 *
12
 *	1. Redistributions of source code must retain the above copyright notice,
13
 *		this list of conditions and the following disclaimer.
14
 *
15
 *	2. Redistributions in binary form must reproduce the above copyright
16
 *		notice, this list of conditions and the following disclaimer in
17
 *		the documentation and/or other materials provided with the
18
 *		distribution.
19
 *
20
 *	3. All advertising materials mentioning features or use of this software
21
 *		must display the following acknowledgment:
22
 *		"This product includes software developed by the pfSense Project
23
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
24
 *
25
 *	4. The names "pfSense" and "pfSense Project" must not be used to
26
 *		 endorse or promote products derived from this software without
27
 *		 prior written permission. For written permission, please contact
28
 *		 coreteam@pfsense.org.
29
 *
30
 *	5. Products derived from this software may not be called "pfSense"
31
 *		nor may "pfSense" appear in their names without prior written
32
 *		permission of the Electric Sheep Fencing, LLC.
33
 *
34
 *	6. Redistributions of any form whatsoever must retain the following
35
 *		acknowledgment:
36
 *
37
 *	"This product includes software developed by the pfSense Project
38
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
39
 *
40
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
41
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
44
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
52
 *
53
 *	====================================================================
54
 *
55
 */
56

    
57
##|+PRIV
58
##|*IDENT=page-system-certmanager
59
##|*NAME=System: Certificate Manager
60
##|*DESCR=Allow access to the 'System: Certificate Manager' page.
61
##|*MATCH=system_certmanager.php*
62
##|-PRIV
63

    
64
require("guiconfig.inc");
65
require_once("certs.inc");
66

    
67
$cert_methods = array(
68
	"import" => gettext("Import an existing Certificate"),
69
	"internal" => gettext("Create an internal Certificate"),
70
	"external" => gettext("Create a Certificate Signing Request"),
71
);
72

    
73
$cert_keylens = array("512", "1024", "2048", "4096");
74
$cert_types = array(
75
	"ca" => "Certificate Authority",
76
	"server" => "Server Certificate",
77
	"user" => "User Certificate");
78

    
79
$altname_types = array("DNS", "IP", "email", "URI");
80
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
81

    
82
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
83

    
84
if (is_numericint($_GET['userid'])) {
85
	$userid = $_GET['userid'];
86
}
87
if (isset($_POST['userid']) && is_numericint($_POST['userid'])) {
88
	$userid = $_POST['userid'];
89
}
90

    
91
if (isset($userid)) {
92
	$cert_methods["existing"] = gettext("Choose an existing certificate");
93
	if (!is_array($config['system']['user'])) {
94
		$config['system']['user'] = array();
95
	}
96
	$a_user =& $config['system']['user'];
97
}
98

    
99
if (is_numericint($_GET['id'])) {
100
	$id = $_GET['id'];
101
}
102
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
103
	$id = $_POST['id'];
104
}
105

    
106
if (!is_array($config['ca'])) {
107
	$config['ca'] = array();
108
}
109

    
110
$a_ca =& $config['ca'];
111

    
112
if (!is_array($config['cert'])) {
113
	$config['cert'] = array();
114
}
115

    
116
$a_cert =& $config['cert'];
117

    
118
$internal_ca_count = 0;
119
foreach ($a_ca as $ca) {
120
	if ($ca['prv']) {
121
		$internal_ca_count++;
122
	}
123
}
124

    
125
$act = $_GET['act'];
126

    
127
if ($_POST['act']) {
128
	$act = $_POST['act'];
129
}
130

    
131
if ($act == "del") {
132

    
133
	if (!isset($a_cert[$id])) {
134
		pfSenseHeader("system_certmanager.php");
135
		exit;
136
	}
137

    
138
	unset($a_cert[$id]);
139
	write_config();
140
	$savemsg = sprintf(gettext("Certificate %s successfully deleted"), htmlspecialchars($a_cert[$id]['descr'])) . "<br />";
141
	pfSenseHeader("system_certmanager.php");
142
	exit;
143
}
144

    
145

    
146
if ($act == "new") {
147
	$pconfig['method'] = $_GET['method'];
148
	$pconfig['keylen'] = "2048";
149
	$pconfig['digest_alg'] = "sha256";
150
	$pconfig['csr_keylen'] = "2048";
151
	$pconfig['csr_digest_alg'] = "sha256";
152
	$pconfig['type'] = "user";
153
	$pconfig['lifetime'] = "3650";
154
}
155

    
156
if ($act == "exp") {
157

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

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

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

    
174
if ($act == "req") {
175

    
176
	if (!$a_cert[$id]) {
177
		pfSenseHeader("system_certmanager.php");
178
		exit;
179
	}
180

    
181
	$exp_name = urlencode("{$a_cert[$id]['descr']}.req");
182
	$exp_data = base64_decode($a_cert[$id]['csr']);
183
	$exp_size = strlen($exp_data);
184

    
185
	header("Content-Type: application/octet-stream");
186
	header("Content-Disposition: attachment; filename={$exp_name}");
187
	header("Content-Length: $exp_size");
188
	echo $exp_data;
189
	exit;
190
}
191

    
192
if ($act == "key") {
193

    
194
	if (!$a_cert[$id]) {
195
		pfSenseHeader("system_certmanager.php");
196
		exit;
197
	}
198

    
199
	$exp_name = urlencode("{$a_cert[$id]['descr']}.key");
200
	$exp_data = base64_decode($a_cert[$id]['prv']);
201
	$exp_size = strlen($exp_data);
202

    
203
	header("Content-Type: application/octet-stream");
204
	header("Content-Disposition: attachment; filename={$exp_name}");
205
	header("Content-Length: $exp_size");
206
	echo $exp_data;
207
	exit;
208
}
209

    
210
if ($act == "p12") {
211
	if (!$a_cert[$id]) {
212
		pfSenseHeader("system_certmanager.php");
213
		exit;
214
	}
215

    
216
	$exp_name = urlencode("{$a_cert[$id]['descr']}.p12");
217
	$args = array();
218
	$args['friendly_name'] = $a_cert[$id]['descr'];
219

    
220
	$ca = lookup_ca($a_cert[$id]['caref']);
221
	if ($ca) {
222
		$args['extracerts'] = openssl_x509_read(base64_decode($ca['crt']));
223
	}
224

    
225
	$res_crt = openssl_x509_read(base64_decode($a_cert[$id]['crt']));
226
	$res_key = openssl_pkey_get_private(array(0 => base64_decode($a_cert[$id]['prv']) , 1 => ""));
227

    
228
	$exp_data = "";
229
	openssl_pkcs12_export($res_crt, $exp_data, $res_key, null, $args);
230
	$exp_size = strlen($exp_data);
231

    
232
	header("Content-Type: application/octet-stream");
233
	header("Content-Disposition: attachment; filename={$exp_name}");
234
	header("Content-Length: $exp_size");
235
	echo $exp_data;
236
	exit;
237
}
238

    
239
if ($act == "csr") {
240
	if (!$a_cert[$id]) {
241
		pfSenseHeader("system_certmanager.php");
242
		exit;
243
	}
244

    
245
	$pconfig['descr'] = $a_cert[$id]['descr'];
246
	$pconfig['csr'] = base64_decode($a_cert[$id]['csr']);
247
}
248

    
249
if ($_POST) {
250
	// This is just the blank altername name that is added for display purposes. We don't want to validate/save it
251
	if($_POST['altname_value0'] == "") {
252
		unset($_POST['altname_type0']);
253
		unset($_POST['altname_value0']);
254
	}
255

    
256
	if ($_POST['save'] == gettext("Save")) {
257
		$input_errors = array();
258
		$pconfig = $_POST;
259

    
260
		/* input validation */
261
		if ($pconfig['method'] == "import") {
262
			$reqdfields = explode(" ",
263
				"descr cert key");
264
			$reqdfieldsn = array(
265
				gettext("Descriptive name"),
266
				gettext("Certificate data"),
267
				gettext("Key data"));
268
			if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
269
				$input_errors[] = gettext("This certificate does not appear to be valid.");
270
			}
271
		}
272

    
273
		if ($pconfig['method'] == "internal") {
274
			$reqdfields = explode(" ",
275
				"descr caref keylen type lifetime dn_country dn_state dn_city ".
276
				"dn_organization dn_email dn_commonname");
277
			$reqdfieldsn = array(
278
				gettext("Descriptive name"),
279
				gettext("Certificate authority"),
280
				gettext("Key length"),
281
				gettext("Certificate Type"),
282
				gettext("Lifetime"),
283
				gettext("Distinguished name Country Code"),
284
				gettext("Distinguished name State or Province"),
285
				gettext("Distinguished name City"),
286
				gettext("Distinguished name Organization"),
287
				gettext("Distinguished name Email Address"),
288
				gettext("Distinguished name Common Name"));
289
		}
290

    
291
		if ($pconfig['method'] == "external") {
292
			$reqdfields = explode(" ",
293
				"descr csr_keylen csr_dn_country csr_dn_state csr_dn_city ".
294
				"csr_dn_organization csr_dn_email csr_dn_commonname");
295
			$reqdfieldsn = array(
296
				gettext("Descriptive name"),
297
				gettext("Key length"),
298
				gettext("Distinguished name Country Code"),
299
				gettext("Distinguished name State or Province"),
300
				gettext("Distinguished name City"),
301
				gettext("Distinguished name Organization"),
302
				gettext("Distinguished name Email Address"),
303
				gettext("Distinguished name Common Name"));
304
		}
305

    
306
		if ($pconfig['method'] == "existing") {
307
			$reqdfields = array("certref");
308
			$reqdfieldsn = array(gettext("Existing Certificate Choice"));
309
		}
310

    
311
		$altnames = array();
312
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
313
		if ($pconfig['method'] != "import" && $pconfig['method'] != "existing") {
314
			/* subjectAltNames */
315
			foreach ($_POST as $key => $value) {
316
				$entry = '';
317
				if (!substr_compare('altname_type', $key, 0, 12)) {
318
					$entry = substr($key, 12);
319
					$field = 'type';
320
				}
321
				elseif (!substr_compare('altname_value', $key, 0, 13)) {
322
					$entry = substr($key, 13);
323
					$field = 'value';
324
				}
325

    
326
				if (ctype_digit($entry)) {
327
					$entry++;	// Pre-bootstrap code is one-indexed, but the bootstrap code is 0-indexed
328
					$altnames[$entry][$field] = $value;
329
				}
330
			}
331

    
332
			$pconfig['altnames']['item'] = $altnames;
333

    
334
			/* Input validation for subjectAltNames */
335
			foreach ($altnames as $idx => $altname) {
336
				switch ($altname['type']) {
337
					case "DNS":
338
						if (!is_hostname($altname['value'], true)) {
339
							array_push($input_errors, "DNS subjectAltName values must be valid hostnames, FQDNs or wildcard domains.");
340
						}
341
						break;
342
					case "IP":
343
						if (!is_ipaddr($altname['value'])) {
344
							array_push($input_errors, "IP subjectAltName values must be valid IP Addresses");
345
						}
346
						break;
347
					case "email":
348
						if (empty($altname['value'])) {
349
							array_push($input_errors, "You must provide an e-mail address for this type of subjectAltName");
350
						}
351
						if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $altname['value'])) {
352
							array_push($input_errors, "The e-mail provided in a subjectAltName contains invalid characters.");
353
						}
354
						break;
355
					case "URI":
356
						/* Close enough? */
357
						if (!is_URL($altname['value'])) {
358
							$input_errors[] = "URI subjectAltName types must be a valid URI";
359
						}
360
						break;
361
					default:
362
						$input_errors[] = "Unrecognized subjectAltName type.";
363
				}
364
			}
365

    
366
			/* Make sure we do not have invalid characters in the fields for the certificate */
367

    
368
			if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
369
				array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
370
			}
371

    
372
			for ($i = 0; $i < count($reqdfields); $i++) {
373
				if (preg_match('/email/', $reqdfields[$i])) { /* dn_email or csr_dn_name */
374
					if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) {
375
						array_push($input_errors, "The field 'Distinguished name Email Address' contains invalid characters.");
376
					}
377
				} else if (preg_match('/commonname/', $reqdfields[$i])) { /* dn_commonname or csr_dn_commonname */
378
					if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) {
379
						array_push($input_errors, "The field 'Distinguished name Common Name' contains invalid characters.");
380
					}
381
				} else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST[$reqdfields[$i]])) {
382
					array_push($input_errors, "The field '" . $reqdfieldsn[$i] . "' contains invalid characters.");
383
				}
384
			}
385

    
386
			if (($pconfig['method'] != "external") && isset($_POST["keylen"]) && !in_array($_POST["keylen"], $cert_keylens)) {
387
				array_push($input_errors, gettext("Please select a valid Key Length."));
388
			}
389
			if (($pconfig['method'] != "external") && !in_array($_POST["digest_alg"], $openssl_digest_algs)) {
390
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
391
			}
392

    
393
			if (($pconfig['method'] == "external") && isset($_POST["csr_keylen"]) && !in_array($_POST["csr_keylen"], $cert_keylens)) {
394
				array_push($input_errors, gettext("Please select a valid Key Length."));
395
			}
396
			if (($pconfig['method'] == "external") && !in_array($_POST["csr_digest_alg"], $openssl_digest_algs)) {
397
				array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
398
			}
399
		}
400

    
401
		/* if this is an AJAX caller then handle via JSON */
402
		if (isAjax() && is_array($input_errors)) {
403
			input_errors2Ajax($input_errors);
404
			exit;
405
		}
406

    
407
		/* save modifications */
408
		if (!$input_errors) {
409

    
410
			if ($pconfig['method'] == "existing") {
411
				$cert = lookup_cert($pconfig['certref']);
412
				if ($cert && $a_user) {
413
					$a_user[$userid]['cert'][] = $cert['refid'];
414
				}
415
			} else {
416
				$cert = array();
417
				$cert['refid'] = uniqid();
418
				if (isset($id) && $a_cert[$id]) {
419
					$cert = $a_cert[$id];
420
				}
421

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

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

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

    
430
				if ($pconfig['method'] == "internal") {
431
					$dn = array(
432
						'countryName' => $pconfig['dn_country'],
433
						'stateOrProvinceName' => $pconfig['dn_state'],
434
						'localityName' => $pconfig['dn_city'],
435
						'organizationName' => $pconfig['dn_organization'],
436
						'emailAddress' => $pconfig['dn_email'],
437
						'commonName' => $pconfig['dn_commonname']);
438

    
439
					if (count($altnames)) {
440
						$altnames_tmp = "";
441
						foreach ($altnames as $altname) {
442
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
443
						}
444

    
445
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
446
					}
447

    
448
					if (!cert_create($cert, $pconfig['caref'], $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['type'], $pconfig['digest_alg'])) {
449
						while ($ssl_err = openssl_error_string()) {
450
							$input_errors = array();
451
							array_push($input_errors, "openssl library returns: " . $ssl_err);
452
						}
453
					}
454
				}
455

    
456
				if ($pconfig['method'] == "external") {
457
					$dn = array(
458
						'countryName' => $pconfig['csr_dn_country'],
459
						'stateOrProvinceName' => $pconfig['csr_dn_state'],
460
						'localityName' => $pconfig['csr_dn_city'],
461
						'organizationName' => $pconfig['csr_dn_organization'],
462
						'emailAddress' => $pconfig['csr_dn_email'],
463
						'commonName' => $pconfig['csr_dn_commonname']);
464
					if (count($altnames)) {
465
						$altnames_tmp = "";
466
						foreach ($altnames as $altname) {
467
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
468
						}
469
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
470
					}
471

    
472
					if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) {
473
						while ($ssl_err = openssl_error_string()) {
474
							$input_errors = array();
475
							array_push($input_errors, "openssl library returns: " . $ssl_err);
476
						}
477
					}
478
				}
479
				error_reporting($old_err_level);
480

    
481
				if (isset($id) && $a_cert[$id]) {
482
					$a_cert[$id] = $cert;
483
				} else {
484
					$a_cert[] = $cert;
485
				}
486

    
487
				if (isset($a_user) && isset($userid)) {
488
					$a_user[$userid]['cert'][] = $cert['refid'];
489
				}
490
			}
491

    
492
			if (!$input_errors) {
493
				write_config();
494
			}
495

    
496
			if ($userid) {
497
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
498
				exit;
499
			}
500
		}
501
	}
502

    
503
	if ($_POST['save'] == gettext("Update")) {
504
		unset($input_errors);
505
		$pconfig = $_POST;
506

    
507
		/* input validation */
508
		$reqdfields = explode(" ", "descr cert");
509
		$reqdfieldsn = array(
510
		gettext("Descriptive name"),
511
		gettext("Final Certificate data"));
512

    
513
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
514

    
515
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
516
			array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
517
		}
518

    
519
//		old way
520
		/* make sure this csr and certificate subjects match */
521
//		$subj_csr = csr_get_subject($pconfig['csr'], false);
522
//		$subj_cert = cert_get_subject($pconfig['cert'], false);
523
//
524
//		if (!isset($_POST['ignoresubjectmismatch']) && !($_POST['ignoresubjectmismatch'] == "yes")) {
525
//			if (strcmp($subj_csr, $subj_cert)) {
526
//				$input_errors[] = sprintf(gettext("The certificate subject '%s' does not match the signing request subject."), $subj_cert);
527
//				$subject_mismatch = true;
528
//			}
529
//		}
530
		$mod_csr = csr_get_modulus($pconfig['csr'], false);
531
		$mod_cert = cert_get_modulus($pconfig['cert'], false);
532

    
533
		if (strcmp($mod_csr, $mod_cert)) {
534
			// simply: if the moduli don't match, then the private key and public key won't match
535
			$input_errors[] = sprintf(gettext("The certificate modulus does not match the signing request modulus."), $subj_cert);
536
			$subject_mismatch = true;
537
		}
538

    
539
		/* if this is an AJAX caller then handle via JSON */
540
		if (isAjax() && is_array($input_errors)) {
541
			input_errors2Ajax($input_errors);
542
			exit;
543
		}
544

    
545
		/* save modifications */
546
		if (!$input_errors) {
547

    
548
			$cert = $a_cert[$id];
549

    
550
			$cert['descr'] = $pconfig['descr'];
551

    
552
			csr_complete($cert, $pconfig['cert']);
553

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

    
556
			write_config();
557

    
558
			pfSenseHeader("system_certmanager.php");
559
		}
560
	}
561
}
562

    
563
include("head.inc");
564

    
565
if ($input_errors)
566
	print_input_errors($input_errors);
567

    
568
if ($savemsg)
569
	print_info_box($savemsg, 'success');
570

    
571
$tab_array = array();
572
$tab_array[] = array(gettext("CAs"), false, "system_camanager.php");
573
$tab_array[] = array(gettext("Certificates"), true, "system_certmanager.php");
574
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
575
display_top_tabs($tab_array);
576

    
577
// Load valid country codes
578
$dn_cc = array();
579
if (file_exists("/etc/ca_countries")){
580
	$dn_cc_file=file("/etc/ca_countries");
581
	foreach($dn_cc_file as $line) {
582
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
583
			$dn_cc[$matches[1]] = $matches[1];
584
		}
585
	}
586
}
587

    
588
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
589
$form = new Form;
590

    
591
if ($act == "csr" || (($_POST['save'] == gettext("Update")) && $input_errors))
592
{
593
	$form->setAction('system_certmanager.php?act=csr');
594

    
595
	$section = new Form_Section('Complete Signing Request');
596

    
597
	if (isset($id) && $a_cert[$id])
598
	{
599
		$form->addGlobal(new Form_Input(
600
			'id',
601
			null,
602
			'hidden',
603
			$id
604
		));
605
	}
606

    
607
	$section->addInput(new Form_Input(
608
		'descr',
609
		'Descriptive name',
610
		'text',
611
		$pconfig['descr']
612
	));
613

    
614
	$section->addInput(new Form_Textarea(
615
		'csr',
616
		'Signing request data',
617
		$pconfig['csr']
618
	))->setReadonly()->setHelp('Copy the certificate signing data from here and '.
619
		'forward it to your certificate authority for signing.');
620

    
621
	$section->addInput(new Form_Textarea(
622
		'cert',
623
		'Final certificate data',
624
		$pconfig["cert"]
625
	))->setHelp('Paste the certificate received from your certificate authority here.');
626

    
627
	$form->add($section);
628
	print $form;
629

    
630
	include("foot.inc");
631
	exit;
632
}
633

    
634
$form->setAction('system_certmanager.php?act=edit');
635

    
636
if (isset($userid) && $a_user)
637
{
638
	$form->addGlobal(new Form_Input(
639
		'userid',
640
		null,
641
		'hidden',
642
		$userid
643
	));
644
}
645

    
646
if (isset($id) && $a_cert[$id])
647
{
648
	$form->addGlobal(new Form_Input(
649
		'id',
650
		null,
651
		'hidden',
652
		$id
653
	));
654
}
655

    
656
$section = new Form_Section('Add a new certificate');
657

    
658
if (!isset($id))
659
{
660
	$section->addInput(new Form_Select(
661
		'method',
662
		'Method',
663
		$pconfig['method'],
664
		$cert_methods
665
	))->toggles();
666
}
667

    
668
$section->addInput(new Form_Input(
669
	'descr',
670
	'Descriptive name',
671
	'text',
672
	($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr']
673
))->addClass('toggle-existing');
674

    
675
$form->add($section);
676
$section = new Form_Section('Import Certificate');
677
$section->addClass('toggle-import collapse');
678

    
679
$section->addInput(new Form_Textarea(
680
	'cert',
681
	'Certificate data',
682
	$pconfig['cert']
683
))->setHelp('Paste a certificate in X.509 PEM format here.');
684

    
685
$section->addInput(new Form_Textarea(
686
	'key',
687
	'Private key data',
688
	$pconfig['key']
689
))->setHelp('Paste a private key in X.509 PEM format here.');
690

    
691
$form->add($section);
692
$section = new Form_Section('Internal Certificate');
693
$section->addClass('toggle-internal collapse');
694

    
695
if (!$internal_ca_count)
696
{
697
	$section->addInput(new Form_StaticText(
698
		'Certificate authority',
699
		gettext('No internal Certificate Authorities have been defined. You must ').
700
		'<a href="system_camanager.php?act=new&amp;method=internal"> '. gettext(" create") .'</a>'.
701
		gettext(' an internal CA before creating an internal certificate.')
702
	));
703
}
704
else
705
{
706
	$allCas = array();
707
	foreach ($a_ca as $ca)
708
	{
709
		if (!$ca['prv'])
710
				continue;
711

    
712
		$allCas[ $ca['refid'] ] = $ca['descr'];
713
	}
714

    
715
	$section->addInput(new Form_Select(
716
		'caref',
717
		'Certificate authority',
718
		$pconfig['caref'],
719
		$allCas
720
	));
721
}
722

    
723
$section->addInput(new Form_Select(
724
	'keylen',
725
	'Key length',
726
	$pconfig['keylen'],
727
	array_combine($cert_keylens, $cert_keylens)
728
));
729

    
730
$section->addInput(new Form_Select(
731
	'digest_alg',
732
	'Digest Algorithm',
733
	$pconfig['digest_alg'],
734
	array_combine($openssl_digest_algs, $openssl_digest_algs)
735
))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
736
	'SHA1 when possible.');
737

    
738
$section->addInput(new Form_Select(
739
	'type',
740
	'Certificate Type',
741
	$pconfig['type'],
742
	$cert_types
743
))->setHelp('Type of certificate to generate. Used for placing '.
744
	'restrictions on the usage of the generated certificate.');
745

    
746
$section->addInput(new Form_Input(
747
	'lifetime',
748
	'Lifetime (days)',
749
	'number',
750
	$pconfig['lifetime']
751
));
752

    
753
$section->addInput(new Form_Select(
754
	'dn_country',
755
	'Country Code',
756
	$pconfig['dn_country'],
757
	$dn_cc
758
));
759

    
760
$section->addInput(new Form_Input(
761
	'dn_state',
762
	'State or Province',
763
	'text',
764
	$pconfig['dn_state'],
765
	['placeholder' => 'e.g. Texas']
766
));
767

    
768
$section->addInput(new Form_Input(
769
	'dn_city',
770
	'City',
771
	'text',
772
	$pconfig['dn_city'],
773
	['placeholder' => 'e.g. Austin']
774
));
775

    
776
$section->addInput(new Form_Input(
777
	'dn_organization',
778
	'Organization',
779
	'text',
780
	$pconfig['dn_organization'],
781
	['placeholder' => 'e.g. My Company Inc.']
782
));
783

    
784
$section->addInput(new Form_Input(
785
	'dn_email',
786
	'Email Address',
787
	'email',
788
	$pconfig['dn_email'],
789
	['placeholder' => 'e.g. admin@mycompany.com']
790
));
791

    
792
$section->addInput(new Form_Input(
793
	'dn_commonname',
794
	'Common Name',
795
	'text',
796
	$pconfig['dn_commonname'],
797
	['placeholder' => 'e.g. www.example.com']
798
));
799

    
800
if (empty($pconfig['altnames']['item']))
801
{
802
	$pconfig['altnames']['item'] = array(
803
		array('type' => null, 'value' => null)
804
	);
805
}
806

    
807
$counter = 0;
808
$numrows = count($pconfig['altnames']['item']) - 1;
809

    
810
foreach ($pconfig['altnames']['item'] as $item) {
811

    
812
	$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
813

    
814
	$group->add(new Form_Select(
815
		'altname_type' . $counter,
816
		'Type',
817
		$item['type'],
818
		array(
819
			'DNS' => 'FQDN or Hostname',
820
			'IP' => 'IP address',
821
			'URI' => 'URI',
822
			'email' => 'email address',
823
		)
824
	))->setHelp(($counter == $numrows) ? 'Type':null);
825

    
826
	$group->add(new Form_Input(
827
		'altname_value' . $counter,
828
		null,
829
		'text',
830
		$item['value']
831
	))->setHelp(($counter == $numrows) ? 'Value':null);
832

    
833
	$group->add(new Form_Button(
834
		'deleterow' . $counter,
835
		'Delete'
836
	))->removeClass('btn-primary')->addClass('btn-warning');
837

    
838
	$group->addClass('repeatable');
839

    
840
	$section->add($group);
841

    
842
	$counter++;
843
}
844

    
845
$section->addInput(new Form_Button(
846
	'addrow',
847
	'Add'
848
))->removeClass('btn-primary')->addClass('btn-success');
849

    
850
$form->add($section);
851
$section = new Form_Section('External Signing Request');
852
$section->addClass('toggle-external collapse');
853

    
854
$section->addInput(new Form_Select(
855
	'csr_keylen',
856
	'Key length',
857
	$pconfig['csr_keylen'],
858
	array_combine($cert_keylens, $cert_keylens)
859
));
860

    
861
$section->addInput(new Form_Select(
862
	'csr_digest_alg',
863
	'Digest Algorithm',
864
	$pconfig['csr_digest_alg'],
865
	array_combine($openssl_digest_algs, $openssl_digest_algs)
866
))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
867
	'SHA1 when possible');
868

    
869
$section->addInput(new Form_Select(
870
	'csr_dn_country',
871
	'Country Code',
872
	$pconfig['dn_country'],
873
	$dn_cc
874
));
875

    
876
$section->addInput(new Form_Input(
877
	'csr_dn_state',
878
	'State or Province',
879
	'text',
880
	$pconfig['csr_dn_state'],
881
	['placeholder' => 'e.g. Texas']
882
));
883

    
884
$section->addInput(new Form_Input(
885
	'csr_dn_city',
886
	'City',
887
	'text',
888
	$pconfig['csr_dn_city'],
889
	['placeholder' => 'e.g. Austin']
890
));
891

    
892
$section->addInput(new Form_Input(
893
	'csr_dn_organization',
894
	'Organization',
895
	'text',
896
	$pconfig['csr_dn_organization'],
897
	['placeholder' => 'e.g. My Company Inc.']
898
));
899

    
900
$section->addInput(new Form_Input(
901
	'csr_dn_email',
902
	'Email Address',
903
	'email',
904
	$pconfig['csr_dn_email'],
905
	['placeholder' => 'e.g. admin@mycompany.com']
906
));
907

    
908
$section->addInput(new Form_Input(
909
	'csr_dn_commonname',
910
	'Common Name',
911
	'text',
912
	$pconfig['csr_dn_commonname'],
913
	['placeholder' => 'e.g. internal-ca']
914
));
915

    
916
$form->add($section);
917
$section = new Form_Section('Choose an Existing Certificate');
918
$section->addClass('toggle-existing collapse');
919

    
920
$existCerts = array();
921

    
922
foreach ($config['cert'] as $cert)	{
923
	if(is_array($config['system']['user'][$userid]['cert'])) { // Could be MIA!
924
		if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert']))
925
			continue;
926
	}
927

    
928
	$ca = lookup_ca($cert['caref']);
929
	if ($ca)
930
		$cert['descr'] .= " (CA: {$ca['descr']})";
931

    
932
	if (cert_in_use($cert['refid']))
933
		$cert['descr'] .= " <i>In Use</i>";
934
	if (is_cert_revoked($cert))
935
		$cert['descr'] .= " <b>Revoked</b>";
936

    
937
	$existCerts[ $cert['refid'] ] = $cert['descr'];
938
}
939

    
940

    
941
$section->addInput(new Form_Select(
942
	'certref',
943
	'Existing Certificates',
944
	$pconfig['certref'],
945
	$existCerts
946
));
947

    
948
$form->add($section);
949
print $form;
950

    
951
} else if ($act == "csr" || (($_POST['save'] == gettext("Update")) && $input_errors)) {
952
	$form = new Form(new Form_Button(
953
		'save',
954
		'Update'
955
	));
956

    
957
	$section = new Form_Section("Complete signing request for " . $pconfig['descr']);
958

    
959
	$section->addInput(new Form_Input(
960
		'descr',
961
		'Descriptive name',
962
		'text',
963
		$pconfig['descr']
964
	));
965

    
966
	$section->addInput(new Form_Textarea(
967
		'csr',
968
		'Signing request data',
969
		$pconfig['csr']
970
	))->setReadonly()
971
	  ->setWidth(7)
972
	  ->setHelp('Copy the certificate signing data from here and forward it to your certificate authority for signing.');
973

    
974
	$section->addInput(new Form_Textarea(
975
		'cert',
976
		'Final certificate data',
977
		$pconfig['cert']
978
	))->setWidth(7)
979
	  ->setHelp('Paste the certificate received from your certificate authority here.');
980

    
981
	 if (isset($id) && $a_cert[$id]) {
982
		 $section->addInput(new Form_Input(
983
			'id',
984
			null,
985
			'hidden',
986
			$id
987
		 ));
988

    
989
		 $section->addInput(new Form_Input(
990
			'act',
991
			null,
992
			'hidden',
993
			'csr'
994
		 ));
995
	 }
996

    
997
	$form->add($section);
998
	print($form);
999
} else {
1000
?>
1001
<div class="table-responsive">
1002
<table class="table table-striped table-hover">
1003
	<thead>
1004
		<tr>
1005
			<th><?=gettext("Name")?></th>
1006
			<th><?=gettext("Issuer")?></th>
1007
			<th><?=gettext("Distinguished Name")?></th>
1008
			<th><?=gettext("In Use")?></th>
1009
			<th class="col-sm-2"><?=gettext("Actions")?></th>
1010
		</tr>
1011
	</thead>
1012
	<tbody>
1013
<?php
1014
foreach($a_cert as $i => $cert):
1015
	$name = htmlspecialchars($cert['descr']);
1016

    
1017
	if ($cert['crt']) {
1018
		$subj = cert_get_subject($cert['crt']);
1019
		$issuer = cert_get_issuer($cert['crt']);
1020
		$purpose = cert_get_purpose($cert['crt']);
1021
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
1022

    
1023
		if ($subj==$issuer)
1024
			$caname = '<i>'. gettext("self-signed") .'</i>';
1025
		else
1026
			$caname = '<i>'. gettext("external").'</i>';
1027

    
1028
		$subj = htmlspecialchars($subj);
1029
	}
1030

    
1031
	if ($cert['csr']) {
1032
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
1033
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
1034
	}
1035

    
1036
	$ca = lookup_ca($cert['caref']);
1037
	if ($ca)
1038
		$caname = $ca['descr'];
1039
?>
1040
		<tr>
1041
			<td>
1042
				<?=$name?><br />
1043
				<?php if ($cert['type']): ?>
1044
					<i><?=$cert_types[$cert['type']]?></i><br />
1045
				<?php endif?>
1046
				<?php if (is_array($purpose)): ?>
1047
					CA: <b><?=$purpose['ca']?></b>, Server: <b><?=$purpose['server']?></b>
1048
				<?php endif?>
1049
			</td>
1050
			<td><?=$caname?></td>
1051
			<td>
1052
				<?=$subj?>
1053
				<?php if (! $cert['csr']): ?>
1054
				<br />
1055
				<small>
1056
					<?=gettext("Valid From")?>: <b><?=$startdate ?></b><br /><?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
1057
				</small>
1058
				<?php endif?>
1059
			</td>
1060
			<td>
1061
				<?php if (is_cert_revoked($cert)): ?>
1062
					<i>Revoked </i>
1063
				<?php endif?>
1064
				<?php if (is_webgui_cert($cert['refid'])): ?>
1065
					webConfigurator
1066
				<?php endif?>
1067
				<?php if (is_user_cert($cert['refid'])): ?>
1068
					User Cert
1069
				<?php endif?>
1070
				<?php if (is_openvpn_server_cert($cert['refid'])): ?>
1071
					OpenVPN Server
1072
				<?php endif?>
1073
				<?php if (is_openvpn_client_cert($cert['refid'])): ?>
1074
					OpenVPN Client
1075
				<?php endif?>
1076
				<?php if (is_ipsec_cert($cert['refid'])): ?>
1077
					IPsec Tunnel
1078
				<?php endif?>
1079
				<?php if (is_captiveportal_cert($cert['refid'])): ?>
1080
					Captive Portal
1081
				<?php endif?>
1082
			</td>
1083
			<td>
1084
				<?php if (!$cert['csr']): ?>
1085
					<a href="system_certmanager.php?act=exp&amp;id=<?=$i?>" class="fa fa-sign-in" title="<?=gettext("Export Certificate")?>"></a>
1086
					<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1087
					<a href="system_certmanager.php?act=p12&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export P12")?>"> P12</a>
1088
				<?php else: ?>
1089
					<a href="system_certmanager.php?act=csr&amp;id=<?=$i?>" class="fa fa-pencil" title="<?=gettext("Update CSR")?>"></a>
1090
					<a href="system_certmanager.php?act=req&amp;id=<?=$i?>" class="fa fa-sign-in" title="<?=gettext("Export Request")?>"></a>
1091
					<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1092
				<?php endif?>
1093
				<?php if (!cert_in_use($cert['refid'])): ?>
1094
					<a href="system_certmanager.php?act=del&amp;id=<?=$i?>" class="fa fa-trash" title="<?=gettext("Delete")?>"></a>
1095
				<?php endif?>
1096
			</td>
1097
		</tr>
1098
<?php endforeach; ?>
1099
	</tbody>
1100
</table>
1101
</div>
1102

    
1103
<nav class="action-buttons">
1104
	<a href="?act=new" class="btn btn-success btn-sm">
1105
		<i class="fa fa-plus icon-embed-btn"></i>
1106
		<?=gettext("Add")?>
1107
	</a>
1108
</nav>
1109
<?
1110
	include("foot.inc");
1111
	exit;
1112
}
1113

    
1114

    
1115
?>
1116
<script type="text/javascript">
1117
//<![CDATA[
1118
events.push(function(){
1119

    
1120
<?php if ($internal_ca_count): ?>
1121
	function internalca_change() {
1122

    
1123
		caref = $('#caref').val();
1124

    
1125
		switch (caref) {
1126
<?php
1127
			foreach ($a_ca as $ca):
1128
				if (!$ca['prv']) {
1129
					continue;
1130
				}
1131

    
1132
				$subject = cert_get_subject_array($ca['crt']);
1133

    
1134
?>
1135
				case "<?=$ca['refid'];?>":
1136
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1137
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1138
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1139
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1140
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1141
					break;
1142
<?php
1143
			endforeach;
1144
?>
1145
		}
1146
	}
1147

    
1148
	// ---------- Click checkbox handlers ---------------------------------------------------------
1149

    
1150
	$('#caref').on('change', function() {
1151
		internalca_change();
1152
	});
1153

    
1154
	// ---------- On initial page load ------------------------------------------------------------
1155

    
1156
	internalca_change();
1157

    
1158
	// Suppress "Delete row" button if there are fewer than two rows
1159
	checkLastRow();
1160

    
1161
<?php endif; ?>
1162

    
1163

    
1164
});
1165
//]]>
1166
</script>
1167
<?php
1168
include('foot.inc');
(195-195/228)