Project

General

Profile

Download (17.7 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
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("CAs"));
76

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

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

    
88
$a_ca =& $config['ca'];
89

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

    
94
$a_cert =& $config['cert'];
95

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

    
100
$a_crl =& $config['crl'];
101

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

    
107
if ($act == "del") {
108

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

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

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

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

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

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

    
158
if ($act == "exp") {
159

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

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

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

    
176
if ($act == "expkey") {
177

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

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

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

    
194
if ($_POST) {
195

    
196
	unset($input_errors);
197
	$input_errors = array();
198
	$pconfig = $_POST;
199

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

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

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

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

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

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

    
292
		$ca['descr'] = $pconfig['descr'];
293

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

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

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

    
345
		if (!$input_errors) {
346
			write_config();
347
		}
348

    
349
		pfSenseHeader("system_camanager.php");
350
	}
351
}
352

    
353
include("head.inc");
354

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

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

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

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

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

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

    
418
	// TODO : Need gray certificate icon
419
	$internal = (!!$ca['prv']);
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><?=$internal?></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")?>"	href="system_camanager.php?act=edit&amp;id=<?=$i?>"></a>
447
						<a class="fa fa-sign-in"	title="<?=gettext("Export")?>"	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")?>"	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
?>
(195-195/229)