Project

General

Profile

Download (16.8 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-2018 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
if (!is_array($config['ca'])) {
52
	$config['ca'] = array();
53
}
54

    
55
$a_ca =& $config['ca'];
56

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

    
61
$a_cert =& $config['cert'];
62

    
63
if (!is_array($config['crl'])) {
64
	$config['crl'] = array();
65
}
66

    
67
$a_crl =& $config['crl'];
68

    
69
foreach ($a_crl as $cid => $acrl) {
70
	if (!isset($acrl['refid'])) {
71
		unset ($a_crl[$cid]);
72
	}
73
}
74

    
75
$act = $_REQUEST['act'];
76

    
77

    
78
if (!empty($id)) {
79
	$thiscrl =& lookup_crl($id);
80
}
81

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

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

    
107
if ($act == "new") {
108
	$pconfig['method'] = $_REQUEST['method'];
109
	$pconfig['caref'] = $_REQUEST['caref'];
110
	$pconfig['lifetime'] = $default_lifetime;
111
	$pconfig['serial'] = "0";
112
}
113

    
114
if ($act == "exp") {
115
	crl_update($thiscrl);
116
	$exp_name = urlencode("{$thiscrl['descr']}.crl");
117
	$exp_data = base64_decode($thiscrl['text']);
118
	$exp_size = strlen($exp_data);
119

    
120
	header("Content-Type: application/octet-stream");
121
	header("Content-Disposition: attachment; filename={$exp_name}");
122
	header("Content-Length: $exp_size");
123
	echo $exp_data;
124
	exit;
125
}
126

    
127
if ($act == "addcert") {
128

    
129
	unset($input_errors);
130
	$pconfig = $_REQUEST;
131

    
132
	if (!$pconfig['crlref'] || !$pconfig['certref']) {
133
		pfSenseHeader("system_crlmanager.php");
134
		exit;
135
	}
136

    
137
	// certref, crlref
138
	$crl =& lookup_crl($pconfig['crlref']);
139
	$cert = lookup_cert($pconfig['certref']);
140

    
141
	if (!$crl['caref'] || !$cert['caref']) {
142
		$input_errors[] = gettext("Both the Certificate and CRL must be specified.");
143
	}
144

    
145
	if ($crl['caref'] != $cert['caref']) {
146
		$input_errors[] = gettext("CA mismatch between the Certificate and CRL. Unable to Revoke.");
147
	}
148
	if (!is_crl_internal($crl)) {
149
		$input_errors[] = gettext("Cannot revoke certificates for an imported/external CRL.");
150
	}
151

    
152
	if (!$input_errors) {
153
		$reason = (empty($pconfig['crlreason'])) ? 0 : $pconfig['crlreason'];
154
		cert_revoke($cert, $crl, $reason);
155
		// refresh IPsec and OpenVPN CRLs
156
		openvpn_refresh_crls();
157
		vpn_ipsec_configure();
158
		write_config("Revoked cert {$cert['descr']} in CRL {$crl['descr']}.");
159
		pfSenseHeader("system_crlmanager.php");
160
		exit;
161
	}
162
}
163

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

    
196
if ($_POST['save']) {
197
	$input_errors = array();
198
	$pconfig = $_POST;
199

    
200
	/* input validation */
201
	if (($pconfig['method'] == "existing") || ($act == "editimported")) {
202
		$reqdfields = explode(" ", "descr crltext");
203
		$reqdfieldsn = array(
204
			gettext("Descriptive name"),
205
			gettext("Certificate Revocation List data"));
206
	}
207
	if ($pconfig['method'] == "internal") {
208
		$reqdfields = explode(" ", "descr caref");
209
		$reqdfieldsn = array(
210
			gettext("Descriptive name"),
211
			gettext("Certificate Authority"));
212
	}
213

    
214
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
215

    
216
	if (preg_match("/[\?\>\<\&\/\\\"\']/", $pconfig['descr'])) {
217
		array_push($input_errors, "The field 'Descriptive Name' contains invalid characters.");
218
	}
219
	if ($pconfig['lifetime'] > $max_lifetime) {
220
		$input_errors[] = gettext("Lifetime is longer than the maximum allowed value. Use a shorter lifetime.");
221
	}
222

    
223
	/* save modifications */
224
	if (!$input_errors) {
225
		$result = false;
226

    
227
		if ($thiscrl) {
228
			$crl =& $thiscrl;
229
		} else {
230
			$crl = array();
231
			$crl['refid'] = uniqid();
232
		}
233

    
234
		$crl['descr'] = $pconfig['descr'];
235
		if ($act != "editimported") {
236
			$crl['caref'] = $pconfig['caref'];
237
			$crl['method'] = $pconfig['method'];
238
		}
239

    
240
		if (($pconfig['method'] == "existing") || ($act == "editimported")) {
241
			$crl['text'] = base64_encode($pconfig['crltext']);
242
		}
243

    
244
		if ($pconfig['method'] == "internal") {
245
			$crl['serial'] = empty($pconfig['serial']) ? 9999 : $pconfig['serial'];
246
			$crl['lifetime'] = empty($pconfig['lifetime']) ? $default_lifetime : $pconfig['lifetime'];
247
			$crl['cert'] = array();
248
		}
249

    
250
		if (!$thiscrl) {
251
			$a_crl[] = $crl;
252
		}
253

    
254
		write_config("Saved CRL {$crl['descr']}");
255
		// refresh IPsec and OpenVPN CRLs
256
		openvpn_refresh_crls();
257
		vpn_ipsec_configure();
258
		pfSenseHeader("system_crlmanager.php");
259
	}
260
}
261

    
262
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("Certificate Revocation"));
263
$pglinks = array("", "system_camanager.php", "system_crlmanager.php");
264

    
265
if ($act == "new" || $act == gettext("Save") || $input_errors || $act == "edit") {
266
	$pgtitle[] = gettext('Edit');
267
	$pglinks[] = "@self";
268
}
269
include("head.inc");
270
?>
271

    
272
<script type="text/javascript">
273
//<![CDATA[
274

    
275
function method_change() {
276

    
277
	method = document.iform.method.value;
278

    
279
	switch (method) {
280
		case "internal":
281
			document.getElementById("existing").style.display="none";
282
			document.getElementById("internal").style.display="";
283
			break;
284
		case "existing":
285
			document.getElementById("existing").style.display="";
286
			document.getElementById("internal").style.display="none";
287
			break;
288
	}
289
}
290

    
291
//]]>
292
</script>
293

    
294
<?php
295

    
296
function build_method_list() {
297
	global $_POST, $crl_methods;
298

    
299
	$list = array();
300

    
301
	foreach ($crl_methods as $method => $desc) {
302
		if (($_POST['importonly'] == "yes") && ($method != "existing")) {
303
			continue;
304
		}
305

    
306
		$list[$method] = $desc;
307
	}
308

    
309
	return($list);
310
}
311

    
312
function build_ca_list() {
313
	global $a_ca;
314

    
315
	$list = array();
316

    
317
	foreach ($a_ca as $ca) {
318
		$list[$ca['refid']] = $ca['descr'];
319
	}
320

    
321
	return($list);
322
}
323

    
324
function build_cacert_list() {
325
	global $ca_certs;
326

    
327
	$list = array();
328

    
329
	foreach ($ca_certs as $cert) {
330
		$list[$cert['refid']] = $cert['descr'];
331
	}
332

    
333
	return($list);
334
}
335

    
336
if ($input_errors) {
337
	print_input_errors($input_errors);
338
}
339

    
340
if ($savemsg) {
341
	print_info_box($savemsg, $class);
342
}
343

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

    
350
if ($act == "new" || $act == gettext("Save") || $input_errors) {
351
	if (!isset($id)) {
352
		$form = new Form();
353

    
354
		$section = new Form_Section('Create new Revocation List');
355

    
356
		$section->addInput(new Form_Select(
357
			'method',
358
			'*Method',
359
			$pconfig['method'],
360
			build_method_list()
361
		));
362

    
363
	}
364

    
365
	$section->addInput(new Form_Input(
366
		'descr',
367
		'*Descriptive name',
368
		'text',
369
		$pconfig['descr']
370
	));
371

    
372
	$section->addInput(new Form_Select(
373
		'caref',
374
		'*Certificate Authority',
375
		$pconfig['caref'],
376
		build_ca_list()
377
	));
378

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

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

    
384
	$section->addInput(new Form_Textarea(
385
		'crltext',
386
		'*CRL data',
387
		$pconfig['crltext']
388
		))->setHelp('Paste a Certificate Revocation List in X.509 CRL format here.');
389

    
390
	$form->add($section);
391

    
392
	$section = new Form_Section('Internal Certificate Revocation List');
393
	$section->addClass('internal');
394

    
395
	$section->addInput(new Form_Input(
396
		'lifetime',
397
		'Lifetime (Days)',
398
		'number',
399
		$pconfig['lifetime'],
400
		['max' => $max_lifetime]
401
	));
402

    
403
	$section->addInput(new Form_Input(
404
		'serial',
405
		'Serial',
406
		'number',
407
		$pconfig['serial'],
408
		['min' => '0', 'max' => '9999']
409
	));
410

    
411
	$form->add($section);
412

    
413
	if (isset($id) && $thiscrl) {
414
		$section->addInput(new Form_Input(
415
			'id',
416
			null,
417
			'hidden',
418
			$id
419
		));
420
	}
421

    
422
	print($form);
423

    
424
} elseif ($act == "editimported") {
425

    
426
	$form = new Form();
427

    
428
	$section = new Form_Section('Edit Imported Certificate Revocation List');
429

    
430
	$section->addInput(new Form_Input(
431
		'descr',
432
		'*Descriptive name',
433
		'text',
434
		$pconfig['descr']
435
	));
436

    
437
	$section->addInput(new Form_Textarea(
438
		'crltext',
439
		'*CRL data',
440
		$pconfig['crltext']
441
	))->setHelp('Paste a Certificate Revocation List in X.509 CRL format here.');
442

    
443
	$section->addInput(new Form_Input(
444
		'id',
445
		null,
446
		'hidden',
447
		$id
448
	));
449

    
450
	$section->addInput(new Form_Input(
451
		'act',
452
		null,
453
		'hidden',
454
		'editimported'
455
	));
456

    
457
	$form->add($section);
458

    
459
	print($form);
460

    
461
} elseif ($act == "edit") {
462
	$crl = $thiscrl;
463

    
464
	$form = new Form(false);
465
?>
466

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

    
517
	$ca_certs = array();
518
	foreach ($a_cert as $cert) {
519
		if ($cert['caref'] == $crl['caref'] && !is_cert_revoked($cert, $id)) {
520
			$ca_certs[] = $cert;
521
		}
522
	}
523

    
524
	if (count($ca_certs) == 0) {
525
		print_info_box(gettext("No certificates found for this CA."), 'danger');
526
	} else {
527
		$section = new Form_Section('Choose a Certificate to Revoke');
528
		$group = new Form_Group(null);
529

    
530
		$group->add(new Form_Select(
531
			'certref',
532
			null,
533
			$pconfig['certref'],
534
			build_cacert_list()
535
			))->setWidth(4)->setHelp('Certificate');
536

    
537
		$group->add(new Form_Select(
538
			'crlreason',
539
			null,
540
			-1,
541
			$openssl_crl_status
542
			))->setHelp('Reason');
543

    
544
		$group->add(new Form_Button(
545
			'submit',
546
			'Add',
547
			null,
548
			'fa-plus'
549
			))->addClass('btn-success btn-sm');
550

    
551
		$section->add($group);
552

    
553
		$section->addInput(new Form_Input(
554
			'id',
555
			null,
556
			'hidden',
557
			$crl['refid']
558
		));
559

    
560
		$section->addInput(new Form_Input(
561
			'act',
562
			null,
563
			'hidden',
564
			'addcert'
565
		));
566

    
567
		$section->addInput(new Form_Input(
568
			'crlref',
569
			null,
570
			'hidden',
571
			$crl['refid']
572
		));
573

    
574
		$form->add($section);
575
	}
576

    
577
	print($form);
578
} else {
579
?>
580

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

    
606
	$i = 0;
607
	foreach ($a_ca as $ca):
608
		$name = htmlspecialchars($ca['descr']);
609

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

    
687

    
688
<?php
689
}
690
?>
691

    
692
<script type="text/javascript">
693
//<![CDATA[
694
events.push(function() {
695

    
696
	// Hides all elements of the specified class. This will usually be a section or group
697
	function hideClass(s_class, hide) {
698
		if (hide) {
699
			$('.' + s_class).hide();
700
		} else {
701
			$('.' + s_class).show();
702
		}
703
	}
704

    
705
	// When the 'method" selector is changed, we show/hide certain sections
706
	$('#method').on('change', function() {
707
		hideClass('internal', ($('#method').val() == 'existing'));
708
		hideClass('existing', ($('#method').val() == 'internal'));
709
	});
710

    
711
	hideClass('internal', ($('#method').val() == 'existing'));
712
	hideClass('existing', ($('#method').val() == 'internal'));
713
});
714
//]]>
715
</script>
716

    
717
<?php include("foot.inc");
(202-202/234)