Project

General

Profile

Download (15.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	system_camanager.php
4

    
5
	Copyright (C) 2008 Shrew Soft Inc.
6
	Copyright (C) 2013-2015 Electric Sheep Fencing, LP
7
	All rights reserved.
8

    
9
	Redistribution and use in source and binary forms, with or without
10
	modification, 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 the
17
	   documentation and/or other materials provided with the distribution.
18

    
19
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
	POSSIBILITY OF SUCH DAMAGE.
29
*/
30
/*
31
	pfSense_MODULE:	certificate_manager
32
*/
33

    
34
##|+PRIV
35
##|*IDENT=page-system-camanager
36
##|*NAME=System: CA Manager
37
##|*DESCR=Allow access to the 'System: CA Manager' page.
38
##|*MATCH=system_camanager.php*
39
##|-PRIV
40

    
41
require("guiconfig.inc");
42
require_once("certs.inc");
43

    
44
$ca_methods = array(
45
	"existing" => gettext("Import an existing Certificate Authority"),
46
	"internal" => gettext("Create an internal Certificate Authority"),
47
	"intermediate" => gettext("Create an intermediate Certificate Authority"));
48

    
49
$ca_keylens = array( "512", "1024", "2048", "4096");
50
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
51

    
52
$pgtitle = array(gettext("System"), gettext("Certificate Authority Manager"));
53

    
54
if (is_numericint($_GET['id']))
55
	$id = $_GET['id'];
56
if (isset($_POST['id']) && is_numericint($_POST['id']))
57
	$id = $_POST['id'];
58

    
59
if (!is_array($config['ca']))
60
	$config['ca'] = array();
61

    
62
$a_ca =& $config['ca'];
63

    
64
if (!is_array($config['cert']))
65
	$config['cert'] = array();
66

    
67
$a_cert =& $config['cert'];
68

    
69
if (!is_array($config['crl']))
70
	$config['crl'] = array();
71

    
72
$a_crl =& $config['crl'];
73

    
74
$act = $_GET['act'];
75
if ($_POST['act'])
76
	$act = $_POST['act'];
77

    
78
if ($act == "del") {
79

    
80
	if (!isset($a_ca[$id])) {
81
		pfSenseHeader("system_camanager.php");
82
		exit;
83
	}
84

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

    
90
	$index = count($a_crl) - 1;
91
	for (;$index >=0; $index--)
92
		if ($a_crl[$index]['caref'] == $a_ca[$id]['refid'])
93
			unset($a_crl[$index]);
94

    
95
	$name = $a_ca[$id]['descr'];
96
	unset($a_ca[$id]);
97
	write_config();
98
	$savemsg = sprintf(gettext("Certificate Authority %s and its CRLs (if any) successfully deleted"), $name) . "<br />";
99
	pfSenseHeader("system_camanager.php");
100
	exit;
101
}
102

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

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

    
124
if ($act == "exp") {
125

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

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

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

    
142
if ($act == "expkey") {
143

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

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

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

    
160
if ($_POST) {
161

    
162
	unset($input_errors);
163
	$input_errors = array();
164
	$pconfig = $_POST;
165

    
166
	/* input validation */
167
	if ($pconfig['method'] == "existing") {
168
		$reqdfields = explode(" ", "descr cert");
169
		$reqdfieldsn = array(
170
				gettext("Descriptive name"),
171
				gettext("Certificate data"));
172
		if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE")))
173
			$input_errors[] = gettext("This certificate does not appear to be valid.");
174
		if ($_POST['key'] && strstr($_POST['key'], "ENCRYPTED"))
175
			$input_errors[] = gettext("Encrypted private keys are not yet supported.");
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
		for ($i = 0; $i < count($reqdfields); $i++) {
213
			if ($reqdfields[$i] == 'dn_email'){
214
				if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_email"]))
215
					array_push($input_errors, "The field 'Distinguished name Email Address' contains invalid characters.");
216
			}else if ($reqdfields[$i] == 'dn_commonname'){
217
				if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_commonname"]))
218
					array_push($input_errors, "The field 'Distinguished name Common Name' contains invalid characters.");
219
			}else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST["$reqdfields[$i]"]))
220
				array_push($input_errors, "The field '" . $reqdfieldsn[$i] . "' contains invalid characters.");
221
		}
222
		if (!in_array($_POST["keylen"], $ca_keylens))
223
			array_push($input_errors, gettext("Please select a valid Key Length."));
224
		if (!in_array($_POST["digest_alg"], $openssl_digest_algs))
225
			array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
226
	}
227

    
228
	/* if this is an AJAX caller then handle via JSON */
229
	if (isAjax() && is_array($input_errors)) {
230
		input_errors2Ajax($input_errors);
231
		exit;
232
	}
233

    
234
	/* save modifications */
235
	if (!$input_errors) {
236

    
237
		$ca = array();
238
		if (!isset($pconfig['refid']) || empty($pconfig['refid']))
239
			$ca['refid'] = uniqid();
240
		else
241
			$ca['refid'] = $pconfig['refid'];
242

    
243
		if (isset($id) && $a_ca[$id])
244
			$ca = $a_ca[$id];
245

    
246
		$ca['descr'] = $pconfig['descr'];
247

    
248
		if ($act == "edit") {
249
			$ca['descr']  = $pconfig['descr'];
250
			$ca['refid']  = $pconfig['refid'];
251
			$ca['serial'] = $pconfig['serial'];
252
			$ca['crt'] = base64_encode($pconfig['cert']);
253
			if (!empty($pconfig['key']))
254
				$ca['prv'] = base64_encode($pconfig['key']);
255
		} else {
256
			$old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */
257
			if ($pconfig['method'] == "existing")
258
				ca_import($ca, $pconfig['cert'], $pconfig['key'], $pconfig['serial']);
259

    
260
			else if ($pconfig['method'] == "internal") {
261
				$dn = array(
262
					'countryName' => $pconfig['dn_country'],
263
					'stateOrProvinceName' => $pconfig['dn_state'],
264
					'localityName' => $pconfig['dn_city'],
265
					'organizationName' => $pconfig['dn_organization'],
266
					'emailAddress' => $pconfig['dn_email'],
267
					'commonName' => $pconfig['dn_commonname']);
268
				if (!ca_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['digest_alg'])){
269
					while($ssl_err = openssl_error_string()){
270
						$input_errors = array();
271
						array_push($input_errors, "openssl library returns: " . $ssl_err);
272
					}
273
				}
274
			}
275
			else if ($pconfig['method'] == "intermediate") {
276
				$dn = array(
277
					'countryName' => $pconfig['dn_country'],
278
					'stateOrProvinceName' => $pconfig['dn_state'],
279
					'localityName' => $pconfig['dn_city'],
280
					'organizationName' => $pconfig['dn_organization'],
281
					'emailAddress' => $pconfig['dn_email'],
282
					'commonName' => $pconfig['dn_commonname']);
283
				if (!ca_inter_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['caref'], $pconfig['digest_alg'])){
284
					while($ssl_err = openssl_error_string()){
285
						$input_errors = array();
286
						array_push($input_errors, "openssl library returns: " . $ssl_err);
287
					}
288
				}
289
			}
290
			error_reporting($old_err_level);
291
		}
292

    
293
		if (isset($id) && $a_ca[$id])
294
			$a_ca[$id] = $ca;
295
		else
296
			$a_ca[] = $ca;
297

    
298
		if (!$input_errors)
299
			write_config();
300

    
301
//		pfSenseHeader("system_camanager.php");
302
	}
303
}
304

    
305
include("head.inc");
306

    
307
if ($input_errors)
308
	print_input_errors($input_errors);
309
if ($savemsg)
310
	print_info_box($savemsg);
311

    
312
// Load valid country codes
313
$dn_cc = array();
314
if (file_exists("/etc/ca_countries")){
315
	$dn_cc_file=file("/etc/ca_countries");
316
	foreach($dn_cc_file as $line)
317
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches))
318
			array_push($dn_cc, $matches[1]);
319
}
320

    
321
$tab_array = array();
322
$tab_array[] = array(gettext("CAs"), true, "system_camanager.php");
323
$tab_array[] = array(gettext("Certificates"), false, "system_certmanager.php");
324
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
325
display_top_tabs($tab_array);
326

    
327
if (!($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors))
328
{
329
?>
330
<div class="table-responsive">
331
<table class="table table-striped table-hover">
332
	<thead>
333
		<tr>
334
			<th><?=gettext("Name")?></th>
335
			<th><?=gettext("Internal")?></th>
336
			<th><?=gettext("Issuer")?></th>
337
			<th><?=gettext("Certificates")?></th>
338
			<th><?=gettext("Distinguished Name")?></th>
339
			<th></th>
340
		</tr>
341
	</thead>
342
	<tbody>
343
<?php
344
foreach ($a_ca as $i => $ca):
345
	$name = htmlspecialchars($ca['descr']);
346
	$subj = cert_get_subject($ca['crt']);
347
	$issuer = cert_get_issuer($ca['crt']);
348
	list($startdate, $enddate) = cert_get_dates($ca['crt']);
349
	if ($subj == $issuer)
350
		$issuer_name = gettext("self-signed");
351
	else
352
		$issuer_name = gettext("external");
353
	$subj = htmlspecialchars($subj);
354
	$issuer = htmlspecialchars($issuer);
355
	$certcount = 0;
356

    
357
	$issuer_ca = lookup_ca($ca['caref']);
358
	if ($issuer_ca)
359
		$issuer_name = $issuer_ca['descr'];
360

    
361
	// TODO : Need gray certificate icon
362
	$internal = (!!$ca['prv']);
363

    
364
	foreach ($a_cert as $cert)
365
		if ($cert['caref'] == $ca['refid'])
366
			$certcount++;
367

    
368
	foreach ($a_ca as $cert)
369
		if ($cert['caref'] == $ca['refid'])
370
			$certcount++;
371
?>
372
		<tr>
373
			<td><?=$name?></td>
374
			<td><?=$internal?></td>
375
			<td><i><?=$issuer_name?></i></td>
376
			<td><?=$certcount?></td>
377
			<td>
378
				<?=$subj?>
379
				<br />
380
				<small>
381
					<?=gettext("Valid From")?>: <b><?=$startdate ?></b>, <?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
382
				</small>
383
			</td>
384
			<td>
385
				<a href="system_camanager.php?act=edit&amp;id=<?=$i?>" class="btn btn-xs btn-primary">
386
					<?=gettext("edit")?>
387
				</a>
388
				<a href="system_camanager.php?act=exp&amp;id=<?=$i?>" class="btn btn-xs btn-default">
389
					<?=gettext("export cert")?>
390
				</a>
391
				<?php if ($ca['prv']): ?>
392
					<a href="system_camanager.php?act=expkey&amp;id=<?=$i?>" class="btn btn-xs btn-default">
393
						<?=gettext("export private key")?>
394
					</a>
395
				<?php endif?>
396
				<a href="system_camanager.php?act=del&amp;id=<?=$i?>" class="btn btn-xs btn-danger">
397
					<?=gettext("delete")?>
398
				</a>
399
			</td>
400
		</tr>
401
<?php endforeach; ?>
402
	</tbody>
403
</table>
404

    
405
<nav class="action-buttons">
406
	<a href="?act=new" class="btn btn-success">add new</a>
407
</nav>
408
<?
409
	include("foot.inc");
410
	exit;
411
}
412

    
413
require('classes/Form.class.php');
414
$form = new Form;
415
$form->setAction('system_camanager.php?act=edit');
416
if (isset($id) && $a_ca[$id])
417
{
418
	$form->addGlobal(new Form_Input(
419
		'id',
420
		null,
421
		'hidden',
422
		$id
423
	));
424
}
425

    
426
if ($act == "edit")
427
{
428
	$form->addGlobal(new Form_Input(
429
		'refid',
430
		null,
431
		'hidden',
432
		$pconfig['refid']
433
	));
434
}
435

    
436
$section = new Form_Section('Create / edit CA');
437

    
438
$section->addInput(new Form_Input(
439
	'descr',
440
	'Descriptive name',
441
	'text',
442
	$pconfig['descr']
443
));
444

    
445
if (!isset($id) || $act == "edit")
446
{
447
	$section->addInput(new Form_Select(
448
		'method',
449
		'Method',
450
		$pconfig['method'],
451
		$ca_methods
452
	))->toggles();
453
}
454

    
455
$form->add($section);
456

    
457
$section = new Form_Section('Existing Certificate Authority');
458
$section->addClass('toggle-existing collapse');
459

    
460
$section->addInput(new Form_Textarea(
461
	'cert',
462
	'Certificate data',
463
	$pconfig['cert']
464
))->setHelp('Paste a certificate in X.509 PEM format here.');
465

    
466
$section->addInput(new Form_Textarea(
467
	'key',
468
	'Certificate Private Key (optional)',
469
	$pconfig['key']
470
))->setHelp('Paste the private key for the above certificate here. This is '.
471
	'optional in most cases, but required if you need to generate a '.
472
	'Certificate Revocation List (CRL).');
473

    
474
$section->addInput(new Form_Input(
475
	'serial',
476
	'Serial for next certificate',
477
	'number',
478
	$pconfig['serial']
479
))->setHelp('Enter a decimal number to be used as the serial number for the next '.
480
	'certificate to be created using this CA.');
481

    
482
$form->add($section);
483

    
484
$section = new Form_Section('Internal Certificate Authority');
485
$section->addClass('toggle-internal', 'toggle-intermediate', 'collapse');
486

    
487
$allCas = array();
488
foreach ($a_ca as $ca)
489
{
490
	if (!$ca['prv'])
491
			continue;
492

    
493
	$allCas[ $ca['refid'] ] = $ca['descr'];
494
}
495

    
496
$group = new Form_Group('Signing Certificate Authority');
497
$group->addClass('toggle-intermediate');
498
$group->add(new Form_Select(
499
	'caref',
500
	null,
501
	$pconfig['caref'],
502
	$allCas
503
));
504
$section->add($group);
505

    
506
$section->addInput(new Form_Select(
507
	'keylen',
508
	'Key length (bits)',
509
	$pconfig['keylen'],
510
	$ca_keylens
511
));
512

    
513
$section->addInput(new Form_Select(
514
	'digest_alg',
515
	'Digest Algorithm',
516
	$pconfig['digest_alg'],
517
	$openssl_digest_algs
518
))->setHelp('NOTE: It is recommended to use an algorithm stronger than SHA1 '.
519
	'when possible.');
520

    
521
$section->addInput(new Form_Input(
522
	'lifetime',
523
	'Lifetime (days)',
524
	'number',
525
	$pconfig['lifetime']
526
));
527

    
528
$section->addInput(new Form_Select(
529
	'dn_country',
530
	'Country Code',
531
	$pconfig['dn_country'],
532
	$dn_cc
533
));
534

    
535
$section->addInput(new Form_Input(
536
	'dn_state',
537
	'State or Province',
538
	'text',
539
	$pconfig['dn_state'],
540
	['placeholder' => 'e.g. Texas']
541
));
542

    
543
$section->addInput(new Form_Input(
544
	'dn_city',
545
	'City',
546
	'text',
547
	$pconfig['dn_city'],
548
	['placeholder' => 'e.g. Austin']
549
));
550

    
551
$section->addInput(new Form_Input(
552
	'dn_organization',
553
	'Organization',
554
	'text',
555
	$pconfig['dn_organization'],
556
	['placeholder' => 'e.g. My Company Inc.']
557
));
558

    
559
$section->addInput(new Form_Input(
560
	'dn_email',
561
	'Email Address',
562
	'email',
563
	$pconfig['dn_email'],
564
	['placeholder' => 'e.g. admin@mycompany.com']
565
));
566

    
567
$section->addInput(new Form_Input(
568
	'dn_commonname',
569
	'Common Name',
570
	'text',
571
	$pconfig['dn_commonname'],
572
	['placeholder' => 'e.g. internal-ca']
573
));
574

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

    
577
print $form;
578

    
579
include('foot.inc');
(198-198/241)