Project

General

Profile

Download (17.9 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	system_camanager.php
4
*/
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *  Copyright (c)  2008 Shrew Soft Inc.
8
 *
9
 *	Redistribution and use in source and binary forms, with or without modification,
10
 *	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
17
 *		the documentation and/or other materials provided with the
18
 *		distribution.
19
 *
20
 *	3. All advertising materials mentioning features or use of this software
21
 *		must display the following acknowledgment:
22
 *		"This product includes software developed by the pfSense Project
23
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
24
 *
25
 *	4. The names "pfSense" and "pfSense Project" must not be used to
26
 *		 endorse or promote products derived from this software without
27
 *		 prior written permission. For written permission, please contact
28
 *		 coreteam@pfsense.org.
29
 *
30
 *	5. Products derived from this software may not be called "pfSense"
31
 *		nor may "pfSense" appear in their names without prior written
32
 *		permission of the Electric Sheep Fencing, LLC.
33
 *
34
 *	6. Redistributions of any form whatsoever must retain the following
35
 *		acknowledgment:
36
 *
37
 *	"This product includes software developed by the pfSense Project
38
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
39
 *
40
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
41
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
44
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
52
 *
53
 *	====================================================================
54
 *
55
 */
56

    
57
##|+PRIV
58
##|*IDENT=page-system-camanager
59
##|*NAME=System: CA Manager
60
##|*DESCR=Allow access to the 'System: CA Manager' page.
61
##|*MATCH=system_camanager.php*
62
##|-PRIV
63

    
64
require("guiconfig.inc");
65
require_once("certs.inc");
66

    
67
$ca_methods = array(
68
	"existing" => gettext("Import an existing Certificate Authority"),
69
	"internal" => gettext("Create an internal Certificate Authority"),
70
	"intermediate" => gettext("Create an intermediate Certificate Authority"));
71

    
72
$ca_keylens = array("512", "1024", "2048", "4096");
73
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
74

    
75
if (is_numericint($_GET['id'])) {
76
	$id = $_GET['id'];
77
}
78
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
79
	$id = $_POST['id'];
80
}
81

    
82
if (!is_array($config['ca'])) {
83
	$config['ca'] = array();
84
}
85

    
86
$a_ca =& $config['ca'];
87

    
88
if (!is_array($config['cert'])) {
89
	$config['cert'] = array();
90
}
91

    
92
$a_cert =& $config['cert'];
93

    
94
if (!is_array($config['crl'])) {
95
	$config['crl'] = array();
96
}
97

    
98
$a_crl =& $config['crl'];
99

    
100
$act = $_GET['act'];
101
if ($_POST['act']) {
102
	$act = $_POST['act'];
103
}
104

    
105
if ($act == "del") {
106

    
107
	if (!isset($a_ca[$id])) {
108
		pfSenseHeader("system_camanager.php");
109
		exit;
110
	}
111

    
112
	$index = count($a_cert) - 1;
113
	for (;$index >= 0; $index--) {
114
		if ($a_cert[$index]['caref'] == $a_ca[$id]['refid']) {
115
			unset($a_cert[$index]);
116
		}
117
	}
118

    
119
	$index = count($a_crl) - 1;
120
	for (;$index >= 0; $index--) {
121
		if ($a_crl[$index]['caref'] == $a_ca[$id]['refid']) {
122
			unset($a_crl[$index]);
123
		}
124
	}
125

    
126
	$name = $a_ca[$id]['descr'];
127
	unset($a_ca[$id]);
128
	write_config();
129
	$savemsg = sprintf(gettext("Certificate Authority %s and its CRLs (if any) successfully deleted."), htmlspecialchars($name));
130
	pfSenseHeader("system_camanager.php");
131
	exit;
132
}
133

    
134
if ($act == "edit") {
135
	if (!$a_ca[$id]) {
136
		pfSenseHeader("system_camanager.php");
137
		exit;
138
	}
139
	$pconfig['descr']  = $a_ca[$id]['descr'];
140
	$pconfig['refid']  = $a_ca[$id]['refid'];
141
	$pconfig['cert']   = base64_decode($a_ca[$id]['crt']);
142
	$pconfig['serial'] = $a_ca[$id]['serial'];
143
	if (!empty($a_ca[$id]['prv'])) {
144
		$pconfig['key'] = base64_decode($a_ca[$id]['prv']);
145
	}
146
}
147

    
148
if ($act == "new") {
149
	$pconfig['method'] = $_GET['method'];
150
	$pconfig['keylen'] = "2048";
151
	$pconfig['digest_alg'] = "sha256";
152
	$pconfig['lifetime'] = "3650";
153
	$pconfig['dn_commonname'] = "internal-ca";
154
}
155

    
156
if ($act == "exp") {
157

    
158
	if (!$a_ca[$id]) {
159
		pfSenseHeader("system_camanager.php");
160
		exit;
161
	}
162

    
163
	$exp_name = urlencode("{$a_ca[$id]['descr']}.crt");
164
	$exp_data = base64_decode($a_ca[$id]['crt']);
165
	$exp_size = strlen($exp_data);
166

    
167
	header("Content-Type: application/octet-stream");
168
	header("Content-Disposition: attachment; filename={$exp_name}");
169
	header("Content-Length: $exp_size");
170
	echo $exp_data;
171
	exit;
172
}
173

    
174
if ($act == "expkey") {
175

    
176
	if (!$a_ca[$id]) {
177
		pfSenseHeader("system_camanager.php");
178
		exit;
179
	}
180

    
181
	$exp_name = urlencode("{$a_ca[$id]['descr']}.key");
182
	$exp_data = base64_decode($a_ca[$id]['prv']);
183
	$exp_size = strlen($exp_data);
184

    
185
	header("Content-Type: application/octet-stream");
186
	header("Content-Disposition: attachment; filename={$exp_name}");
187
	header("Content-Length: $exp_size");
188
	echo $exp_data;
189
	exit;
190
}
191

    
192
if ($_POST) {
193

    
194
	unset($input_errors);
195
	$input_errors = array();
196
	$pconfig = $_POST;
197

    
198
	/* input validation */
199
	if ($pconfig['method'] == "existing") {
200
		$reqdfields = explode(" ", "descr cert");
201
		$reqdfieldsn = array(
202
			gettext("Descriptive name"),
203
			gettext("Certificate data"));
204
		if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
205
			$input_errors[] = gettext("This certificate does not appear to be valid.");
206
		}
207
		if ($_POST['key'] && strstr($_POST['key'], "ENCRYPTED")) {
208
			$input_errors[] = gettext("Encrypted private keys are not yet supported.");
209
		}
210
	}
211
	if ($pconfig['method'] == "internal") {
212
		$reqdfields = explode(" ",
213
			"descr keylen lifetime dn_country dn_state dn_city ".
214
			"dn_organization dn_email dn_commonname");
215
		$reqdfieldsn = array(
216
			gettext("Descriptive name"),
217
			gettext("Key length"),
218
			gettext("Lifetime"),
219
			gettext("Distinguished name Country Code"),
220
			gettext("Distinguished name State or Province"),
221
			gettext("Distinguished name City"),
222
			gettext("Distinguished name Organization"),
223
			gettext("Distinguished name Email Address"),
224
			gettext("Distinguished name Common Name"));
225
	}
226
	if ($pconfig['method'] == "intermediate") {
227
		$reqdfields = explode(" ",
228
			"descr caref keylen lifetime dn_country dn_state dn_city ".
229
			"dn_organization dn_email dn_commonname");
230
		$reqdfieldsn = array(
231
			gettext("Descriptive name"),
232
			gettext("Signing Certificate Authority"),
233
			gettext("Key length"),
234
			gettext("Lifetime"),
235
			gettext("Distinguished name Country Code"),
236
			gettext("Distinguished name State or Province"),
237
			gettext("Distinguished name City"),
238
			gettext("Distinguished name Organization"),
239
			gettext("Distinguished name Email Address"),
240
			gettext("Distinguished name Common Name"));
241
	}
242

    
243
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
244
	if ($pconfig['method'] != "existing") {
245
		/* Make sure we do not have invalid characters in the fields for the certificate */
246
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
247
			array_push($input_errors, gettext("The field 'Descriptive Name' contains invalid characters."));
248
		}
249

    
250
		for ($i = 0; $i < count($reqdfields); $i++) {
251
			if ($reqdfields[$i] == 'dn_email') {
252
				if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_email"])) {
253
					array_push($input_errors, gettext("The field 'Distinguished name Email Address' contains invalid characters."));
254
				}
255
			} else if ($reqdfields[$i] == 'dn_commonname') {
256
				if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_commonname"])) {
257
					array_push($input_errors, gettext("The field 'Distinguished name Common Name' contains invalid characters."));
258
				}
259
			} else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST["$reqdfields[$i]"])) {
260
				array_push($input_errors, sprintf(gettext("The field '%s' contains invalid characters."), $reqdfieldsn[$i]));
261
			}
262
		}
263
		if (!in_array($_POST["keylen"], $ca_keylens)) {
264
			array_push($input_errors, gettext("Please select a valid Key Length."));
265
		}
266
		if (!in_array($_POST["digest_alg"], $openssl_digest_algs)) {
267
			array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
268
		}
269
	}
270

    
271
	/* if this is an AJAX caller then handle via JSON */
272
	if (isAjax() && is_array($input_errors)) {
273
		input_errors2Ajax($input_errors);
274
		exit;
275
	}
276

    
277
	/* save modifications */
278
	if (!$input_errors) {
279
		$ca = array();
280
		if (!isset($pconfig['refid']) || empty($pconfig['refid'])) {
281
			$ca['refid'] = uniqid();
282
		} else {
283
			$ca['refid'] = $pconfig['refid'];
284
		}
285

    
286
		if (isset($id) && $a_ca[$id]) {
287
			$ca = $a_ca[$id];
288
		}
289

    
290
		$ca['descr'] = $pconfig['descr'];
291

    
292
		if ($act == "edit") {
293
			$ca['descr']  = $pconfig['descr'];
294
			$ca['refid']  = $pconfig['refid'];
295
			$ca['serial'] = $pconfig['serial'];
296
			$ca['crt']	  = base64_encode($pconfig['cert']);
297
			if (!empty($pconfig['key'])) {
298
				$ca['prv']	  = base64_encode($pconfig['key']);
299
			}
300
		} else {
301
			$old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */
302
			if ($pconfig['method'] == "existing") {
303
				ca_import($ca, $pconfig['cert'], $pconfig['key'], $pconfig['serial']);
304
			} else if ($pconfig['method'] == "internal") {
305
				$dn = array(
306
					'countryName' => $pconfig['dn_country'],
307
					'stateOrProvinceName' => $pconfig['dn_state'],
308
					'localityName' => $pconfig['dn_city'],
309
					'organizationName' => $pconfig['dn_organization'],
310
					'emailAddress' => $pconfig['dn_email'],
311
					'commonName' => $pconfig['dn_commonname']);
312
				if (!ca_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['digest_alg'])) {
313
					while ($ssl_err = openssl_error_string()) {
314
						$input_errors = array();
315
						array_push($input_errors, "openssl library returns: " . $ssl_err);
316
					}
317
				}
318
			} else if ($pconfig['method'] == "intermediate") {
319
				$dn = array(
320
					'countryName' => $pconfig['dn_country'],
321
					'stateOrProvinceName' => $pconfig['dn_state'],
322
					'localityName' => $pconfig['dn_city'],
323
					'organizationName' => $pconfig['dn_organization'],
324
					'emailAddress' => $pconfig['dn_email'],
325
					'commonName' => $pconfig['dn_commonname']);
326

    
327
				if (!ca_inter_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['caref'], $pconfig['digest_alg'])) {
328
					while ($ssl_err = openssl_error_string()) {
329
						$input_errors = array();
330
						array_push($input_errors, "openssl library returns: " . $ssl_err);
331
					}
332
				}
333
			}
334
			error_reporting($old_err_level);
335
		}
336

    
337
		if (isset($id) && $a_ca[$id]) {
338
			$a_ca[$id] = $ca;
339
		} else {
340
			$a_ca[] = $ca;
341
		}
342

    
343
		if (!$input_errors) {
344
			write_config();
345
		}
346

    
347
		pfSenseHeader("system_camanager.php");
348
	}
349
}
350

    
351
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("CAs"));
352

    
353
if ($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors) {
354
	$pgtitle[] = gettext('Edit');
355
}
356
include("head.inc");
357

    
358
if ($input_errors) {
359
	print_input_errors($input_errors);
360
}
361

    
362
if ($savemsg) {
363
	print_info_box($savemsg, 'success');
364
}
365

    
366
// Load valid country codes
367
$dn_cc = array();
368
if (file_exists("/etc/ca_countries")) {
369
	$dn_cc_file=file("/etc/ca_countries");
370
	foreach ($dn_cc_file as $line) {
371
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
372
			$dn_cc[$matches[1]] = $matches[1];
373
		}
374
	}
375
}
376

    
377
$tab_array = array();
378
$tab_array[] = array(gettext("CAs"), true, "system_camanager.php");
379
$tab_array[] = array(gettext("Certificates"), false, "system_certmanager.php");
380
$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php");
381
display_top_tabs($tab_array);
382

    
383
if (!($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors)) {
384
?>
385
<div class="panel panel-default">
386
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificate Authorities')?></h2></div>
387
	<div class="panel-body">
388
		<div class="table-responsive">
389
		<table class="table table-striped table-hover">
390
			<thead>
391
				<tr>
392
					<th><?=gettext("Name")?></th>
393
					<th><?=gettext("Internal")?></th>
394
					<th><?=gettext("Issuer")?></th>
395
					<th><?=gettext("Certificates")?></th>
396
					<th><?=gettext("Distinguished Name")?></th>
397
					<th><?=gettext("Actions")?></th>
398
				</tr>
399
			</thead>
400
			<tbody>
401
<?php
402
foreach ($a_ca as $i => $ca):
403
	$name = htmlspecialchars($ca['descr']);
404
	$subj = cert_get_subject($ca['crt']);
405
	$issuer = cert_get_issuer($ca['crt']);
406
	list($startdate, $enddate) = cert_get_dates($ca['crt']);
407
	if ($subj == $issuer) {
408
		$issuer_name = gettext("self-signed");
409
	} else {
410
		$issuer_name = gettext("external");
411
	}
412
	$subj = htmlspecialchars($subj);
413
	$issuer = htmlspecialchars($issuer);
414
	$certcount = 0;
415

    
416
	$issuer_ca = lookup_ca($ca['caref']);
417
	if ($issuer_ca) {
418
		$issuer_name = $issuer_ca['descr'];
419
	}
420

    
421
	foreach ($a_cert as $cert) {
422
		if ($cert['caref'] == $ca['refid']) {
423
			$certcount++;
424
		}
425
	}
426

    
427
	foreach ($a_ca as $cert) {
428
		if ($cert['caref'] == $ca['refid']) {
429
			$certcount++;
430
		}
431
	}
432
?>
433
				<tr>
434
					<td><?=$name?></td>
435
					<td><i class="fa fa-<?= (!empty($ca['prv'])) ? "check" : "times" ; ?>"></i></td>
436
					<td><i><?=$issuer_name?></i></td>
437
					<td><?=$certcount?></td>
438
					<td>
439
						<?=$subj?>
440
						<br />
441
						<small>
442
							<?=gettext("Valid From")?>: <b><?=$startdate ?></b><br /><?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
443
						</small>
444
					</td>
445
					<td>
446
						<a class="fa fa-pencil"	title="<?=gettext("Edit CA")?>"	href="system_camanager.php?act=edit&amp;id=<?=$i?>"></a>
447
						<a class="fa fa-certificate"	title="<?=gettext("Export CA")?>"	href="system_camanager.php?act=exp&amp;id=<?=$i?>"></a>
448
					<?php if ($ca['prv']): ?>
449
						<a class="fa fa-key"	title="<?=gettext("Export key")?>"	href="system_camanager.php?act=expkey&amp;id=<?=$i?>"></a>
450
					<?php endif?>
451
						<a class="fa fa-trash" 	title="<?=gettext("Delete CA")?>"	href="system_camanager.php?act=del&amp;id=<?=$i?>"></a>
452
					</td>
453
				</tr>
454
<?php endforeach; ?>
455
			</tbody>
456
		</table>
457
		</div>
458
	</div>
459
</div>
460

    
461
<nav class="action-buttons">
462
	<a href="?act=new" class="btn btn-success btn-sm">
463
		<i class="fa fa-plus icon-embed-btn"></i>
464
		<?=gettext("Add")?>
465
	</a>
466
</nav>
467
<?php
468
	include("foot.inc");
469
	exit;
470
}
471

    
472
$form = new Form;
473
//$form->setAction('system_camanager.php?act=edit');
474
if (isset($id) && $a_ca[$id]) {
475
	$form->addGlobal(new Form_Input(
476
		'id',
477
		null,
478
		'hidden',
479
		$id
480
	));
481
}
482

    
483
if ($act == "edit") {
484
	$form->addGlobal(new Form_Input(
485
		'refid',
486
		null,
487
		'hidden',
488
		$pconfig['refid']
489
	));
490
}
491

    
492
$section = new Form_Section('Create / Edit CA');
493

    
494
$section->addInput(new Form_Input(
495
	'descr',
496
	'Descriptive name',
497
	'text',
498
	$pconfig['descr']
499
));
500

    
501
if (!isset($id) || $act == "edit") {
502
	$section->addInput(new Form_Select(
503
		'method',
504
		'Method',
505
		$pconfig['method'],
506
		$ca_methods
507
	))->toggles();
508
}
509

    
510
$form->add($section);
511

    
512
$section = new Form_Section('Existing Certificate Authority');
513
$section->addClass('toggle-existing collapse');
514

    
515
$section->addInput(new Form_Textarea(
516
	'cert',
517
	'Certificate data',
518
	$pconfig['cert']
519
))->setHelp('Paste a certificate in X.509 PEM format here.');
520

    
521
$section->addInput(new Form_Textarea(
522
	'key',
523
	'Certificate Private Key (optional)',
524
	$pconfig['key']
525
))->setHelp('Paste the private key for the above certificate here. This is '.
526
	'optional in most cases, but required if you need to generate a '.
527
	'Certificate Revocation List (CRL).');
528

    
529
$section->addInput(new Form_Input(
530
	'serial',
531
	'Serial for next certificate',
532
	'number',
533
	$pconfig['serial']
534
))->setHelp('Enter a decimal number to be used as the serial number for the next '.
535
	'certificate to be created using this CA.');
536

    
537
$form->add($section);
538

    
539
$section = new Form_Section('Internal Certificate Authority');
540
$section->addClass('toggle-internal', 'toggle-intermediate', 'collapse');
541

    
542
$allCas = array();
543
foreach ($a_ca as $ca) {
544
	if (!$ca['prv']) {
545
			continue;
546
	}
547

    
548
	$allCas[ $ca['refid'] ] = $ca['descr'];
549
}
550

    
551
$group = new Form_Group('Signing Certificate Authority');
552
$group->addClass('toggle-intermediate', 'collapse');
553
$group->add(new Form_Select(
554
	'caref',
555
	null,
556
	$pconfig['caref'],
557
	$allCas
558
));
559
$section->add($group);
560

    
561
$section->addInput(new Form_Select(
562
	'keylen',
563
	'Key length (bits)',
564
	$pconfig['keylen'],
565
	array_combine($ca_keylens, $ca_keylens)
566
));
567

    
568
$section->addInput(new Form_Select(
569
	'digest_alg',
570
	'Digest Algorithm',
571
	$pconfig['digest_alg'],
572
	array_combine($openssl_digest_algs, $openssl_digest_algs)
573
))->setHelp('NOTE: It is recommended to use an algorithm stronger than SHA1 '.
574
	'when possible.');
575

    
576
$section->addInput(new Form_Input(
577
	'lifetime',
578
	'Lifetime (days)',
579
	'number',
580
	$pconfig['lifetime']
581
));
582

    
583
$section->addInput(new Form_Select(
584
	'dn_country',
585
	'Country Code',
586
	$pconfig['dn_country'],
587
	$dn_cc
588
));
589

    
590
$section->addInput(new Form_Input(
591
	'dn_state',
592
	'State or Province',
593
	'text',
594
	$pconfig['dn_state'],
595
	['placeholder' => 'e.g. Texas']
596
));
597

    
598
$section->addInput(new Form_Input(
599
	'dn_city',
600
	'City',
601
	'text',
602
	$pconfig['dn_city'],
603
	['placeholder' => 'e.g. Austin']
604
));
605

    
606
$section->addInput(new Form_Input(
607
	'dn_organization',
608
	'Organization',
609
	'text',
610
	$pconfig['dn_organization'],
611
	['placeholder' => 'e.g. My Company Inc.']
612
));
613

    
614
$section->addInput(new Form_Input(
615
	'dn_email',
616
	'Email Address',
617
	'email',
618
	$pconfig['dn_email'],
619
	['placeholder' => 'e.g. admin@mycompany.com']
620
));
621

    
622
$section->addInput(new Form_Input(
623
	'dn_commonname',
624
	'Common Name',
625
	'text',
626
	$pconfig['dn_commonname'],
627
	['placeholder' => 'e.g. internal-ca']
628
));
629

    
630
$form->add($section);
631

    
632
print $form;
633

    
634
$internal_ca_count = 0;
635
foreach ($a_ca as $ca) {
636
	if ($ca['prv']) {
637
		$internal_ca_count++;
638
	}
639
}
640

    
641
include('foot.inc');
642
?>
(193-193/227)