Project

General

Profile

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

    
22
##|+PRIV
23
##|*IDENT=page-system-crlmanager
24
##|*NAME=System: CRL Manager
25
##|*DESCR=Allow access to the 'System: CRL Manager' page.
26
##|*MATCH=system_crlmanager.php*
27
##|-PRIV
28

    
29
require_once("guiconfig.inc");
30
require_once("certs.inc");
31
require_once("openvpn.inc");
32
require_once("pfsense-utils.inc");
33
require_once("vpn.inc");
34

    
35
$max_lifetime = crl_get_max_lifetime();
36
$default_lifetime = 3650;
37
if ($max_lifetime < $default_lifetime) {
38
	$default_lifetime = $max_lifetime;
39
}
40

    
41
global $openssl_crl_status;
42

    
43
$crl_methods = array(
44
	"internal" => gettext("Create an internal Certificate Revocation List"),
45
	"existing" => gettext("Import an existing Certificate Revocation List"));
46

    
47
if (isset($_REQUEST['id']) && ctype_alnum($_REQUEST['id'])) {
48
	$id = $_REQUEST['id'];
49
}
50

    
51
init_config_arr(array('ca'));
52
$a_ca = &$config['ca'];
53

    
54
init_config_arr(array('cert'));
55
$a_cert = &$config['cert'];
56

    
57
init_config_arr(array('crl'));
58
$a_crl = &$config['crl'];
59

    
60
foreach ($a_crl as $cid => $acrl) {
61
	if (!isset($acrl['refid'])) {
62
		unset ($a_crl[$cid]);
63
	}
64
}
65

    
66
$act = $_REQUEST['act'];
67

    
68

    
69
if (!empty($id)) {
70
	$thiscrl =& lookup_crl($id);
71
}
72

    
73
// If we were given an invalid crlref in the id, no sense in continuing as it would only cause errors.
74
if (!$thiscrl && (($act != "") && ($act != "new"))) {
75
	pfSenseHeader("system_crlmanager.php");
76
	$act="";
77
	$savemsg = gettext("Invalid CRL reference.");
78
	$class = "danger";
79
}
80

    
81
if ($_POST['act'] == "del") {
82
	$name = htmlspecialchars($thiscrl['descr']);
83
	if (crl_in_use($id)) {
84
		$savemsg = sprintf(gettext("Certificate Revocation List %s is in use and cannot be deleted."), $name);
85
		$class = "danger";
86
	} else {
87
		foreach ($a_crl as $cid => $acrl) {
88
			if ($acrl['refid'] == $thiscrl['refid']) {
89
				unset($a_crl[$cid]);
90
			}
91
		}
92
		write_config("Deleted CRL {$name}.");
93
		$savemsg = sprintf(gettext("Certificate Revocation List %s successfully deleted."), $name);
94
		$class = "success";
95
	}
96
}
97

    
98
if ($act == "new") {
99
	$pconfig['method'] = $_REQUEST['method'];
100
	$pconfig['caref'] = $_REQUEST['caref'];
101
	$pconfig['lifetime'] = $default_lifetime;
102
	$pconfig['serial'] = "0";
103
}
104

    
105
if ($act == "exp") {
106
	crl_update($thiscrl);
107
	$exp_name = urlencode("{$thiscrl['descr']}.crl");
108
	$exp_data = base64_decode($thiscrl['text']);
109
	$exp_size = strlen($exp_data);
110

    
111
	header("Content-Type: application/octet-stream");
112
	header("Content-Disposition: attachment; filename={$exp_name}");
113
	header("Content-Length: $exp_size");
114
	echo $exp_data;
115
	exit;
116
}
117

    
118
if ($act == "addcert") {
119
	unset($input_errors);
120
	$pconfig = $_REQUEST;
121

    
122
	if (!$pconfig['crlref'] || !$pconfig['certref']) {
123
		pfSenseHeader("system_crlmanager.php");
124
		exit;
125
	}
126

    
127
	// certref, crlref
128
	$crl =& lookup_crl($pconfig['crlref']);
129
	$cert = lookup_cert($pconfig['certref']);
130

    
131
	if (!$crl['caref'] || !$cert['caref']) {
132
		$input_errors[] = gettext("Both the Certificate and CRL must be specified.");
133
	}
134

    
135
	if ($crl['caref'] != $cert['caref']) {
136
		$input_errors[] = gettext("CA mismatch between the Certificate and CRL. Unable to Revoke.");
137
	}
138
	if (!is_crl_internal($crl)) {
139
		$input_errors[] = gettext("Cannot revoke certificates for an imported/external CRL.");
140
	}
141

    
142
	if (!$input_errors) {
143
		$reason = (empty($pconfig['crlreason'])) ? 0 : $pconfig['crlreason'];
144
		cert_revoke($cert, $crl, $reason);
145
		// refresh IPsec and OpenVPN CRLs
146
		openvpn_refresh_crls();
147
		vpn_ipsec_configure();
148
		write_config("Revoked cert {$cert['descr']} in CRL {$crl['descr']}.");
149
		pfSenseHeader("system_crlmanager.php");
150
		exit;
151
	}
152
}
153

    
154
if ($act == "delcert") {
155
	if (!is_array($thiscrl['cert'])) {
156
		pfSenseHeader("system_crlmanager.php");
157
		exit;
158
	}
159
	$found = false;
160
	foreach ($thiscrl['cert'] as $acert) {
161
		if ($acert['refid'] == $_REQUEST['certref']) {
162
			$found = true;
163
			$thiscert = $acert;
164
		}
165
	}
166
	if (!$found) {
167
		pfSenseHeader("system_crlmanager.php");
168
		exit;
169
	}
170
	$certname = htmlspecialchars($thiscert['descr']);
171
	$crlname = htmlspecialchars($thiscrl['descr']);
172
	if (cert_unrevoke($thiscert, $thiscrl)) {
173
		$savemsg = sprintf(gettext('Deleted Certificate %1$s from CRL %2$s.'), $certname, $crlname);
174
		$class = "success";
175
		// refresh IPsec and OpenVPN CRLs
176
		openvpn_refresh_crls();
177
		vpn_ipsec_configure();
178
		write_config($savemsg);
179
	} else {
180
		$savemsg = sprintf(gettext('Failed to delete Certificate %1$s from CRL %2$s.'), $certname, $crlname);
181
		$class = "danger";
182
	}
183
	$act="edit";
184
}
185

    
186
if ($_POST['save']) {
187
	$input_errors = array();
188
	$pconfig = $_POST;
189

    
190
	/* input validation */
191
	if (($pconfig['method'] == "existing") || ($act == "editimported")) {
192
		$reqdfields = explode(" ", "descr crltext");
193
		$reqdfieldsn = array(
194
			gettext("Descriptive name"),
195
			gettext("Certificate Revocation List data"));
196
	}
197
	if ($pconfig['method'] == "internal") {
198
		$reqdfields = explode(" ", "descr caref");
199
		$reqdfieldsn = array(
200
			gettext("Descriptive name"),
201
			gettext("Certificate Authority"));
202
	}
203

    
204
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
205

    
206
	if (preg_match("/[\?\>\<\&\/\\\"\']/", $pconfig['descr'])) {
207
		array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
208
	}
209
	if ($pconfig['lifetime'] > $max_lifetime) {
210
		$input_errors[] = gettext("Lifetime is longer than the maximum allowed value. Use a shorter lifetime.");
211
	}
212

    
213
	/* save modifications */
214
	if (!$input_errors) {
215
		$result = false;
216

    
217
		if ($thiscrl) {
218
			$crl =& $thiscrl;
219
		} else {
220
			$crl = array();
221
			$crl['refid'] = uniqid();
222
		}
223

    
224
		$crl['descr'] = $pconfig['descr'];
225
		if ($act != "editimported") {
226
			$crl['caref'] = $pconfig['caref'];
227
			$crl['method'] = $pconfig['method'];
228
		}
229

    
230
		if (($pconfig['method'] == "existing") || ($act == "editimported")) {
231
			$crl['text'] = base64_encode($pconfig['crltext']);
232
		}
233

    
234
		if ($pconfig['method'] == "internal") {
235
			$crl['serial'] = empty($pconfig['serial']) ? 9999 : $pconfig['serial'];
236
			$crl['lifetime'] = empty($pconfig['lifetime']) ? $default_lifetime : $pconfig['lifetime'];
237
			$crl['cert'] = array();
238
		}
239

    
240
		if (!$thiscrl) {
241
			$a_crl[] = $crl;
242
		}
243

    
244
		write_config("Saved CRL {$crl['descr']}");
245
		// refresh IPsec and OpenVPN CRLs
246
		openvpn_refresh_crls();
247
		vpn_ipsec_configure();
248
		pfSenseHeader("system_crlmanager.php");
249
	}
250
}
251

    
252
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificate Revocation"));
253
$pglinks = array("", "system_camanager.php", "system_crlmanager.php");
254

    
255
if ($act == "new" || $act == gettext("Save") || $input_errors || $act == "edit") {
256
	$pgtitle[] = gettext('Edit');
257
	$pglinks[] = "@self";
258
}
259
include("head.inc");
260
?>
261

    
262
<script type="text/javascript">
263
//<![CDATA[
264

    
265
function method_change() {
266

    
267
	method = document.iform.method.value;
268

    
269
	switch (method) {
270
		case "internal":
271
			document.getElementById("existing").style.display="none";
272
			document.getElementById("internal").style.display="";
273
			break;
274
		case "existing":
275
			document.getElementById("existing").style.display="";
276
			document.getElementById("internal").style.display="none";
277
			break;
278
	}
279
}
280

    
281
//]]>
282
</script>
283

    
284
<?php
285

    
286
function build_method_list() {
287
	global $_POST, $crl_methods;
288

    
289
	$list = array();
290

    
291
	foreach ($crl_methods as $method => $desc) {
292
		if (($_POST['importonly'] == "yes") && ($method != "existing")) {
293
			continue;
294
		}
295

    
296
		$list[$method] = $desc;
297
	}
298

    
299
	return($list);
300
}
301

    
302
function build_ca_list() {
303
	global $a_ca;
304

    
305
	$list = array();
306

    
307
	foreach ($a_ca as $ca) {
308
		$list[$ca['refid']] = $ca['descr'];
309
	}
310

    
311
	return($list);
312
}
313

    
314
function build_cacert_list() {
315
	global $ca_certs;
316

    
317
	$list = array();
318

    
319
	foreach ($ca_certs as $cert) {
320
		$list[$cert['refid']] = $cert['descr'];
321
	}
322

    
323
	return($list);
324
}
325

    
326
if ($input_errors) {
327
	print_input_errors($input_errors);
328
}
329

    
330
if ($savemsg) {
331
	print_info_box($savemsg, $class);
332
}
333

    
334
$tab_array = array();
335
$tab_array[] = array(gettext("CAs"), false, "system_camanager.php");
336
$tab_array[] = array(gettext("Certificates"), false, "system_certmanager.php");
337
$tab_array[] = array(gettext("Certificate Revocation"), true, "system_crlmanager.php");
338
display_top_tabs($tab_array);
339

    
340
if ($act == "new" || $act == gettext("Save") || $input_errors) {
341
	$form = new Form();
342

    
343
	$section = new Form_Section('Create new Revocation List');
344

    
345
	if (!isset($id)) {
346
		$section->addInput(new Form_Select(
347
			'method',
348
			'*Method',
349
			$pconfig['method'],
350
			build_method_list()
351
		));
352
	}
353

    
354
	$section->addInput(new Form_Input(
355
		'descr',
356
		'*Descriptive name',
357
		'text',
358
		$pconfig['descr']
359
	));
360

    
361
	$section->addInput(new Form_Select(
362
		'caref',
363
		'*Certificate Authority',
364
		$pconfig['caref'],
365
		build_ca_list()
366
	));
367

    
368
	$form->add($section);
369

    
370
	$section = new Form_Section('Existing Certificate Revocation List');
371
	$section->addClass('existing');
372

    
373
	$section->addInput(new Form_Textarea(
374
		'crltext',
375
		'*CRL data',
376
		$pconfig['crltext']
377
		))->setHelp('Paste a Certificate Revocation List in X.509 CRL format here.');
378

    
379
	$form->add($section);
380

    
381
	$section = new Form_Section('Internal Certificate Revocation List');
382
	$section->addClass('internal');
383

    
384
	$section->addInput(new Form_Input(
385
		'lifetime',
386
		'Lifetime (Days)',
387
		'number',
388
		$pconfig['lifetime'],
389
		['max' => $max_lifetime]
390
	));
391

    
392
	$section->addInput(new Form_Input(
393
		'serial',
394
		'Serial',
395
		'number',
396
		$pconfig['serial'],
397
		['min' => '0', 'max' => '9999']
398
	));
399

    
400
	$form->add($section);
401

    
402
	if (isset($id) && $thiscrl) {
403
		$form->addGlobal(new Form_Input(
404
			'id',
405
			null,
406
			'hidden',
407
			$id
408
		));
409
	}
410

    
411
	print($form);
412

    
413
} elseif ($act == "editimported") {
414

    
415
	$form = new Form();
416

    
417
	$section = new Form_Section('Edit Imported Certificate Revocation List');
418

    
419
	$section->addInput(new Form_Input(
420
		'descr',
421
		'*Descriptive name',
422
		'text',
423
		$pconfig['descr']
424
	));
425

    
426
	$section->addInput(new Form_Textarea(
427
		'crltext',
428
		'*CRL data',
429
		$pconfig['crltext']
430
	))->setHelp('Paste a Certificate Revocation List in X.509 CRL format here.');
431

    
432
	$form->addGlobal(new Form_Input(
433
		'id',
434
		null,
435
		'hidden',
436
		$id
437
	));
438

    
439
	$form->addGlobal(new Form_Input(
440
		'act',
441
		null,
442
		'hidden',
443
		'editimported'
444
	));
445

    
446
	$form->add($section);
447

    
448
	print($form);
449

    
450
} elseif ($act == "edit") {
451
	$crl = $thiscrl;
452

    
453
	$form = new Form(false);
454
?>
455

    
456
	<div class="panel panel-default">
457
		<div class="panel-heading"><h2 class="panel-title"><?=gettext("Currently Revoked Certificates for CRL") . ': ' . $crl['descr']?></h2></div>
458
		<div class="panel-body table-responsive">
459
<?php
460
	if (!is_array($crl['cert']) || (count($crl['cert']) == 0)) {
461
		print_info_box(gettext("No certificates found for this CRL."), 'danger');
462
	} else {
463
?>
464
			<table class="table table-striped table-hover table-condensed">
465
				<thead>
466
					<tr>
467
						<th><?=gettext("Certificate Name")?></th>
468
						<th><?=gettext("Revocation Reason")?></th>
469
						<th><?=gettext("Revoked At")?></th>
470
						<th></th>
471
					</tr>
472
				</thead>
473
				<tbody>
474
<?php
475
		foreach ($crl['cert'] as $i => $cert):
476
			$name = htmlspecialchars($cert['descr']);
477
?>
478
					<tr>
479
						<td class="listlr">
480
							<?=$name; ?>
481
						</td>
482
						<td class="listlr">
483
							<?=$openssl_crl_status[$cert["reason"]]; ?>
484
						</td>
485
						<td class="listlr">
486
							<?=date("D M j G:i:s T Y", $cert["revoke_time"]); ?>
487
						</td>
488
						<td class="list">
489
							<a href="system_crlmanager.php?act=delcert&amp;id=<?=$crl['refid']; ?>&amp;certref=<?=$cert['refid']; ?>" usepost>
490
								<i class="fa fa-trash" title="<?=gettext("Delete this certificate from the CRL")?>" alt="<?=gettext("Delete this certificate from the CRL")?>"></i>
491
							</a>
492
						</td>
493
					</tr>
494
<?php
495
		endforeach;
496
?>
497
				</tbody>
498
			</table>
499
<?php
500
	}
501
?>
502
		</div>
503
	</div>
504
<?php
505

    
506
	$ca_certs = array();
507
	foreach ($a_cert as $cert) {
508
		if ($cert['caref'] == $crl['caref'] && !is_cert_revoked($cert, $id)) {
509
			$ca_certs[] = $cert;
510
		}
511
	}
512

    
513
	if (count($ca_certs) == 0) {
514
		print_info_box(gettext("No certificates found for this CA."), 'danger');
515
	} else {
516
		$section = new Form_Section('Choose a Certificate to Revoke');
517
		$group = new Form_Group(null);
518

    
519
		$group->add(new Form_Select(
520
			'certref',
521
			null,
522
			$pconfig['certref'],
523
			build_cacert_list()
524
			))->setWidth(4)->setHelp('Certificate');
525

    
526
		$group->add(new Form_Select(
527
			'crlreason',
528
			null,
529
			-1,
530
			$openssl_crl_status
531
			))->setHelp('Reason');
532

    
533
		$group->add(new Form_Button(
534
			'submit',
535
			'Add',
536
			null,
537
			'fa-plus'
538
			))->addClass('btn-success btn-sm');
539

    
540
		$section->add($group);
541

    
542
		$form->addGlobal(new Form_Input(
543
			'id',
544
			null,
545
			'hidden',
546
			$crl['refid']
547
		));
548

    
549
		$form->addGlobal(new Form_Input(
550
			'act',
551
			null,
552
			'hidden',
553
			'addcert'
554
		));
555

    
556
		$form->addGlobal(new Form_Input(
557
			'crlref',
558
			null,
559
			'hidden',
560
			$crl['refid']
561
		));
562

    
563
		$form->add($section);
564
	}
565

    
566
	print($form);
567
} else {
568
?>
569

    
570
	<div class="panel panel-default">
571
		<div class="panel-heading"><h2 class="panel-title"><?=gettext("Additional Certificate Revocation Lists")?></h2></div>
572
		<div class="panel-body table-responsive">
573
			<table class="table table-striped table-hover table-condensed table-rowdblclickedit">
574
				<thead>
575
					<tr>
576
						<th><?=gettext("Name")?></th>
577
						<th><?=gettext("Internal")?></th>
578
						<th><?=gettext("Certificates")?></th>
579
						<th><?=gettext("In Use")?></th>
580
						<th><?=gettext("Actions")?></th>
581
					</tr>
582
				</thead>
583
				<tbody>
584
<?php
585
	$pluginparams = array();
586
	$pluginparams['type'] = 'certificates';
587
	$pluginparams['event'] = 'used_crl';
588
	$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
589
	// Map CRLs to CAs in one pass
590
	$ca_crl_map = array();
591
	foreach ($a_crl as $crl) {
592
		$ca_crl_map[$crl['caref']][] = $crl['refid'];
593
	}
594

    
595
	$i = 0;
596
	foreach ($a_ca as $ca):
597
		$name = htmlspecialchars($ca['descr']);
598

    
599
		if ($ca['prv']) {
600
			$cainternal = "YES";
601
		} else {
602
			$cainternal = "NO";
603
		}
604
?>
605
					<tr>
606
						<td colspan="4">
607
							<?=$name?>
608
						</td>
609
						<td>
610
<?php
611
		if ($cainternal == "YES"):
612
?>
613
							<a href="system_crlmanager.php?act=new&amp;caref=<?=$ca['refid']; ?>" class="btn btn-xs btn-success">
614
								<i class="fa fa-plus icon-embed-btn"></i>
615
								<?=gettext("Add or Import CRL")?>
616
							</a>
617
<?php
618
		else:
619
?>
620
							<a href="system_crlmanager.php?act=new&amp;caref=<?=$ca['refid']; ?>&amp;importonly=yes" class="btn btn-xs btn-success">
621
								<i class="fa fa-plus icon-embed-btn"></i>
622
								<?=gettext("Add or Import CRL")?>
623
							</a>
624
<?php
625
		endif;
626
?>
627
						</td>
628
					</tr>
629
<?php
630
		if (is_array($ca_crl_map[$ca['refid']])):
631
			foreach ($ca_crl_map[$ca['refid']] as $crl):
632
				$tmpcrl = lookup_crl($crl);
633
				$internal = is_crl_internal($tmpcrl);
634
				if ($internal && (!isset($tmpcrl['cert']) || empty($tmpcrl['cert'])) ) {
635
					$tmpcrl['cert'] = array();
636
				}
637
				$inuse = crl_in_use($tmpcrl['refid']);
638
?>
639
					<tr>
640
						<td><?=$tmpcrl['descr']; ?></td>
641
						<td><i class="fa fa-<?=($internal) ? "check" : "times"; ?>"></i></td>
642
						<td><?=($internal) ? count($tmpcrl['cert']) : "Unknown (imported)"; ?></td>
643
						<td><i class="fa fa-<?=($inuse) ? "check" : "times"; ?>"></i>
644
						<?php echo cert_usedby_description($tmpcrl['refid'], $certificates_used_by_packages); ?>
645
						</td>
646
						<td>
647
							<a href="system_crlmanager.php?act=exp&amp;id=<?=$tmpcrl['refid']?>" class="fa fa-download" title="<?=gettext("Export CRL")?>" ></a>
648
<?php
649
				if ($internal): ?>
650
							<a href="system_crlmanager.php?act=edit&amp;id=<?=$tmpcrl['refid']?>" class="fa fa-pencil" title="<?=gettext("Edit CRL")?>"></a>
651
<?php
652
				else:
653
?>
654
							<a href="system_crlmanager.php?act=editimported&amp;id=<?=$tmpcrl['refid']?>" class="fa fa-pencil" title="<?=gettext("Edit CRL")?>"></a>
655
<?php			endif;
656
				if (!$inuse):
657
?>
658
							<a href="system_crlmanager.php?act=del&amp;id=<?=$tmpcrl['refid']?>" class="fa fa-trash" title="<?=gettext("Delete CRL")?>" usepost></a>
659
<?php
660
				endif;
661
?>
662
						</td>
663
					</tr>
664
<?php
665
				$i++;
666
				endforeach;
667
			endif;
668
			$i++;
669
		endforeach;
670
?>
671
				</tbody>
672
			</table>
673
		</div>
674
	</div>
675

    
676

    
677
<?php
678
}
679
?>
680

    
681
<script type="text/javascript">
682
//<![CDATA[
683
events.push(function() {
684

    
685
	// Hides all elements of the specified class. This will usually be a section or group
686
	function hideClass(s_class, hide) {
687
		if (hide) {
688
			$('.' + s_class).hide();
689
		} else {
690
			$('.' + s_class).show();
691
		}
692
	}
693

    
694
	// When the 'method" selector is changed, we show/hide certain sections
695
	$('#method').on('change', function() {
696
		hideClass('internal', ($('#method').val() == 'existing'));
697
		hideClass('existing', ($('#method').val() == 'internal'));
698
	});
699

    
700
	hideClass('internal', ($('#method').val() == 'existing'));
701
	hideClass('existing', ($('#method').val() == 'internal'));
702
});
703
//]]>
704
</script>
705

    
706
<?php include("foot.inc");
(193-193/225)