Project

General

Profile

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

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

    
62
require_once("guiconfig.inc");
63
require_once("certs.inc");
64

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

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

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

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

    
84
$a_ca =& $config['ca'];
85

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

    
90
$a_cert =& $config['cert'];
91

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

    
96
$a_crl =& $config['crl'];
97

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

    
103
if ($act == "del") {
104

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

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

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

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

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

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

    
154
if ($act == "exp") {
155

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

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

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

    
172
if ($act == "expkey") {
173

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

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

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

    
190
if ($_POST) {
191

    
192
	unset($input_errors);
193
	$input_errors = array();
194
	$pconfig = $_POST;
195

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

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

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

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

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

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

    
288
		$ca['descr'] = $pconfig['descr'];
289

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

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

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

    
341
		if (!$input_errors) {
342
			write_config();
343
		}
344

    
345
		pfSenseHeader("system_camanager.php");
346
	}
347
}
348

    
349
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("CAs"));
350

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

    
356
if ($input_errors) {
357
	print_input_errors($input_errors);
358
}
359

    
360
if ($savemsg) {
361
	print_info_box($savemsg, 'success');
362
}
363

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

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

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

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

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

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

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

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

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

    
490
$section = new Form_Section('Create / Edit CA');
491

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

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

    
508
$form->add($section);
509

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

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

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

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

    
535
$form->add($section);
536

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

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

    
546
	$allCas[ $ca['refid'] ] = $ca['descr'];
547
}
548

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

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

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

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

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

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

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

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

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

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

    
628
$form->add($section);
629

    
630
print $form;
631

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

    
639
include('foot.inc');
640
?>
(192-192/225)