Project

General

Profile

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

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

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

    
33
$ca_methods = array(
34
	"existing" => gettext("Import an existing Certificate Authority"),
35
	"internal" => gettext("Create an internal Certificate Authority"),
36
	"intermediate" => gettext("Create an intermediate Certificate Authority"));
37

    
38
$ca_keylens = array("512", "1024", "2048", "3072", "4096", "7680", "8192", "15360", "16384");
39
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512", "whirlpool");
40

    
41
if (is_numericint($_GET['id'])) {
42
	$id = $_GET['id'];
43
}
44
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
45
	$id = $_POST['id'];
46
}
47

    
48
if (!is_array($config['ca'])) {
49
	$config['ca'] = array();
50
}
51

    
52
$a_ca =& $config['ca'];
53

    
54
if (!is_array($config['cert'])) {
55
	$config['cert'] = array();
56
}
57

    
58
$a_cert =& $config['cert'];
59

    
60
if (!is_array($config['crl'])) {
61
	$config['crl'] = array();
62
}
63

    
64
$a_crl =& $config['crl'];
65

    
66
$act = $_GET['act'];
67
if ($_POST['act']) {
68
	$act = $_POST['act'];
69
}
70

    
71
if ($act == "del") {
72

    
73
	if (!isset($a_ca[$id])) {
74
		pfSenseHeader("system_camanager.php");
75
		exit;
76
	}
77

    
78
	$index = count($a_cert) - 1;
79
	for (;$index >= 0; $index--) {
80
		if ($a_cert[$index]['caref'] == $a_ca[$id]['refid']) {
81
			unset($a_cert[$index]);
82
		}
83
	}
84

    
85
	$index = count($a_crl) - 1;
86
	for (;$index >= 0; $index--) {
87
		if ($a_crl[$index]['caref'] == $a_ca[$id]['refid']) {
88
			unset($a_crl[$index]);
89
		}
90
	}
91

    
92
	$name = $a_ca[$id]['descr'];
93
	unset($a_ca[$id]);
94
	write_config();
95
	$savemsg = sprintf(gettext("Certificate Authority %s and its CRLs (if any) successfully deleted."), htmlspecialchars($name));
96
	pfSenseHeader("system_camanager.php");
97
	exit;
98
}
99

    
100
if ($act == "edit") {
101
	if (!$a_ca[$id]) {
102
		pfSenseHeader("system_camanager.php");
103
		exit;
104
	}
105
	$pconfig['descr']  = $a_ca[$id]['descr'];
106
	$pconfig['refid']  = $a_ca[$id]['refid'];
107
	$pconfig['cert']   = base64_decode($a_ca[$id]['crt']);
108
	$pconfig['serial'] = $a_ca[$id]['serial'];
109
	if (!empty($a_ca[$id]['prv'])) {
110
		$pconfig['key'] = base64_decode($a_ca[$id]['prv']);
111
	}
112
}
113

    
114
if ($act == "new") {
115
	$pconfig['method'] = $_GET['method'];
116
	$pconfig['keylen'] = "2048";
117
	$pconfig['digest_alg'] = "sha256";
118
	$pconfig['lifetime'] = "3650";
119
	$pconfig['dn_commonname'] = "internal-ca";
120
}
121

    
122
if ($act == "exp") {
123

    
124
	if (!$a_ca[$id]) {
125
		pfSenseHeader("system_camanager.php");
126
		exit;
127
	}
128

    
129
	$exp_name = urlencode("{$a_ca[$id]['descr']}.crt");
130
	$exp_data = base64_decode($a_ca[$id]['crt']);
131
	$exp_size = strlen($exp_data);
132

    
133
	header("Content-Type: application/octet-stream");
134
	header("Content-Disposition: attachment; filename={$exp_name}");
135
	header("Content-Length: $exp_size");
136
	echo $exp_data;
137
	exit;
138
}
139

    
140
if ($act == "expkey") {
141

    
142
	if (!$a_ca[$id]) {
143
		pfSenseHeader("system_camanager.php");
144
		exit;
145
	}
146

    
147
	$exp_name = urlencode("{$a_ca[$id]['descr']}.key");
148
	$exp_data = base64_decode($a_ca[$id]['prv']);
149
	$exp_size = strlen($exp_data);
150

    
151
	header("Content-Type: application/octet-stream");
152
	header("Content-Disposition: attachment; filename={$exp_name}");
153
	header("Content-Length: $exp_size");
154
	echo $exp_data;
155
	exit;
156
}
157

    
158
if ($_POST) {
159

    
160
	unset($input_errors);
161
	$input_errors = array();
162
	$pconfig = $_POST;
163

    
164
	/* input validation */
165
	if ($pconfig['method'] == "existing") {
166
		$reqdfields = explode(" ", "descr cert");
167
		$reqdfieldsn = array(
168
			gettext("Descriptive name"),
169
			gettext("Certificate data"));
170
		if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
171
			$input_errors[] = gettext("This certificate does not appear to be valid.");
172
		}
173
		if ($_POST['key'] && strstr($_POST['key'], "ENCRYPTED")) {
174
			$input_errors[] = gettext("Encrypted private keys are not yet supported.");
175
		}
176
	}
177
	if ($pconfig['method'] == "internal") {
178
		$reqdfields = explode(" ",
179
			"descr keylen lifetime dn_country dn_state dn_city ".
180
			"dn_organization dn_email dn_commonname");
181
		$reqdfieldsn = array(
182
			gettext("Descriptive name"),
183
			gettext("Key length"),
184
			gettext("Lifetime"),
185
			gettext("Distinguished name Country Code"),
186
			gettext("Distinguished name State or Province"),
187
			gettext("Distinguished name City"),
188
			gettext("Distinguished name Organization"),
189
			gettext("Distinguished name Email Address"),
190
			gettext("Distinguished name Common Name"));
191
	}
192
	if ($pconfig['method'] == "intermediate") {
193
		$reqdfields = explode(" ",
194
			"descr caref keylen lifetime dn_country dn_state dn_city ".
195
			"dn_organization dn_email dn_commonname");
196
		$reqdfieldsn = array(
197
			gettext("Descriptive name"),
198
			gettext("Signing Certificate Authority"),
199
			gettext("Key length"),
200
			gettext("Lifetime"),
201
			gettext("Distinguished name Country Code"),
202
			gettext("Distinguished name State or Province"),
203
			gettext("Distinguished name City"),
204
			gettext("Distinguished name Organization"),
205
			gettext("Distinguished name Email Address"),
206
			gettext("Distinguished name Common Name"));
207
	}
208

    
209
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
210
	if ($pconfig['method'] != "existing") {
211
		/* Make sure we do not have invalid characters in the fields for the certificate */
212
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
213
			array_push($input_errors, gettext("The field 'Descriptive Name' contains invalid characters."));
214
		}
215

    
216
		for ($i = 0; $i < count($reqdfields); $i++) {
217
			if ($reqdfields[$i] == 'dn_email') {
218
				if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_email"])) {
219
					array_push($input_errors, gettext("The field 'Distinguished name Email Address' contains invalid characters."));
220
				}
221
			} else if ($reqdfields[$i] == 'dn_commonname') {
222
				if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_commonname"])) {
223
					array_push($input_errors, gettext("The field 'Distinguished name Common Name' contains invalid characters."));
224
				}
225
			} else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST["$reqdfields[$i]"])) {
226
				array_push($input_errors, sprintf(gettext("The field '%s' contains invalid characters."), $reqdfieldsn[$i]));
227
			}
228
		}
229
		if (!in_array($_POST["keylen"], $ca_keylens)) {
230
			array_push($input_errors, gettext("Please select a valid Key Length."));
231
		}
232
		if (!in_array($_POST["digest_alg"], $openssl_digest_algs)) {
233
			array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
234
		}
235
	}
236

    
237
	/* save modifications */
238
	if (!$input_errors) {
239
		$ca = array();
240
		if (!isset($pconfig['refid']) || empty($pconfig['refid'])) {
241
			$ca['refid'] = uniqid();
242
		} else {
243
			$ca['refid'] = $pconfig['refid'];
244
		}
245

    
246
		if (isset($id) && $a_ca[$id]) {
247
			$ca = $a_ca[$id];
248
		}
249

    
250
		$ca['descr'] = $pconfig['descr'];
251

    
252
		if ($act == "edit") {
253
			$ca['descr']  = $pconfig['descr'];
254
			$ca['refid']  = $pconfig['refid'];
255
			$ca['serial'] = $pconfig['serial'];
256
			$ca['crt']	  = base64_encode($pconfig['cert']);
257
			if (!empty($pconfig['key'])) {
258
				$ca['prv']	  = base64_encode($pconfig['key']);
259
			}
260
		} else {
261
			$old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */
262
			if ($pconfig['method'] == "existing") {
263
				ca_import($ca, $pconfig['cert'], $pconfig['key'], $pconfig['serial']);
264
			} else if ($pconfig['method'] == "internal") {
265
				$dn = array(
266
					'countryName' => $pconfig['dn_country'],
267
					'stateOrProvinceName' => $pconfig['dn_state'],
268
					'localityName' => $pconfig['dn_city'],
269
					'organizationName' => $pconfig['dn_organization'],
270
					'emailAddress' => $pconfig['dn_email'],
271
					'commonName' => $pconfig['dn_commonname']);
272
				if (!empty($pconfig['dn_organizationalunit'])) {
273
					$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
274
				}
275
				if (!ca_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['digest_alg'])) {
276
					while ($ssl_err = openssl_error_string()) {
277
						$input_errors = array();
278
						array_push($input_errors, "openssl library returns: " . $ssl_err);
279
					}
280
				}
281
			} else if ($pconfig['method'] == "intermediate") {
282
				$dn = array(
283
					'countryName' => $pconfig['dn_country'],
284
					'stateOrProvinceName' => $pconfig['dn_state'],
285
					'localityName' => $pconfig['dn_city'],
286
					'organizationName' => $pconfig['dn_organization'],
287
					'emailAddress' => $pconfig['dn_email'],
288
					'commonName' => $pconfig['dn_commonname']);
289
				if (!empty($pconfig['dn_organizationalunit'])) {
290
					$dn['organizationalUnitName'] = $pconfig['dn_organizationalunit'];
291
				}
292
				if (!ca_inter_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['caref'], $pconfig['digest_alg'])) {
293
					while ($ssl_err = openssl_error_string()) {
294
						$input_errors = array();
295
						array_push($input_errors, "openssl library returns: " . $ssl_err);
296
					}
297
				}
298
			}
299
			error_reporting($old_err_level);
300
		}
301

    
302
		if (isset($id) && $a_ca[$id]) {
303
			$a_ca[$id] = $ca;
304
		} else {
305
			$a_ca[] = $ca;
306
		}
307

    
308
		if (!$input_errors) {
309
			write_config();
310
		}
311

    
312
		pfSenseHeader("system_camanager.php");
313
	}
314
}
315

    
316
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("CAs"));
317

    
318
if ($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors) {
319
	$pgtitle[] = gettext('Edit');
320
}
321
include("head.inc");
322

    
323
if ($input_errors) {
324
	print_input_errors($input_errors);
325
}
326

    
327
if ($savemsg) {
328
	print_info_box($savemsg, 'success');
329
}
330

    
331
// Load valid country codes
332
$dn_cc = array();
333
if (file_exists("/etc/ca_countries")) {
334
	$dn_cc_file=file("/etc/ca_countries");
335
	foreach ($dn_cc_file as $line) {
336
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
337
			$dn_cc[$matches[1]] = $matches[1];
338
		}
339
	}
340
}
341

    
342
$tab_array = array();
343
$tab_array[] = array(gettext("CAs"), true, "system_camanager.php");
344
$tab_array[] = array(gettext("Certificates"), false, "system_certmanager.php");
345
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
346
display_top_tabs($tab_array);
347

    
348
if (!($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors)) {
349
?>
350
<div class="panel panel-default">
351
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificate Authorities')?></h2></div>
352
	<div class="panel-body">
353
		<div class="table-responsive">
354
		<table class="table table-striped table-hover table-rowdblclickedit">
355
			<thead>
356
				<tr>
357
					<th><?=gettext("Name")?></th>
358
					<th><?=gettext("Internal")?></th>
359
					<th><?=gettext("Issuer")?></th>
360
					<th><?=gettext("Certificates")?></th>
361
					<th><?=gettext("Distinguished Name")?></th>
362
					<th><?=gettext("Actions")?></th>
363
				</tr>
364
			</thead>
365
			<tbody>
366
<?php
367
foreach ($a_ca as $i => $ca):
368
	$name = htmlspecialchars($ca['descr']);
369
	$subj = cert_get_subject($ca['crt']);
370
	$issuer = cert_get_issuer($ca['crt']);
371
	list($startdate, $enddate) = cert_get_dates($ca['crt']);
372
	if ($subj == $issuer) {
373
		$issuer_name = gettext("self-signed");
374
	} else {
375
		$issuer_name = gettext("external");
376
	}
377
	$subj = htmlspecialchars($subj);
378
	$issuer = htmlspecialchars($issuer);
379
	$certcount = 0;
380

    
381
	$issuer_ca = lookup_ca($ca['caref']);
382
	if ($issuer_ca) {
383
		$issuer_name = $issuer_ca['descr'];
384
	}
385

    
386
	foreach ($a_cert as $cert) {
387
		if ($cert['caref'] == $ca['refid']) {
388
			$certcount++;
389
		}
390
	}
391

    
392
	foreach ($a_ca as $cert) {
393
		if ($cert['caref'] == $ca['refid']) {
394
			$certcount++;
395
		}
396
	}
397
?>
398
				<tr>
399
					<td><?=$name?></td>
400
					<td><i class="fa fa-<?= (!empty($ca['prv'])) ? "check" : "times" ; ?>"></i></td>
401
					<td><i><?=$issuer_name?></i></td>
402
					<td><?=$certcount?></td>
403
					<td>
404
						<?=$subj?>
405
						<br />
406
						<small>
407
							<?=gettext("Valid From")?>: <b><?=$startdate ?></b><br /><?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
408
						</small>
409
					</td>
410
					<td>
411
						<a class="fa fa-pencil"	title="<?=gettext("Edit CA")?>"	href="system_camanager.php?act=edit&amp;id=<?=$i?>"></a>
412
						<a class="fa fa-certificate"	title="<?=gettext("Export CA")?>"	href="system_camanager.php?act=exp&amp;id=<?=$i?>"></a>
413
					<?php if ($ca['prv']): ?>
414
						<a class="fa fa-key"	title="<?=gettext("Export key")?>"	href="system_camanager.php?act=expkey&amp;id=<?=$i?>"></a>
415
					<?php endif?>
416
						<a class="fa fa-trash" 	title="<?=gettext("Delete CA")?>"	href="system_camanager.php?act=del&amp;id=<?=$i?>"></a>
417
					</td>
418
				</tr>
419
<?php endforeach; ?>
420
			</tbody>
421
		</table>
422
		</div>
423
	</div>
424
</div>
425

    
426
<nav class="action-buttons">
427
	<a href="?act=new" class="btn btn-success btn-sm">
428
		<i class="fa fa-plus icon-embed-btn"></i>
429
		<?=gettext("Add")?>
430
	</a>
431
</nav>
432
<?php
433
	include("foot.inc");
434
	exit;
435
}
436

    
437
$form = new Form;
438
//$form->setAction('system_camanager.php?act=edit');
439
if (isset($id) && $a_ca[$id]) {
440
	$form->addGlobal(new Form_Input(
441
		'id',
442
		null,
443
		'hidden',
444
		$id
445
	));
446
}
447

    
448
if ($act == "edit") {
449
	$form->addGlobal(new Form_Input(
450
		'refid',
451
		null,
452
		'hidden',
453
		$pconfig['refid']
454
	));
455
}
456

    
457
$section = new Form_Section('Create / Edit CA');
458

    
459
$section->addInput(new Form_Input(
460
	'descr',
461
	'Descriptive name',
462
	'text',
463
	$pconfig['descr']
464
));
465

    
466
if (!isset($id) || $act == "edit") {
467
	$section->addInput(new Form_Select(
468
		'method',
469
		'Method',
470
		$pconfig['method'],
471
		$ca_methods
472
	))->toggles();
473
}
474

    
475
$form->add($section);
476

    
477
$section = new Form_Section('Existing Certificate Authority');
478
$section->addClass('toggle-existing collapse');
479

    
480
$section->addInput(new Form_Textarea(
481
	'cert',
482
	'Certificate data',
483
	$pconfig['cert']
484
))->setHelp('Paste a certificate in X.509 PEM format here.');
485

    
486
$section->addInput(new Form_Textarea(
487
	'key',
488
	'Certificate Private Key (optional)',
489
	$pconfig['key']
490
))->setHelp('Paste the private key for the above certificate here. This is '.
491
	'optional in most cases, but is required when generating a '.
492
	'Certificate Revocation List (CRL).');
493

    
494
$section->addInput(new Form_Input(
495
	'serial',
496
	'Serial for next certificate',
497
	'number',
498
	$pconfig['serial']
499
))->setHelp('Enter a decimal number to be used as the serial number for the next '.
500
	'certificate to be created using this CA.');
501

    
502
$form->add($section);
503

    
504
$section = new Form_Section('Internal Certificate Authority');
505
$section->addClass('toggle-internal', 'toggle-intermediate', 'collapse');
506

    
507
$allCas = array();
508
foreach ($a_ca as $ca) {
509
	if (!$ca['prv']) {
510
			continue;
511
	}
512

    
513
	$allCas[ $ca['refid'] ] = $ca['descr'];
514
}
515

    
516
$group = new Form_Group('Signing Certificate Authority');
517
$group->addClass('toggle-intermediate', 'collapse');
518
$group->add(new Form_Select(
519
	'caref',
520
	null,
521
	$pconfig['caref'],
522
	$allCas
523
));
524
$section->add($group);
525

    
526
$section->addInput(new Form_Select(
527
	'keylen',
528
	'Key length (bits)',
529
	$pconfig['keylen'],
530
	array_combine($ca_keylens, $ca_keylens)
531
));
532

    
533
$section->addInput(new Form_Select(
534
	'digest_alg',
535
	'Digest Algorithm',
536
	$pconfig['digest_alg'],
537
	array_combine($openssl_digest_algs, $openssl_digest_algs)
538
))->setHelp('NOTE: It is recommended to use an algorithm stronger than SHA1 '.
539
	'when possible.');
540

    
541
$section->addInput(new Form_Input(
542
	'lifetime',
543
	'Lifetime (days)',
544
	'number',
545
	$pconfig['lifetime']
546
));
547

    
548
$section->addInput(new Form_Select(
549
	'dn_country',
550
	'Country Code',
551
	$pconfig['dn_country'],
552
	$dn_cc
553
));
554

    
555
$section->addInput(new Form_Input(
556
	'dn_state',
557
	'State or Province',
558
	'text',
559
	$pconfig['dn_state'],
560
	['placeholder' => 'e.g. Texas']
561
));
562

    
563
$section->addInput(new Form_Input(
564
	'dn_city',
565
	'City',
566
	'text',
567
	$pconfig['dn_city'],
568
	['placeholder' => 'e.g. Austin']
569
));
570

    
571
$section->addInput(new Form_Input(
572
	'dn_organization',
573
	'Organization',
574
	'text',
575
	$pconfig['dn_organization'],
576
	['placeholder' => 'e.g. My Company Inc']
577
));
578

    
579
$section->addInput(new Form_Input(
580
	'dn_organizationalunit',
581
	'Organizational Unit',
582
	'text',
583
	$pconfig['dn_organizationalunit'],
584
	['placeholder' => 'e.g. My Department Name (optional)']
585
));
586

    
587
$section->addInput(new Form_Input(
588
	'dn_email',
589
	'Email Address',
590
	'email',
591
	$pconfig['dn_email'],
592
	['placeholder' => 'e.g. admin@mycompany.com']
593
));
594

    
595
$section->addInput(new Form_Input(
596
	'dn_commonname',
597
	'Common Name',
598
	'text',
599
	$pconfig['dn_commonname'],
600
	['placeholder' => 'e.g. internal-ca']
601
));
602

    
603
$form->add($section);
604

    
605
print $form;
606

    
607
$internal_ca_count = 0;
608
foreach ($a_ca as $ca) {
609
	if ($ca['prv']) {
610
		$internal_ca_count++;
611
	}
612
}
613

    
614
include('foot.inc');
615
?>
(194-194/227)