Project

General

Profile

Download (16.4 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-2016 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
global $openssl_crl_status;
36

    
37
$crl_methods = array(
38
	"internal" => gettext("Create an internal Certificate Revocation List"),
39
	"existing" => gettext("Import an existing Certificate Revocation List"));
40

    
41
if (isset($_REQUEST['id']) && ctype_alnum($_REQUEST['id'])) {
42
	$id = $_REQUEST['id'];
43
}
44

    
45
if (!is_array($config['ca'])) {
46
	$config['ca'] = array();
47
}
48

    
49
$a_ca =& $config['ca'];
50

    
51
if (!is_array($config['cert'])) {
52
	$config['cert'] = array();
53
}
54

    
55
$a_cert =& $config['cert'];
56

    
57
if (!is_array($config['crl'])) {
58
	$config['crl'] = array();
59
}
60

    
61
$a_crl =& $config['crl'];
62

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

    
69
$act = $_REQUEST['act'];
70

    
71

    
72
if (!empty($id)) {
73
	$thiscrl =& lookup_crl($id);
74
}
75

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

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

    
101
if ($act == "new") {
102
	$pconfig['method'] = $_REQUEST['method'];
103
	$pconfig['caref'] = $_REQUEST['caref'];
104
	$pconfig['lifetime'] = "9999";
105
	$pconfig['serial'] = "0";
106
}
107

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

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

    
121
if ($act == "addcert") {
122

    
123
	unset($input_errors);
124
	$pconfig = $_REQUEST;
125

    
126
	if (!$pconfig['crlref'] || !$pconfig['certref']) {
127
		pfSenseHeader("system_crlmanager.php");
128
		exit;
129
	}
130

    
131
	// certref, crlref
132
	$crl =& lookup_crl($pconfig['crlref']);
133
	$cert = lookup_cert($pconfig['certref']);
134

    
135
	if (!$crl['caref'] || !$cert['caref']) {
136
		$input_errors[] = gettext("Both the Certificate and CRL must be specified.");
137
	}
138

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

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

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

    
190
if ($_POST['save']) {
191
	$input_errors = array();
192
	$pconfig = $_POST;
193

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

    
208
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
209

    
210
	if (preg_match("/[\?\>\<\&\/\\\"\']/", $pconfig['descr'])) {
211
		array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
212
	}
213

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

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

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

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

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

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

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

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

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

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

    
266
function method_change() {
267

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

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

    
282
//]]>
283
</script>
284

    
285
<?php
286

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

    
290
	$list = array();
291

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

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

    
300
	return($list);
301
}
302

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

    
306
	$list = array();
307

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

    
312
	return($list);
313
}
314

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

    
318
	$list = array();
319

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

    
324
	return($list);
325
}
326

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

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

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

    
341
if ($act == "new" || $act == gettext("Save") || $input_errors) {
342
	if (!isset($id)) {
343
		$form = new Form();
344

    
345
		$section = new Form_Section('Create new Revocation List');
346

    
347
		$section->addInput(new Form_Select(
348
			'method',
349
			'*Method',
350
			$pconfig['method'],
351
			build_method_list()
352
		));
353

    
354
	}
355

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

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

    
370
	$form->add($section);
371

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

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

    
381
	$form->add($section);
382

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

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

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

    
402
	$form->add($section);
403

    
404
	if (isset($id) && $thiscrl) {
405
		$section->addInput(new Form_Input(
406
			'id',
407
			null,
408
			'hidden',
409
			$id
410
		));
411
	}
412

    
413
	print($form);
414

    
415
} elseif ($act == "editimported") {
416

    
417
	$form = new Form();
418

    
419
	$section = new Form_Section('Edit Imported Certificate Revocation List');
420

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

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

    
434
	$section->addInput(new Form_Input(
435
		'id',
436
		null,
437
		'hidden',
438
		$id
439
	));
440

    
441
	$section->addInput(new Form_Input(
442
		'act',
443
		null,
444
		'hidden',
445
		'editimported'
446
	));
447

    
448
	$form->add($section);
449

    
450
	print($form);
451

    
452
} elseif ($act == "edit") {
453
	$crl = $thiscrl;
454

    
455
	$form = new Form(false);
456
?>
457

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

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

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

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

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

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

    
542
		$section->add($group);
543

    
544
		$section->addInput(new Form_Input(
545
			'id',
546
			null,
547
			'hidden',
548
			$crl['refid']
549
		));
550

    
551
		$section->addInput(new Form_Input(
552
			'act',
553
			null,
554
			'hidden',
555
			'addcert'
556
		));
557

    
558
		$section->addInput(new Form_Input(
559
			'crlref',
560
			null,
561
			'hidden',
562
			$crl['refid']
563
		));
564

    
565
		$form->add($section);
566
	}
567

    
568
	print($form);
569
} else {
570
?>
571

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

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

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

    
675

    
676
<?php
677
}
678
?>
679

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

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

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

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

    
705
<?php include("foot.inc");
706

    
(199-199/231)