Project

General

Profile

Download (31.1 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
	pfSense_MODULE: certificate_manager
58
*/
59

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

    
67
require("guiconfig.inc");
68
require_once("certs.inc");
69

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

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

    
82
$altname_types = array("DNS", "IP", "email", "URI");
83
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
84

    
85
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
86

    
87
if (is_numericint($_GET['userid'])) {
88
	$userid = $_GET['userid'];
89
}
90
if (isset($_POST['userid']) && is_numericint($_POST['userid'])) {
91
	$userid = $_POST['userid'];
92
}
93

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

    
102
if (is_numericint($_GET['id'])) {
103
	$id = $_GET['id'];
104
}
105
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
106
	$id = $_POST['id'];
107
}
108

    
109
if (!is_array($config['ca'])) {
110
	$config['ca'] = array();
111
}
112

    
113
$a_ca =& $config['ca'];
114

    
115
if (!is_array($config['cert'])) {
116
	$config['cert'] = array();
117
}
118

    
119
$a_cert =& $config['cert'];
120

    
121
$internal_ca_count = 0;
122
foreach ($a_ca as $ca) {
123
	if ($ca['prv']) {
124
		$internal_ca_count++;
125
	}
126
}
127

    
128
$act = $_GET['act'];
129

    
130
if ($_POST['act']) {
131
	$act = $_POST['act'];
132
}
133

    
134
if ($act == "del") {
135

    
136
	if (!isset($a_cert[$id])) {
137
		pfSenseHeader("system_certmanager.php");
138
		exit;
139
	}
140

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

    
148

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

    
159
if ($act == "exp") {
160

    
161
	if (!$a_cert[$id]) {
162
		pfSenseHeader("system_certmanager.php");
163
		exit;
164
	}
165

    
166
	$exp_name = urlencode("{$a_cert[$id]['descr']}.crt");
167
	$exp_data = base64_decode($a_cert[$id]['crt']);
168
	$exp_size = strlen($exp_data);
169

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

    
177
if ($act == "req") {
178

    
179
	if (!$a_cert[$id]) {
180
		pfSenseHeader("system_certmanager.php");
181
		exit;
182
	}
183

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

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

    
195
if ($act == "key") {
196

    
197
	if (!$a_cert[$id]) {
198
		pfSenseHeader("system_certmanager.php");
199
		exit;
200
	}
201

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

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

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

    
219
	$exp_name = urlencode("{$a_cert[$id]['descr']}.p12");
220
	$args = array();
221
	$args['friendly_name'] = $a_cert[$id]['descr'];
222

    
223
	$ca = lookup_ca($a_cert[$id]['caref']);
224
	if ($ca) {
225
		$args['extracerts'] = openssl_x509_read(base64_decode($ca['crt']));
226
	}
227

    
228
	$res_crt = openssl_x509_read(base64_decode($a_cert[$id]['crt']));
229
	$res_key = openssl_pkey_get_private(array(0 => base64_decode($a_cert[$id]['prv']) , 1 => ""));
230

    
231
	$exp_data = "";
232
	openssl_pkcs12_export($res_crt, $exp_data, $res_key, null, $args);
233
	$exp_size = strlen($exp_data);
234

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

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

    
248
	$pconfig['descr'] = $a_cert[$id]['descr'];
249
	$pconfig['csr'] = base64_decode($a_cert[$id]['csr']);
250
}
251

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

    
259
	if ($_POST['save'] == gettext("Save")) {
260
		$input_errors = array();
261
		$pconfig = $_POST;
262

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

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

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

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

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

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

    
335
			$pconfig['altnames']['item'] = $altnames;
336

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

    
369
			/* Make sure we do not have invalid characters in the fields for the certificate */
370

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

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

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

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

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

    
410
		/* save modifications */
411
		if (!$input_errors) {
412

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

    
425
				$cert['descr'] = $pconfig['descr'];
426

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

    
429
				if ($pconfig['method'] == "import") {
430
					cert_import($cert, $pconfig['cert'], $pconfig['key']);
431
				}
432

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

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

    
448
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
449
					}
450

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

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

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

    
484
				if (isset($id) && $a_cert[$id]) {
485
					$a_cert[$id] = $cert;
486
				} else {
487
					$a_cert[] = $cert;
488
				}
489

    
490
				if (isset($a_user) && isset($userid)) {
491
					$a_user[$userid]['cert'][] = $cert['refid'];
492
				}
493
			}
494

    
495
			if (!$input_errors) {
496
				write_config();
497
			}
498

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

    
506
	if ($_POST['save'] == gettext("Update")) {
507
		unset($input_errors);
508
		$pconfig = $_POST;
509

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

    
516
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
517

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

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

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

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

    
548
		/* save modifications */
549
		if (!$input_errors) {
550

    
551
			$cert = $a_cert[$id];
552

    
553
			$cert['descr'] = $pconfig['descr'];
554

    
555
			csr_complete($cert, $pconfig['cert']);
556

    
557
			$a_cert[$id] = $cert;
558

    
559
			write_config();
560

    
561
			pfSenseHeader("system_certmanager.php");
562
		}
563
	}
564
}
565

    
566
include("head.inc");
567

    
568
if ($input_errors)
569
	print_input_errors($input_errors);
570

    
571
if ($savemsg)
572
	print_info_box($savemsg, 'success');
573

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

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

    
591
if ($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors)) {
592
$form = new Form;
593

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

    
598
	$section = new Form_Section('Complete Signing Request');
599

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

    
610
	$section->addInput(new Form_Input(
611
		'descr',
612
		'Descriptive name',
613
		'text',
614
		$pconfig['descr']
615
	));
616

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

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

    
630
	$form->add($section);
631
	print $form;
632

    
633
	include("foot.inc");
634
	exit;
635
}
636

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

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

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

    
659
$section = new Form_Section('Add a new certificate');
660

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

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

    
678
$form->add($section);
679
$section = new Form_Section('Import Certificate');
680
$section->addClass('toggle-import collapse');
681

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

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

    
694
$form->add($section);
695
$section = new Form_Section('Internal Certificate');
696
$section->addClass('toggle-internal collapse');
697

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

    
715
		$allCas[ $ca['refid'] ] = $ca['descr'];
716
	}
717

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

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

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

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

    
749
$section->addInput(new Form_Input(
750
	'lifetime',
751
	'Lifetime (days)',
752
	'number',
753
	$pconfig['lifetime']
754
));
755

    
756
$section->addInput(new Form_Select(
757
	'dn_country',
758
	'Country Code',
759
	$pconfig['dn_country'],
760
	$dn_cc
761
));
762

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

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

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

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

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

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

    
810
$counter = 0;
811
$numrows = count($pconfig['altnames']['item']) - 1;
812

    
813
foreach ($pconfig['altnames']['item'] as $item) {
814

    
815
	$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
816

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

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

    
836
	$group->add(new Form_Button(
837
		'deleterow' . $counter,
838
		'Delete'
839
	))->removeClass('btn-primary')->addClass('btn-warning');
840

    
841
	$group->addClass('repeatable');
842

    
843
	$section->add($group);
844

    
845
	$counter++;
846
}
847

    
848
$section->addInput(new Form_Button(
849
	'addrow',
850
	'Add'
851
))->removeClass('btn-primary')->addClass('btn-success');
852

    
853
$form->add($section);
854
$section = new Form_Section('External Signing Request');
855
$section->addClass('toggle-external collapse');
856

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

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

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

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

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

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

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

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

    
919
$form->add($section);
920
$section = new Form_Section('Choose an Existing Certificate');
921
$section->addClass('toggle-existing collapse');
922

    
923
$existCerts = array();
924

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

    
931
	$ca = lookup_ca($cert['caref']);
932
	if ($ca)
933
		$cert['descr'] .= " (CA: {$ca['descr']})";
934

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

    
940
	$existCerts[ $cert['refid'] ] = $cert['descr'];
941
}
942

    
943

    
944
$section->addInput(new Form_Select(
945
	'certref',
946
	'Existing Certificates',
947
	$pconfig['certref'],
948
	$existCerts
949
));
950

    
951
$form->add($section);
952
print $form;
953

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

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

    
962
	$section->addInput(new Form_Input(
963
		'descr',
964
		'Descriptive name',
965
		'text',
966
		$pconfig['descr']
967
	));
968

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

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

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

    
992
		 $section->addInput(new Form_Input(
993
			'act',
994
			null,
995
			'hidden',
996
			'csr'
997
		 ));
998
	 }
999

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

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

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

    
1031
		$subj = htmlspecialchars($subj);
1032
	}
1033

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

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

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

    
1117

    
1118
?>
1119
<script type="text/javascript">
1120
//<![CDATA[
1121
events.push(function(){
1122

    
1123
<?php if ($internal_ca_count): ?>
1124
	function internalca_change() {
1125

    
1126
		caref = $('#caref').val();
1127

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

    
1135
				$subject = cert_get_subject_array($ca['crt']);
1136

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

    
1151
	// ---------- Click checkbox handlers ---------------------------------------------------------
1152

    
1153
	$('#caref').on('change', function() {
1154
		internalca_change();
1155
	});
1156

    
1157
	// ---------- On initial page load ------------------------------------------------------------
1158

    
1159
	internalca_change();
1160

    
1161
	// Suppress "Delete row" button if there are fewer than two rows
1162
	checkLastRow();
1163

    
1164
<?php endif; ?>
1165

    
1166

    
1167
});
1168
//]]>
1169
</script>
1170
<?php
1171
include('foot.inc');
(195-195/228)