Project

General

Profile

Download (32.7 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
	"server" => "Server Certificate",
76
	"user" => "User Certificate");
77

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

    
81
if (is_numericint($_GET['userid'])) {
82
	$userid = $_GET['userid'];
83
}
84
if (isset($_POST['userid']) && is_numericint($_POST['userid'])) {
85
	$userid = $_POST['userid'];
86
}
87

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

    
96
if (is_numericint($_GET['id'])) {
97
	$id = $_GET['id'];
98
}
99
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
100
	$id = $_POST['id'];
101
}
102

    
103
if (!is_array($config['ca'])) {
104
	$config['ca'] = array();
105
}
106

    
107
$a_ca =& $config['ca'];
108

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

    
113
$a_cert =& $config['cert'];
114

    
115
$internal_ca_count = 0;
116
foreach ($a_ca as $ca) {
117
	if ($ca['prv']) {
118
		$internal_ca_count++;
119
	}
120
}
121

    
122
$act = $_GET['act'];
123

    
124
if ($_POST['act']) {
125
	$act = $_POST['act'];
126
}
127

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

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

    
135
	unset($a_cert[$id]);
136
	write_config();
137
	$savemsg = sprintf(gettext("Certificate %s successfully deleted."), htmlspecialchars($a_cert[$id]['descr']));
138
	pfSenseHeader("system_certmanager.php");
139
	exit;
140
}
141

    
142

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

    
153
if ($act == "exp") {
154

    
155
	if (!$a_cert[$id]) {
156
		pfSenseHeader("system_certmanager.php");
157
		exit;
158
	}
159

    
160
	$exp_name = urlencode("{$a_cert[$id]['descr']}.crt");
161
	$exp_data = base64_decode($a_cert[$id]['crt']);
162
	$exp_size = strlen($exp_data);
163

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

    
171
if ($act == "req") {
172

    
173
	if (!$a_cert[$id]) {
174
		pfSenseHeader("system_certmanager.php");
175
		exit;
176
	}
177

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

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

    
189
if ($act == "key") {
190

    
191
	if (!$a_cert[$id]) {
192
		pfSenseHeader("system_certmanager.php");
193
		exit;
194
	}
195

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

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

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

    
213
	$exp_name = urlencode("{$a_cert[$id]['descr']}.p12");
214
	$args = array();
215
	$args['friendly_name'] = $a_cert[$id]['descr'];
216

    
217
	$ca = lookup_ca($a_cert[$id]['caref']);
218
	if ($ca) {
219
		$args['extracerts'] = openssl_x509_read(base64_decode($ca['crt']));
220
	}
221

    
222
	$res_crt = openssl_x509_read(base64_decode($a_cert[$id]['crt']));
223
	$res_key = openssl_pkey_get_private(array(0 => base64_decode($a_cert[$id]['prv']) , 1 => ""));
224

    
225
	$exp_data = "";
226
	openssl_pkcs12_export($res_crt, $exp_data, $res_key, null, $args);
227
	$exp_size = strlen($exp_data);
228

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

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

    
242
	$pconfig['descr'] = $a_cert[$id]['descr'];
243
	$pconfig['csr'] = base64_decode($a_cert[$id]['csr']);
244
}
245

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

    
253
	if ($_POST['save'] == gettext("Save")) {
254
		$input_errors = array();
255
		$pconfig = $_POST;
256

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

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

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

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

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

    
322
				if (ctype_digit($entry)) {
323
					$entry++;	// Pre-bootstrap code is one-indexed, but the bootstrap code is 0-indexed
324
					$altnames[$entry][$field] = $value;
325
				}
326
			}
327

    
328
			$pconfig['altnames']['item'] = $altnames;
329

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

    
362
			/* Make sure we do not have invalid characters in the fields for the certificate */
363

    
364
			if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
365
				array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
366
			}
367

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

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

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

    
397
		/* if this is an AJAX caller then handle via JSON */
398
		if (isAjax() && is_array($input_errors)) {
399
			input_errors2Ajax($input_errors);
400
			exit;
401
		}
402

    
403
		/* save modifications */
404
		if (!$input_errors) {
405

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

    
418
				$cert['descr'] = $pconfig['descr'];
419

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

    
422
				if ($pconfig['method'] == "import") {
423
					cert_import($cert, $pconfig['cert'], $pconfig['key']);
424
				}
425

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

    
435
					if (count($altnames)) {
436
						$altnames_tmp = "";
437
						foreach ($altnames as $altname) {
438
							$altnames_tmp[] = "{$altname['type']}:{$altname['value']}";
439
						}
440

    
441
						$dn['subjectAltName'] = implode(",", $altnames_tmp);
442
					}
443

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

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

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

    
477
				if (isset($id) && $a_cert[$id]) {
478
					$a_cert[$id] = $cert;
479
				} else {
480
					$a_cert[] = $cert;
481
				}
482

    
483
				if (isset($a_user) && isset($userid)) {
484
					$a_user[$userid]['cert'][] = $cert['refid'];
485
				}
486
			}
487

    
488
			if (!$input_errors) {
489
				write_config();
490
			}
491

    
492
			if ($userid) {
493
				post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid));
494
				exit;
495
			}
496
		}
497
	}
498

    
499
	if ($_POST['save'] == gettext("Update")) {
500
		unset($input_errors);
501
		$pconfig = $_POST;
502

    
503
		/* input validation */
504
		$reqdfields = explode(" ", "descr cert");
505
		$reqdfieldsn = array(
506
			gettext("Descriptive name"),
507
			gettext("Final Certificate data"));
508

    
509
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
510

    
511
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
512
			array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
513
		}
514

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

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

    
535
		/* if this is an AJAX caller then handle via JSON */
536
		if (isAjax() && is_array($input_errors)) {
537
			input_errors2Ajax($input_errors);
538
			exit;
539
		}
540

    
541
		/* save modifications */
542
		if (!$input_errors) {
543

    
544
			$cert = $a_cert[$id];
545

    
546
			$cert['descr'] = $pconfig['descr'];
547

    
548
			csr_complete($cert, $pconfig['cert']);
549

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

    
552
			write_config();
553

    
554
			pfSenseHeader("system_certmanager.php");
555
		}
556
	}
557
}
558

    
559
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificates"));
560

    
561
if (($act == "new" || ($_POST['save'] == gettext("Save") && $input_errors)) || ($act == "csr" || ($_POST['save'] == gettext("Update") && $input_errors))) {
562
	$pgtitle[] = gettext('Edit');
563
}
564
include("head.inc");
565

    
566
if ($input_errors) {
567
	print_input_errors($input_errors);
568
}
569

    
570
if ($savemsg) {
571
	print_info_box($savemsg, 'success');
572
}
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
	$form->setAction('system_certmanager.php?act=csr');
596

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

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

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

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

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

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

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

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

    
637
if (isset($userid) && $a_user) {
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
	$form->addGlobal(new Form_Input(
648
		'id',
649
		null,
650
		'hidden',
651
		$id
652
	));
653
}
654

    
655
$section = new Form_Section('Add a New Certificate');
656

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

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

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

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

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

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

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

    
707
		$allCas[ $ca['refid'] ] = $ca['descr'];
708
	}
709

    
710
	$section->addInput(new Form_Select(
711
		'caref',
712
		'Certificate authority',
713
		$pconfig['caref'],
714
		$allCas
715
	));
716
}
717

    
718
$section->addInput(new Form_Select(
719
	'keylen',
720
	'Key length',
721
	$pconfig['keylen'],
722
	array_combine($cert_keylens, $cert_keylens)
723
));
724

    
725
$section->addInput(new Form_Select(
726
	'digest_alg',
727
	'Digest Algorithm',
728
	$pconfig['digest_alg'],
729
	array_combine($openssl_digest_algs, $openssl_digest_algs)
730
))->setHelp('NOTE: It is recommended to use an algorithm stronger than '.
731
	'SHA1 when possible.');
732

    
733
$section->addInput(new Form_Select(
734
	'type',
735
	'Certificate Type',
736
	$pconfig['type'],
737
	$cert_types
738
))->setHelp('Type of certificate to generate. Used for placing '.
739
	'restrictions on the usage of the generated certificate.');
740

    
741
$section->addInput(new Form_Input(
742
	'lifetime',
743
	'Lifetime (days)',
744
	'number',
745
	$pconfig['lifetime']
746
));
747

    
748
$section->addInput(new Form_Select(
749
	'dn_country',
750
	'Country Code',
751
	$pconfig['dn_country'],
752
	$dn_cc
753
));
754

    
755
$section->addInput(new Form_Input(
756
	'dn_state',
757
	'State or Province',
758
	'text',
759
	$pconfig['dn_state'],
760
	['placeholder' => 'e.g. Texas']
761
));
762

    
763
$section->addInput(new Form_Input(
764
	'dn_city',
765
	'City',
766
	'text',
767
	$pconfig['dn_city'],
768
	['placeholder' => 'e.g. Austin']
769
));
770

    
771
$section->addInput(new Form_Input(
772
	'dn_organization',
773
	'Organization',
774
	'text',
775
	$pconfig['dn_organization'],
776
	['placeholder' => 'e.g. My Company Inc.']
777
));
778

    
779
$section->addInput(new Form_Input(
780
	'dn_email',
781
	'Email Address',
782
	'text',
783
	$pconfig['dn_email'],
784
	['placeholder' => 'e.g. admin@mycompany.com']
785
));
786

    
787
$section->addInput(new Form_Input(
788
	'dn_commonname',
789
	'Common Name',
790
	'text',
791
	$pconfig['dn_commonname'],
792
	['placeholder' => 'e.g. www.example.com']
793
));
794

    
795
if (empty($pconfig['altnames']['item'])) {
796
	$pconfig['altnames']['item'] = array(
797
		array('type' => null, 'value' => null)
798
	);
799
}
800

    
801
$counter = 0;
802
$numrows = count($pconfig['altnames']['item']) - 1;
803

    
804
foreach ($pconfig['altnames']['item'] as $item) {
805

    
806
	$group = new Form_Group($counter == 0 ? 'Alternative Names':'');
807

    
808
	$group->add(new Form_Select(
809
		'altname_type' . $counter,
810
		'Type',
811
		$item['type'],
812
		array(
813
			'DNS' => gettext('FQDN or Hostname'),
814
			'IP' => gettext('IP address'),
815
			'URI' => gettext('URI'),
816
			'email' => gettext('email address'),
817
		)
818
	))->setHelp(($counter == $numrows) ? 'Type':null);
819

    
820
	$group->add(new Form_Input(
821
		'altname_value' . $counter,
822
		null,
823
		'text',
824
		$item['value']
825
	))->setHelp(($counter == $numrows) ? 'Value':null);
826

    
827
	$group->add(new Form_Button(
828
		'deleterow' . $counter,
829
		'Delete',
830
		null,
831
		'fa-trash'
832
	))->addClass('btn-warning');
833

    
834
	$group->addClass('repeatable');
835

    
836
	$section->add($group);
837

    
838
	$counter++;
839
}
840

    
841
$section->addInput(new Form_Button(
842
	'addrow',
843
	'Add',
844
	null,
845
	'fa-plus'
846
))->addClass('btn-success');
847

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

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

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

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

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

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

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

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

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

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

    
918
$existCerts = array();
919

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

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

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

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

    
942

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

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

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

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

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

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

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

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

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

    
996
	$form->add($section);
997

    
998
	$form->addGlobal(new Form_Button(
999
		'Submit',
1000
		'Update',
1001
		null,
1002
		'fa-save'
1003
	))->addClass('btn-primary');
1004

    
1005
	print($form);
1006
} else {
1007
?>
1008
<div class="panel panel-default">
1009
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificates')?></h2></div>
1010
	<div class="panel-body">
1011
		<div class="table-responsive">
1012
		<table class="table table-striped table-hover">
1013
			<thead>
1014
				<tr>
1015
					<th><?=gettext("Name")?></th>
1016
					<th><?=gettext("Issuer")?></th>
1017
					<th><?=gettext("Distinguished Name")?></th>
1018
					<th><?=gettext("In Use")?></th>
1019

    
1020
					<th class="col-sm-2"><?=gettext("Actions")?></th>
1021
				</tr>
1022
			</thead>
1023
			<tbody>
1024
<?php
1025

    
1026
$pluginparams = array();
1027
$pluginparams['type'] = 'certificates';
1028
$pluginparams['event'] = 'used_certificates';
1029
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
1030
$i = 0;
1031
foreach ($a_cert as $i => $cert):
1032
	$name = htmlspecialchars($cert['descr']);
1033

    
1034
	if ($cert['crt']) {
1035
		$subj = cert_get_subject($cert['crt']);
1036
		$issuer = cert_get_issuer($cert['crt']);
1037
		$purpose = cert_get_purpose($cert['crt']);
1038
		list($startdate, $enddate) = cert_get_dates($cert['crt']);
1039

    
1040
		if ($subj == $issuer) {
1041
			$caname = '<i>'. gettext("self-signed") .'</i>';
1042
		} else {
1043
			$caname = '<i>'. gettext("external").'</i>';
1044
		}
1045

    
1046
		$subj = htmlspecialchars($subj);
1047
	}
1048

    
1049
	if ($cert['csr']) {
1050
		$subj = htmlspecialchars(csr_get_subject($cert['csr']));
1051
		$caname = "<em>" . gettext("external - signature pending") . "</em>";
1052
	}
1053

    
1054
	$ca = lookup_ca($cert['caref']);
1055
	if ($ca) {
1056
		$caname = $ca['descr'];
1057
	}
1058
?>
1059
				<tr>
1060
					<td>
1061
						<?=$name?><br />
1062
						<?php if ($cert['type']): ?>
1063
							<i><?=$cert_types[$cert['type']]?></i><br />
1064
						<?php endif?>
1065
						<?php if (is_array($purpose)): ?>
1066
							CA: <b><?=$purpose['ca']?></b>, <?=gettext("Server")?>: <b><?=$purpose['server']?></b>
1067
						<?php endif?>
1068
					</td>
1069
					<td><?=$caname?></td>
1070
					<td>
1071
						<?=$subj?>
1072
						<?php if (!$cert['csr']): ?>
1073
						<br />
1074
						<small>
1075
							<?=gettext("Valid From")?>: <b><?=$startdate ?></b><br /><?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
1076
						</small>
1077
						<?php endif?>
1078
					</td>
1079
					<td>
1080
						<?php if (is_cert_revoked($cert)): ?>
1081
							<i><?=gettext("Revoked")?></i>
1082
						<?php endif?>
1083
						<?php if (is_webgui_cert($cert['refid'])): ?>
1084
							<?=gettext("webConfigurator")?>
1085
						<?php endif?>
1086
						<?php if (is_user_cert($cert['refid'])): ?>
1087
							<?=gettext("User Cert")?>
1088
						<?php endif?>
1089
						<?php if (is_openvpn_server_cert($cert['refid'])): ?>
1090
							<?=gettext("OpenVPN Server")?>
1091
						<?php endif?>
1092
						<?php if (is_openvpn_client_cert($cert['refid'])): ?>
1093
							<?=gettext("OpenVPN Client")?>
1094
						<?php endif?>
1095
						<?php if (is_ipsec_cert($cert['refid'])): ?>
1096
							<?=gettext("IPsec Tunnel")?>
1097
						<?php endif?>
1098
						<?php if (is_captiveportal_cert($cert['refid'])): ?>
1099
							<?=gettext("Captive Portal")?>
1100
						<?php endif?>
1101
<?php
1102
							$refid = $cert['refid'];
1103
							if (is_array($certificates_used_by_packages)) {
1104
								foreach ($certificates_used_by_packages as $name => $package) {
1105
									if (isset($package['certificatelist'][$refid])) {
1106
										$hint = "" ;
1107
										if (is_array($package['certificatelist'][$refid])) {
1108
											foreach ($package['certificatelist'][$refid] as $cert_used) {
1109
												$hint = $hint . $cert_used['usedby']."\n";
1110
											}
1111
										}
1112
										$count = count($package['certificatelist'][$refid]);
1113
										echo "<div title='".htmlspecialchars($hint)."'>";
1114
										echo htmlspecialchars($package['pkgname'])." ($count)<br />";
1115
										echo "</div>";
1116
									}
1117
								}
1118
							}
1119
?>
1120
					</td>
1121
					<td>
1122
						<?php if (!$cert['csr']): ?>
1123
							<a href="system_certmanager.php?act=exp&amp;id=<?=$i?>" class="fa fa-certificate" title="<?=gettext("Export Certificate")?>"></a>
1124
							<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1125
							<a href="system_certmanager.php?act=p12&amp;id=<?=$i?>" class="fa fa-archive" title="<?=gettext("Export P12")?>"></a>
1126
						<?php else: ?>
1127
							<a href="system_certmanager.php?act=csr&amp;id=<?=$i?>" class="fa fa-pencil" title="<?=gettext("Update CSR")?>"></a>
1128
							<a href="system_certmanager.php?act=req&amp;id=<?=$i?>" class="fa fa-sign-in" title="<?=gettext("Export Request")?>"></a>
1129
							<a href="system_certmanager.php?act=key&amp;id=<?=$i?>" class="fa fa-key" title="<?=gettext("Export Key")?>"></a>
1130
						<?php endif?>
1131
						<?php if (!cert_in_use($cert['refid'])): ?>
1132
							<a href="system_certmanager.php?act=del&amp;id=<?=$i?>" class="fa fa-trash" title="<?=gettext("Delete Certificate")?>"></a>
1133
						<?php endif?>
1134
					</td>
1135
				</tr>
1136
<?php
1137
	$i++; 
1138
	endforeach; ?>
1139
			</tbody>
1140
		</table>
1141
		</div>
1142
	</div>
1143
</div>
1144

    
1145
<nav class="action-buttons">
1146
	<a href="?act=new" class="btn btn-success btn-sm">
1147
		<i class="fa fa-plus icon-embed-btn"></i>
1148
		<?=gettext("Add")?>
1149
	</a>
1150
</nav>
1151
<?php
1152
	include("foot.inc");
1153
	exit;
1154
}
1155

    
1156

    
1157
?>
1158
<script type="text/javascript">
1159
//<![CDATA[
1160
events.push(function() {
1161

    
1162
<?php if ($internal_ca_count): ?>
1163
	function internalca_change() {
1164

    
1165
		caref = $('#caref').val();
1166

    
1167
		switch (caref) {
1168
<?php
1169
			foreach ($a_ca as $ca):
1170
				if (!$ca['prv']) {
1171
					continue;
1172
				}
1173

    
1174
				$subject = cert_get_subject_array($ca['crt']);
1175

    
1176
?>
1177
				case "<?=$ca['refid'];?>":
1178
					$('#dn_country').val("<?=$subject[0]['v'];?>");
1179
					$('#dn_state').val("<?=$subject[1]['v'];?>");
1180
					$('#dn_city').val("<?=$subject[2]['v'];?>");
1181
					$('#dn_organization').val("<?=$subject[3]['v'];?>");
1182
					$('#dn_email').val("<?=$subject[4]['v'];?>");
1183
					break;
1184
<?php
1185
			endforeach;
1186
?>
1187
		}
1188
	}
1189

    
1190
	// ---------- Click checkbox handlers ---------------------------------------------------------
1191

    
1192
	$('#caref').on('change', function() {
1193
		internalca_change();
1194
	});
1195

    
1196
	// ---------- On initial page load ------------------------------------------------------------
1197

    
1198
	internalca_change();
1199

    
1200
	// Suppress "Delete row" button if there are fewer than two rows
1201
	checkLastRow();
1202

    
1203
<?php endif; ?>
1204

    
1205

    
1206
});
1207
//]]>
1208
</script>
1209
<?php
1210
include('foot.inc');
(194-194/227)