Project

General

Profile

Download (17.9 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 Rubicon Communications, LLC (Netgate)
7
 * Copyright (c) 2008 Shrew Soft Inc
8
 * All rights reserved.
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 * http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22

    
23
##|+PRIV
24
##|*IDENT=page-system-camanager
25
##|*NAME=System: CA Manager
26
##|*DESCR=Allow access to the 'System: CA Manager' page.
27
##|*MATCH=system_camanager.php*
28
##|-PRIV
29

    
30
require_once("guiconfig.inc");
31
require_once("certs.inc");
32
require_once("pfsense-utils.inc");
33

    
34
$ca_methods = array(
35
	"existing" => gettext("Import an existing Certificate Authority"),
36
	"internal" => gettext("Create an internal Certificate Authority"),
37
	"intermediate" => gettext("Create an intermediate Certificate Authority"));
38

    
39
$ca_keylens = array("512", "1024", "2048", "3072", "4096", "7680", "8192", "15360", "16384");
40
global $openssl_digest_algs;
41

    
42
if (isset($_REQUEST['id']) && is_numericint($_REQUEST['id'])) {
43
	$id = $_REQUEST['id'];
44
}
45

    
46
if (!is_array($config['ca'])) {
47
	$config['ca'] = array();
48
}
49

    
50
$a_ca =& $config['ca'];
51

    
52
if (!is_array($config['cert'])) {
53
	$config['cert'] = array();
54
}
55

    
56
$a_cert =& $config['cert'];
57

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

    
62
$a_crl =& $config['crl'];
63

    
64
if ($_REQUEST['act']) {
65
	$act = $_REQUEST['act'];
66
}
67

    
68
if ($_POST['act'] == "del") {
69

    
70
	if (!isset($a_ca[$id])) {
71
		pfSenseHeader("system_camanager.php");
72
		exit;
73
	}
74

    
75
	/* Only remove CA reference when deleting. It can be reconnected if a new matching CA is imported */
76
	$index = count($a_cert) - 1;
77
	for (;$index >= 0; $index--) {
78
		if ($a_cert[$index]['caref'] == $a_ca[$id]['refid']) {
79
			unset($a_cert[$index]['caref']);
80
		}
81
	}
82

    
83
	/* Remove any CRLs for this CA, there is no way to recover the connection once the CA has been removed. */
84
	$index = count($a_crl) - 1;
85
	for (;$index >= 0; $index--) {
86
		if ($a_crl[$index]['caref'] == $a_ca[$id]['refid']) {
87
			unset($a_crl[$index]);
88
		}
89
	}
90

    
91
	$name = $a_ca[$id]['descr'];
92
	unset($a_ca[$id]);
93
	write_config();
94
	$savemsg = sprintf(gettext("Certificate Authority %s and its CRLs (if any) successfully deleted."), htmlspecialchars($name));
95
	pfSenseHeader("system_camanager.php");
96
	exit;
97
}
98

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

    
113
if ($act == "new") {
114
	$pconfig['method'] = $_POST['method'];
115
	$pconfig['keylen'] = "2048";
116
	$pconfig['digest_alg'] = "sha256";
117
	$pconfig['lifetime'] = "3650";
118
	$pconfig['dn_commonname'] = "internal-ca";
119
}
120

    
121
if ($act == "exp") {
122

    
123
	if (!$a_ca[$id]) {
124
		pfSenseHeader("system_camanager.php");
125
		exit;
126
	}
127

    
128
	$exp_name = urlencode("{$a_ca[$id]['descr']}.crt");
129
	$exp_data = base64_decode($a_ca[$id]['crt']);
130
	$exp_size = strlen($exp_data);
131

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

    
139
if ($act == "expkey") {
140

    
141
	if (!$a_ca[$id]) {
142
		pfSenseHeader("system_camanager.php");
143
		exit;
144
	}
145

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

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

    
157
if ($_POST['save']) {
158

    
159
	unset($input_errors);
160
	$input_errors = array();
161
	$pconfig = $_POST;
162

    
163
	/* input validation */
164
	if ($pconfig['method'] == "existing") {
165
		$reqdfields = explode(" ", "descr cert");
166
		$reqdfieldsn = array(
167
			gettext("Descriptive name"),
168
			gettext("Certificate data"));
169
		if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) {
170
			$input_errors[] = gettext("This certificate does not appear to be valid.");
171
		}
172
		if ($_POST['key'] && strstr($_POST['key'], "ENCRYPTED")) {
173
			$input_errors[] = gettext("Encrypted private keys are not yet supported.");
174
		}
175
		if (!$input_errors && !empty($_POST['key']) && cert_get_publickey($_POST['cert'], false) != cert_get_publickey($_POST['key'], false, 'prv')) {
176
			$input_errors[] = gettext("The submitted private key does not match the submitted certificate data.");
177
		}
178
	}
179
	if ($pconfig['method'] == "internal") {
180
		$reqdfields = explode(" ",
181
			"descr keylen lifetime dn_country dn_state dn_city ".
182
			"dn_organization dn_email dn_commonname");
183
		$reqdfieldsn = array(
184
			gettext("Descriptive name"),
185
			gettext("Key length"),
186
			gettext("Lifetime"),
187
			gettext("Distinguished name Country Code"),
188
			gettext("Distinguished name State or Province"),
189
			gettext("Distinguished name City"),
190
			gettext("Distinguished name Organization"),
191
			gettext("Distinguished name Email Address"),
192
			gettext("Distinguished name Common Name"));
193
	}
194
	if ($pconfig['method'] == "intermediate") {
195
		$reqdfields = explode(" ",
196
			"descr caref keylen lifetime dn_country dn_state dn_city ".
197
			"dn_organization dn_email dn_commonname");
198
		$reqdfieldsn = array(
199
			gettext("Descriptive name"),
200
			gettext("Signing Certificate Authority"),
201
			gettext("Key length"),
202
			gettext("Lifetime"),
203
			gettext("Distinguished name Country Code"),
204
			gettext("Distinguished name State or Province"),
205
			gettext("Distinguished name City"),
206
			gettext("Distinguished name Organization"),
207
			gettext("Distinguished name Email Address"),
208
			gettext("Distinguished name Common Name"));
209
	}
210

    
211
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
212
	if ($pconfig['method'] != "existing") {
213
		/* Make sure we do not have invalid characters in the fields for the certificate */
214
		if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) {
215
			array_push($input_errors, gettext("The field 'Descriptive Name' contains invalid characters."));
216
		}
217

    
218
		for ($i = 0; $i < count($reqdfields); $i++) {
219
			if ($reqdfields[$i] == 'dn_email') {
220
				if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_email"])) {
221
					array_push($input_errors, gettext("The field 'Distinguished name Email Address' contains invalid characters."));
222
				}
223
			}
224
		}
225
		if (!in_array($_POST["keylen"], $ca_keylens)) {
226
			array_push($input_errors, gettext("Please select a valid Key Length."));
227
		}
228
		if (!in_array($_POST["digest_alg"], $openssl_digest_algs)) {
229
			array_push($input_errors, gettext("Please select a valid Digest Algorithm."));
230
		}
231
	}
232

    
233
	/* save modifications */
234
	if (!$input_errors) {
235
		$ca = array();
236
		if (!isset($pconfig['refid']) || empty($pconfig['refid'])) {
237
			$ca['refid'] = uniqid();
238
		} else {
239
			$ca['refid'] = $pconfig['refid'];
240
		}
241

    
242
		if (isset($id) && $a_ca[$id]) {
243
			$ca = $a_ca[$id];
244
		}
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
			}
256
		} else {
257
			$old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */
258
			if ($pconfig['method'] == "existing") {
259
				ca_import($ca, $pconfig['cert'], $pconfig['key'], $pconfig['serial']);
260
			} else if ($pconfig['method'] == "internal") {
261
				$dn = array(
262
					'countryName' => $pconfig['dn_country'],
263
					'stateOrProvinceName' => cert_escape_x509_chars($pconfig['dn_state']),
264
					'localityName' => cert_escape_x509_chars($pconfig['dn_city']),
265
					'organizationName' => cert_escape_x509_chars($pconfig['dn_organization']),
266
					'emailAddress' => cert_escape_x509_chars($pconfig['dn_email']),
267
					'commonName' => cert_escape_x509_chars($pconfig['dn_commonname']));
268
				if (!empty($pconfig['dn_organizationalunit'])) {
269
					$dn['organizationalUnitName'] = cert_escape_x509_chars($pconfig['dn_organizationalunit']);
270
				}
271
				if (!ca_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['digest_alg'])) {
272
					$input_errors = array();
273
					while ($ssl_err = openssl_error_string()) {
274
						if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
275
							array_push($input_errors, "openssl library returns: " . $ssl_err);
276
						}
277
					}
278
				}
279
			} else if ($pconfig['method'] == "intermediate") {
280
				$dn = array(
281
					'countryName' => $pconfig['dn_country'],
282
					'stateOrProvinceName' => cert_escape_x509_chars($pconfig['dn_state']),
283
					'localityName' => cert_escape_x509_chars($pconfig['dn_city']),
284
					'organizationName' => cert_escape_x509_chars($pconfig['dn_organization']),
285
					'emailAddress' => cert_escape_x509_chars($pconfig['dn_email']),
286
					'commonName' => cert_escape_x509_chars($pconfig['dn_commonname']));
287
				if (!empty($pconfig['dn_organizationalunit'])) {
288
					$dn['organizationalUnitName'] = cert_escape_x509_chars($pconfig['dn_organizationalunit']);
289
				}
290
				if (!ca_inter_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['caref'], $pconfig['digest_alg'])) {
291
					$input_errors = array();
292
					while ($ssl_err = openssl_error_string()) {
293
						if (strpos($ssl_err, 'NCONF_get_string:no value') === false) {
294
							array_push($input_errors, "openssl library returns: " . $ssl_err);
295
						}
296
					}
297
				}
298
			}
299
			error_reporting($old_err_level);
300
		}
301

    
302
		if (isset($id) && $a_ca[$id]) {
303
			$a_ca[$id] = $ca;
304
		} else {
305
			$a_ca[] = $ca;
306
		}
307

    
308
		if (!$input_errors) {
309
			write_config();
310
			pfSenseHeader("system_camanager.php");
311
		}
312
	}
313
}
314

    
315
$pgtitle = array(gettext("System"), gettext("Certificate Manager"), gettext("CAs"));
316
$pglinks = array("", "system_camanager.php", "system_camanager.php");
317

    
318
if ($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors) {
319
	$pgtitle[] = gettext('Edit');
320
	$pglinks[] = "@self";
321
}
322
include("head.inc");
323

    
324
if ($input_errors) {
325
	print_input_errors($input_errors);
326
}
327

    
328
if ($savemsg) {
329
	print_info_box($savemsg, 'success');
330
}
331

    
332
// Load valid country codes
333
$dn_cc = array();
334
if (file_exists("/etc/ca_countries")) {
335
	$dn_cc_file=file("/etc/ca_countries");
336
	foreach ($dn_cc_file as $line) {
337
		if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) {
338
			$dn_cc[$matches[1]] = $matches[1];
339
		}
340
	}
341
}
342

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

    
349
if (!($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors)) {
350
?>
351
<div class="panel panel-default">
352
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Certificate Authorities')?></h2></div>
353
	<div class="panel-body">
354
		<div class="table-responsive">
355
		<table class="table table-striped table-hover table-rowdblclickedit">
356
			<thead>
357
				<tr>
358
					<th><?=gettext("Name")?></th>
359
					<th><?=gettext("Internal")?></th>
360
					<th><?=gettext("Issuer")?></th>
361
					<th><?=gettext("Certificates")?></th>
362
					<th><?=gettext("Distinguished Name")?></th>
363
					<th><?=gettext("In Use")?></th>
364
					<th><?=gettext("Actions")?></th>
365
				</tr>
366
			</thead>
367
			<tbody>
368
<?php
369
$pluginparams = array();
370
$pluginparams['type'] = 'certificates';
371
$pluginparams['event'] = 'used_ca';
372
$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
373

    
374
foreach ($a_ca as $i => $ca):
375
	$name = htmlspecialchars($ca['descr']);
376
	$subj = cert_get_subject($ca['crt']);
377
	$issuer = cert_get_issuer($ca['crt']);
378
	list($startdate, $enddate) = cert_get_dates($ca['crt']);
379
	if ($subj == $issuer) {
380
		$issuer_name = gettext("self-signed");
381
	} else {
382
		$issuer_name = gettext("external");
383
	}
384
	$subj = htmlspecialchars(cert_escape_x509_chars($subj, true));
385
	$issuer = htmlspecialchars($issuer);
386
	$certcount = 0;
387

    
388
	$issuer_ca = lookup_ca($ca['caref']);
389
	if ($issuer_ca) {
390
		$issuer_name = $issuer_ca['descr'];
391
	}
392

    
393
	foreach ($a_cert as $cert) {
394
		if ($cert['caref'] == $ca['refid']) {
395
			$certcount++;
396
		}
397
	}
398

    
399
	foreach ($a_ca as $cert) {
400
		if ($cert['caref'] == $ca['refid']) {
401
			$certcount++;
402
		}
403
	}
404
?>
405
				<tr>
406
					<td><?=$name?></td>
407
					<td><i class="fa fa-<?= (!empty($ca['prv'])) ? "check" : "times" ; ?>"></i></td>
408
					<td><i><?=$issuer_name?></i></td>
409
					<td><?=$certcount?></td>
410
					<td>
411
						<?=$subj?>
412
						<br />
413
						<small>
414
							<?=gettext("Valid From")?>: <b><?=$startdate ?></b><br /><?=gettext("Valid Until")?>: <b><?=$enddate ?></b>
415
						</small>
416
					</td>
417
					<td class="text-nowrap">
418
						<?php if (is_openvpn_server_ca($ca['refid'])): ?>
419
							<?=gettext("OpenVPN Server")?><br/>
420
						<?php endif?>
421
						<?php if (is_openvpn_client_ca($ca['refid'])): ?>
422
							<?=gettext("OpenVPN Client")?><br/>
423
						<?php endif?>
424
						<?php if (is_ipsec_peer_ca($ca['refid'])): ?>
425
							<?=gettext("IPsec Tunnel")?><br/>
426
						<?php endif?>
427
						<?php if (is_ldap_peer_ca($ca['refid'])): ?>
428
							<?=gettext("LDAP Server")?>
429
						<?php endif?>
430
						<?php echo cert_usedby_description($ca['refid'], $certificates_used_by_packages); ?>
431
					</td>
432
					<td class="text-nowrap">
433
						<a class="fa fa-pencil"	title="<?=gettext("Edit CA")?>"	href="system_camanager.php?act=edit&amp;id=<?=$i?>"></a>
434
						<a class="fa fa-certificate"	title="<?=gettext("Export CA")?>"	href="system_camanager.php?act=exp&amp;id=<?=$i?>"></a>
435
					<?php if ($ca['prv']): ?>
436
						<a class="fa fa-key"	title="<?=gettext("Export key")?>"	href="system_camanager.php?act=expkey&amp;id=<?=$i?>"></a>
437
					<?php endif?>
438
					<?php if (!ca_in_use($ca['refid'])): ?>
439
						<a class="fa fa-trash" 	title="<?=gettext("Delete CA and its CRLs")?>"	href="system_camanager.php?act=del&amp;id=<?=$i?>" usepost ></a>
440
					<?php endif?>
441
					</td>
442
				</tr>
443
<?php endforeach; ?>
444
			</tbody>
445
		</table>
446
		</div>
447
	</div>
448
</div>
449

    
450
<nav class="action-buttons">
451
	<a href="?act=new" class="btn btn-success btn-sm">
452
		<i class="fa fa-plus icon-embed-btn"></i>
453
		<?=gettext("Add")?>
454
	</a>
455
</nav>
456
<?php
457
	include("foot.inc");
458
	exit;
459
}
460

    
461
$form = new Form;
462
//$form->setAction('system_camanager.php?act=edit');
463
if (isset($id) && $a_ca[$id]) {
464
	$form->addGlobal(new Form_Input(
465
		'id',
466
		null,
467
		'hidden',
468
		$id
469
	));
470
}
471

    
472
if ($act == "edit") {
473
	$form->addGlobal(new Form_Input(
474
		'refid',
475
		null,
476
		'hidden',
477
		$pconfig['refid']
478
	));
479
}
480

    
481
$section = new Form_Section('Create / Edit CA');
482

    
483
$section->addInput(new Form_Input(
484
	'descr',
485
	'*Descriptive name',
486
	'text',
487
	$pconfig['descr']
488
));
489

    
490
if (!isset($id) || $act == "edit") {
491
	$section->addInput(new Form_Select(
492
		'method',
493
		'*Method',
494
		$pconfig['method'],
495
		$ca_methods
496
	))->toggles();
497
}
498

    
499
$form->add($section);
500

    
501
$section = new Form_Section('Existing Certificate Authority');
502
$section->addClass('toggle-existing collapse');
503

    
504
$section->addInput(new Form_Textarea(
505
	'cert',
506
	'*Certificate data',
507
	$pconfig['cert']
508
))->setHelp('Paste a certificate in X.509 PEM format here.');
509

    
510
$section->addInput(new Form_Textarea(
511
	'key',
512
	'Certificate Private Key (optional)',
513
	$pconfig['key']
514
))->setHelp('Paste the private key for the above certificate here. This is '.
515
	'optional in most cases, but is required when generating a '.
516
	'Certificate Revocation List (CRL).');
517

    
518
$section->addInput(new Form_Input(
519
	'serial',
520
	'Serial for next certificate',
521
	'number',
522
	$pconfig['serial']
523
))->setHelp('Enter a decimal number to be used as the serial number for the next '.
524
	'certificate to be created using this CA.');
525

    
526
$form->add($section);
527

    
528
$section = new Form_Section('Internal Certificate Authority');
529
$section->addClass('toggle-internal', 'toggle-intermediate', 'collapse');
530

    
531
$allCas = array();
532
foreach ($a_ca as $ca) {
533
	if (!$ca['prv']) {
534
			continue;
535
	}
536

    
537
	$allCas[ $ca['refid'] ] = $ca['descr'];
538
}
539

    
540
$group = new Form_Group('*Signing Certificate Authority');
541
$group->addClass('toggle-intermediate', 'collapse');
542
$group->add(new Form_Select(
543
	'caref',
544
	null,
545
	$pconfig['caref'],
546
	$allCas
547
));
548
$section->add($group);
549

    
550
$section->addInput(new Form_Select(
551
	'keylen',
552
	'*Key length (bits)',
553
	$pconfig['keylen'],
554
	array_combine($ca_keylens, $ca_keylens)
555
));
556

    
557
$section->addInput(new Form_Select(
558
	'digest_alg',
559
	'*Digest Algorithm',
560
	$pconfig['digest_alg'],
561
	array_combine($openssl_digest_algs, $openssl_digest_algs)
562
))->setHelp('NOTE: It is recommended to use an algorithm stronger than SHA1 '.
563
	'when possible.');
564

    
565
$section->addInput(new Form_Input(
566
	'lifetime',
567
	'*Lifetime (days)',
568
	'number',
569
	$pconfig['lifetime']
570
));
571

    
572
$section->addInput(new Form_Select(
573
	'dn_country',
574
	'*Country Code',
575
	$pconfig['dn_country'],
576
	$dn_cc
577
));
578

    
579
$section->addInput(new Form_Input(
580
	'dn_state',
581
	'*State or Province',
582
	'text',
583
	$pconfig['dn_state'],
584
	['placeholder' => 'e.g. Texas']
585
));
586

    
587
$section->addInput(new Form_Input(
588
	'dn_city',
589
	'*City',
590
	'text',
591
	$pconfig['dn_city'],
592
	['placeholder' => 'e.g. Austin']
593
));
594

    
595
$section->addInput(new Form_Input(
596
	'dn_organization',
597
	'*Organization',
598
	'text',
599
	$pconfig['dn_organization'],
600
	['placeholder' => 'e.g. My Company Inc']
601
));
602

    
603
$section->addInput(new Form_Input(
604
	'dn_organizationalunit',
605
	'Organizational Unit',
606
	'text',
607
	$pconfig['dn_organizationalunit'],
608
	['placeholder' => 'e.g. My Department Name (optional)']
609
));
610

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

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

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

    
629
print $form;
630

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

    
638
include('foot.inc');
639
?>
(190-190/223)