Project

General

Profile

Download (22.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * certs.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2008-2016 Rubicon Communications, LLC (Netgate)
7
 * Copyright (c) 2008 Shrew Soft Inc. All rights reserved.
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
define("OPEN_SSL_CONF_PATH", "/etc/ssl/openssl.cnf");
56

    
57
require_once("functions.inc");
58

    
59
global $openssl_digest_algs;
60
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
61

    
62
global $openssl_crl_status;
63
$openssl_crl_status = array(
64
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
65
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
66
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
67
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
68
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
69
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
70
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
71
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
72
);
73

    
74
function & lookup_ca($refid) {
75
	global $config;
76

    
77
	if (is_array($config['ca'])) {
78
		foreach ($config['ca'] as & $ca) {
79
			if ($ca['refid'] == $refid) {
80
				return $ca;
81
			}
82
		}
83
	}
84

    
85
	return false;
86
}
87

    
88
function & lookup_ca_by_subject($subject) {
89
	global $config;
90

    
91
	if (is_array($config['ca'])) {
92
		foreach ($config['ca'] as & $ca) {
93
			$ca_subject = cert_get_subject($ca['crt']);
94
			if ($ca_subject == $subject) {
95
				return $ca;
96
			}
97
		}
98
	}
99

    
100
	return false;
101
}
102

    
103
function & lookup_cert($refid) {
104
	global $config;
105

    
106
	if (is_array($config['cert'])) {
107
		foreach ($config['cert'] as & $cert) {
108
			if ($cert['refid'] == $refid) {
109
				return $cert;
110
			}
111
		}
112
	}
113

    
114
	return false;
115
}
116

    
117
function & lookup_cert_by_name($name) {
118
	global $config;
119
	if (is_array($config['cert'])) {
120
		foreach ($config['cert'] as & $cert) {
121
			if ($cert['descr'] == $name) {
122
				return $cert;
123
			}
124
		}
125
	}
126
}
127

    
128
function & lookup_crl($refid) {
129
	global $config;
130

    
131
	if (is_array($config['crl'])) {
132
		foreach ($config['crl'] as & $crl) {
133
			if ($crl['refid'] == $refid) {
134
				return $crl;
135
			}
136
		}
137
	}
138

    
139
	return false;
140
}
141

    
142
function ca_chain_array(& $cert) {
143
	if ($cert['caref']) {
144
		$chain = array();
145
		$crt = lookup_ca($cert['caref']);
146
		$chain[] = $crt;
147
		while ($crt) {
148
			$caref = $crt['caref'];
149
			if ($caref) {
150
				$crt = lookup_ca($caref);
151
			} else {
152
				$crt = false;
153
			}
154
			if ($crt) {
155
				$chain[] = $crt;
156
			}
157
		}
158
		return $chain;
159
	}
160
	return false;
161
}
162

    
163
function ca_chain(& $cert) {
164
	if ($cert['caref']) {
165
		$ca = "";
166
		$cas = ca_chain_array($cert);
167
		if (is_array($cas)) {
168
			foreach ($cas as & $ca_cert) {
169
				$ca .= base64_decode($ca_cert['crt']);
170
				$ca .= "\n";
171
			}
172
		}
173
		return $ca;
174
	}
175
	return "";
176
}
177

    
178
function ca_import(& $ca, $str, $key = "", $serial = 0) {
179
	global $config;
180

    
181
	$ca['crt'] = base64_encode($str);
182
	if (!empty($key)) {
183
		$ca['prv'] = base64_encode($key);
184
	}
185
	if (!empty($serial)) {
186
		$ca['serial'] = $serial;
187
	}
188
	$subject = cert_get_subject($str, false);
189
	$issuer = cert_get_issuer($str, false);
190

    
191
	// Find my issuer unless self-signed
192
	if ($issuer <> $subject) {
193
		$issuer_crt =& lookup_ca_by_subject($issuer);
194
		if ($issuer_crt) {
195
			$ca['caref'] = $issuer_crt['refid'];
196
		}
197
	}
198

    
199
	/* Correct if child certificate was loaded first */
200
	if (is_array($config['ca'])) {
201
		foreach ($config['ca'] as & $oca) {
202
			$issuer = cert_get_issuer($oca['crt']);
203
			if ($ca['refid'] <> $oca['refid'] && $issuer == $subject) {
204
				$oca['caref'] = $ca['refid'];
205
			}
206
		}
207
	}
208
	if (is_array($config['cert'])) {
209
		foreach ($config['cert'] as & $cert) {
210
			$issuer = cert_get_issuer($cert['crt']);
211
			if ($issuer == $subject) {
212
				$cert['caref'] = $ca['refid'];
213
			}
214
		}
215
	}
216
	return true;
217
}
218

    
219
function ca_create(& $ca, $keylen, $lifetime, $dn, $digest_alg = "sha256") {
220

    
221
	$args = array(
222
		"x509_extensions" => "v3_ca",
223
		"digest_alg" => $digest_alg,
224
		"private_key_bits" => (int)$keylen,
225
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
226
		"encrypt_key" => false);
227

    
228
	// generate a new key pair
229
	$res_key = openssl_pkey_new($args);
230
	if (!$res_key) {
231
		return false;
232
	}
233

    
234
	// generate a certificate signing request
235
	$res_csr = openssl_csr_new($dn, $res_key, $args);
236
	if (!$res_csr) {
237
		return false;
238
	}
239

    
240
	// self sign the certificate
241
	$res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args);
242
	if (!$res_crt) {
243
		return false;
244
	}
245

    
246
	// export our certificate data
247
	if (!openssl_pkey_export($res_key, $str_key) ||
248
	    !openssl_x509_export($res_crt, $str_crt)) {
249
		return false;
250
	}
251

    
252
	// return our ca information
253
	$ca['crt'] = base64_encode($str_crt);
254
	$ca['prv'] = base64_encode($str_key);
255
	$ca['serial'] = 0;
256

    
257
	return true;
258
}
259

    
260
function ca_inter_create(& $ca, $keylen, $lifetime, $dn, $caref, $digest_alg = "sha256") {
261
	// Create Intermediate Certificate Authority
262
	$signing_ca =& lookup_ca($caref);
263
	if (!$signing_ca) {
264
		return false;
265
	}
266

    
267
	$signing_ca_res_crt = openssl_x509_read(base64_decode($signing_ca['crt']));
268
	$signing_ca_res_key = openssl_pkey_get_private(array(0 => base64_decode($signing_ca['prv']) , 1 => ""));
269
	if (!$signing_ca_res_crt || !$signing_ca_res_key) {
270
		return false;
271
	}
272
	$signing_ca_serial = ++$signing_ca['serial'];
273

    
274
	$args = array(
275
		"x509_extensions" => "v3_ca",
276
		"digest_alg" => $digest_alg,
277
		"private_key_bits" => (int)$keylen,
278
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
279
		"encrypt_key" => false);
280

    
281
	// generate a new key pair
282
	$res_key = openssl_pkey_new($args);
283
	if (!$res_key) {
284
		return false;
285
	}
286

    
287
	// generate a certificate signing request
288
	$res_csr = openssl_csr_new($dn, $res_key, $args);
289
	if (!$res_csr) {
290
		return false;
291
	}
292

    
293
	// Sign the certificate
294
	$res_crt = openssl_csr_sign($res_csr, $signing_ca_res_crt, $signing_ca_res_key, $lifetime, $args, $signing_ca_serial);
295
	if (!$res_crt) {
296
		return false;
297
	}
298

    
299
	// export our certificate data
300
	if (!openssl_pkey_export($res_key, $str_key) ||
301
	    !openssl_x509_export($res_crt, $str_crt)) {
302
		return false;
303
	}
304

    
305
	// return our ca information
306
	$ca['crt'] = base64_encode($str_crt);
307
	$ca['prv'] = base64_encode($str_key);
308
	$ca['serial'] = 0;
309
	$ca['caref'] = $caref;
310

    
311
	return true;
312
}
313

    
314
function cert_import(& $cert, $crt_str, $key_str) {
315

    
316
	$cert['crt'] = base64_encode($crt_str);
317
	$cert['prv'] = base64_encode($key_str);
318

    
319
	$subject = cert_get_subject($crt_str, false);
320
	$issuer = cert_get_issuer($crt_str, false);
321

    
322
	// Find my issuer unless self-signed
323
	if ($issuer <> $subject) {
324
		$issuer_crt =& lookup_ca_by_subject($issuer);
325
		if ($issuer_crt) {
326
			$cert['caref'] = $issuer_crt['refid'];
327
		}
328
	}
329
	return true;
330
}
331

    
332
function cert_create(& $cert, $caref, $keylen, $lifetime, $dn, $type = "user", $digest_alg = "sha256") {
333

    
334
	$cert['type'] = $type;
335

    
336
	if ($type != "self-signed") {
337
		$cert['caref'] = $caref;
338
		$ca =& lookup_ca($caref);
339
		if (!$ca) {
340
			return false;
341
		}
342

    
343
		$ca_str_crt = base64_decode($ca['crt']);
344
		$ca_str_key = base64_decode($ca['prv']);
345
		$ca_res_crt = openssl_x509_read($ca_str_crt);
346
		$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
347
		if (!$ca_res_key) {
348
			return false;
349
		}
350
		$ca_serial = ++$ca['serial'];
351
	}
352

    
353
	switch ($type) {
354
		case "ca":
355
			$cert_type = "v3_ca";
356
			break;
357
		case "server":
358
		case "self-signed":
359
			$cert_type = "server";
360
			break;
361
		default:
362
			$cert_type = "usr_cert";
363
			break;
364
	}
365

    
366
	// in case of using Subject Alternative Names use other sections (with postfix '_san')
367
	// pass subjectAltName over environment variable 'SAN'
368
	if ($dn['subjectAltName']) {
369
		putenv("SAN={$dn['subjectAltName']}"); // subjectAltName can be set _only_ via configuration file
370
		$cert_type .= '_san';
371
		unset($dn['subjectAltName']);
372
	}
373

    
374
	$args = array(
375
		"x509_extensions" => $cert_type,
376
		"digest_alg" => $digest_alg,
377
		"private_key_bits" => (int)$keylen,
378
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
379
		"encrypt_key" => false);
380

    
381
	// generate a new key pair
382
	$res_key = openssl_pkey_new($args);
383
	if (!$res_key) {
384
		return false;
385
	}
386

    
387
	// If this is a self-signed cert, blank out the CA and sign with the cert's key
388
	if ($type == "self-signed") {
389
		$ca           = null;
390
		$ca_res_crt   = null;
391
		$ca_res_key   = $res_key;
392
		$ca_serial    = 0;
393
		$cert['type'] = "server";
394
	}
395

    
396
	// generate a certificate signing request
397
	$res_csr = openssl_csr_new($dn, $res_key, $args);
398
	if (!$res_csr) {
399
		return false;
400
	}
401

    
402
	// sign the certificate using an internal CA
403
	$res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime,
404
				 $args, $ca_serial);
405
	if (!$res_crt) {
406
		return false;
407
	}
408

    
409
	// export our certificate data
410
	if (!openssl_pkey_export($res_key, $str_key) ||
411
	    !openssl_x509_export($res_crt, $str_crt)) {
412
		return false;
413
	}
414

    
415
	// return our certificate information
416
	$cert['crt'] = base64_encode($str_crt);
417
	$cert['prv'] = base64_encode($str_key);
418

    
419
	return true;
420
}
421

    
422
function csr_generate(& $cert, $keylen, $dn, $digest_alg = "sha256") {
423

    
424
	$args = array(
425
		"x509_extensions" => "v3_req",
426
		"digest_alg" => $digest_alg,
427
		"private_key_bits" => (int)$keylen,
428
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
429
		"encrypt_key" => false);
430

    
431
	// generate a new key pair
432
	$res_key = openssl_pkey_new($args);
433
	if (!$res_key) {
434
		return false;
435
	}
436

    
437
	// generate a certificate signing request
438
	$res_csr = openssl_csr_new($dn, $res_key, $args);
439
	if (!$res_csr) {
440
		return false;
441
	}
442

    
443
	// export our request data
444
	if (!openssl_pkey_export($res_key, $str_key) ||
445
	    !openssl_csr_export($res_csr, $str_csr)) {
446
		return false;
447
	}
448

    
449
	// return our request information
450
	$cert['csr'] = base64_encode($str_csr);
451
	$cert['prv'] = base64_encode($str_key);
452

    
453
	return true;
454
}
455

    
456
function csr_complete(& $cert, $str_crt) {
457
	$str_key = base64_decode($cert['prv']);
458
	cert_import($cert, $str_crt, $str_key);
459
	unset($cert['csr']);
460
	return true;
461
}
462

    
463
function csr_get_subject($str_crt, $decode = true) {
464

    
465
	if ($decode) {
466
		$str_crt = base64_decode($str_crt);
467
	}
468

    
469
	$components = openssl_csr_get_subject($str_crt);
470

    
471
	if (empty($components) || !is_array($components)) {
472
		return "unknown";
473
	}
474

    
475
	ksort($components);
476
	foreach ($components as $a => $v) {
477
		if (!strlen($subject)) {
478
			$subject = "{$a}={$v}";
479
		} else {
480
			$subject = "{$a}={$v}, {$subject}";
481
		}
482
	}
483

    
484
	return $subject;
485
}
486

    
487
function cert_get_subject($str_crt, $decode = true) {
488

    
489
	if ($decode) {
490
		$str_crt = base64_decode($str_crt);
491
	}
492

    
493
	$inf_crt = openssl_x509_parse($str_crt);
494
	$components = $inf_crt['subject'];
495

    
496
	if (empty($components) || !is_array($components)) {
497
		return "unknown";
498
	}
499

    
500
	ksort($components);
501
	foreach ($components as $a => $v) {
502
		if (is_array($v)) {
503
			ksort($v);
504
			foreach ($v as $w) {
505
				$asubject = "{$a}={$w}";
506
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
507
			}
508
		} else {
509
			$asubject = "{$a}={$v}";
510
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
511
		}
512
	}
513

    
514
	return $subject;
515
}
516

    
517
function cert_get_subject_array($crt) {
518
	$str_crt = base64_decode($crt);
519
	$inf_crt = openssl_x509_parse($str_crt);
520
	$components = $inf_crt['subject'];
521

    
522
	if (!is_array($components)) {
523
		return;
524
	}
525

    
526
	$subject_array = array();
527

    
528
	foreach ($components as $a => $v) {
529
		$subject_array[] = array('a' => $a, 'v' => $v);
530
	}
531

    
532
	return $subject_array;
533
}
534

    
535
function cert_get_subject_hash($crt) {
536
	$str_crt = base64_decode($crt);
537
	$inf_crt = openssl_x509_parse($str_crt);
538
	return $inf_crt['subject'];
539
}
540

    
541
function cert_get_issuer($str_crt, $decode = true) {
542

    
543
	if ($decode) {
544
		$str_crt = base64_decode($str_crt);
545
	}
546

    
547
	$inf_crt = openssl_x509_parse($str_crt);
548
	$components = $inf_crt['issuer'];
549

    
550
	if (empty($components) || !is_array($components)) {
551
		return "unknown";
552
	}
553

    
554
	ksort($components);
555
	foreach ($components as $a => $v) {
556
		if (is_array($v)) {
557
			ksort($v);
558
			foreach ($v as $w) {
559
				$aissuer = "{$a}={$w}";
560
				$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
561
			}
562
		} else {
563
			$aissuer = "{$a}={$v}";
564
			$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
565
		}
566
	}
567

    
568
	return $issuer;
569
}
570

    
571
/* this function works on x509 (crt), rsa key (prv), and req(csr) */
572
function cert_get_modulus($str_crt, $decode = true, $type = "crt") {
573
	if ($decode) {
574
		$str_crt = base64_decode($str_crt);
575
	}
576

    
577
	$modulus = "";
578
	if (in_array($type, array("crt", "prv", "csr"))) {
579
		$type = str_replace(array("crt", "prv", "csr"), array("x509", "rsa", "req"), $type);
580
		$modulus = exec("echo \"{$str_crt}\" | openssl {$type} -noout -modulus");
581
	}
582
	return $modulus;
583
}
584

    
585
/* Same but returns modulus keysize not modulus itself */
586
function cert_get_modulus_keysize($str_crt, $decode = true, $type = "crt") {
587
	// modulus usually returned as "modulus=.....". Remove anything before an "=" and return 4 x (hex string length)
588
	$raw_modulus = explode('=', cert_get_modulus($str_crt, $decode, $type));
589
	return strlen(array_pop($raw_modulus))*4;
590
}
591

    
592
function csr_get_modulus($str_crt, $decode = true) {
593
	return cert_get_modulus($str_crt, $decode, "csr");
594
}
595

    
596
function cert_get_purpose($str_crt, $decode = true) {
597
	if ($decode) {
598
		$str_crt = base64_decode($str_crt);
599
	}
600
	$crt_details = openssl_x509_parse($str_crt);
601
	$purpose = array();
602
	$purpose['ca'] = (stristr($crt_details['extensions']['basicConstraints'], 'CA:TRUE') === false) ? 'No': 'Yes';
603
	$purpose['server'] = ($crt_details['extensions']['nsCertType'] == "SSL Server") ? 'Yes': 'No';
604
	return $purpose;
605
}
606

    
607
function cert_get_dates($str_crt, $decode = true) {
608
	if ($decode) {
609
		$str_crt = base64_decode($str_crt);
610
	}
611
	$crt_details = openssl_x509_parse($str_crt);
612
	if ($crt_details['validFrom_time_t'] > 0) {
613
		$start = date('r', $crt_details['validFrom_time_t']);
614
	}
615
	if ($crt_details['validTo_time_t'] > 0) {
616
		$end = date('r', $crt_details['validTo_time_t']);
617
	}
618
	return array($start, $end);
619
}
620

    
621
function cert_get_serial($str_crt, $decode = true) {
622
	if ($decode) {
623
		$str_crt = base64_decode($str_crt);
624
	}
625
	$crt_details = openssl_x509_parse($str_crt);
626
	if (isset($crt_details['serialNumber']) && !empty($crt_details['serialNumber'])) {
627
		return $crt_details['serialNumber'];
628
	} else {
629
		return NULL;
630
	}
631
}
632

    
633
function prv_get_modulus($str_crt, $decode = true) {
634
	return cert_get_modulus($str_crt, $decode, "prv");
635
}
636

    
637
function is_user_cert($certref) {
638
	global $config;
639
	if (!is_array($config['system']['user'])) {
640
		return;
641
	}
642
	foreach ($config['system']['user'] as $user) {
643
		if (!is_array($user['cert'])) {
644
			continue;
645
		}
646
		foreach ($user['cert'] as $cert) {
647
			if ($certref == $cert) {
648
				return true;
649
			}
650
		}
651
	}
652
	return false;
653
}
654

    
655
function is_openvpn_server_cert($certref) {
656
	global $config;
657
	if (!is_array($config['openvpn']['openvpn-server'])) {
658
		return;
659
	}
660
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
661
		if ($ovpns['certref'] == $certref) {
662
			return true;
663
		}
664
	}
665
	return false;
666
}
667

    
668
function is_openvpn_client_cert($certref) {
669
	global $config;
670
	if (!is_array($config['openvpn']['openvpn-client'])) {
671
		return;
672
	}
673
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
674
		if ($ovpnc['certref'] == $certref) {
675
			return true;
676
		}
677
	}
678
	return false;
679
}
680

    
681
function is_ipsec_cert($certref) {
682
	global $config;
683
	if (!is_array($config['ipsec']['phase1'])) {
684
		return;
685
	}
686
	foreach ($config['ipsec']['phase1'] as $ipsec) {
687
		if ($ipsec['certref'] == $certref) {
688
			return true;
689
		}
690
	}
691
	return false;
692
}
693

    
694
function is_webgui_cert($certref) {
695
	global $config;
696
	if (($config['system']['webgui']['ssl-certref'] == $certref) &&
697
	    ($config['system']['webgui']['protocol'] != "http")) {
698
		return true;
699
	}
700
}
701

    
702
function is_package_cert($certref) {
703
	$pluginparams = array();
704
	$pluginparams['type'] = 'certificates';
705
	$pluginparams['event'] = 'used_certificates';
706

    
707
	$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
708

    
709
	/* Check if any package is using certificate */
710
	foreach ($certificates_used_by_packages as $name => $package) {
711
		if (is_array($package['certificatelist'][$certref]) &&
712
		    isset($package['certificatelist'][$certref]) > 0) {
713
			return true;
714
		}
715
	}
716
}
717

    
718
function is_captiveportal_cert($certref) {
719
	global $config;
720
	if (!is_array($config['captiveportal'])) {
721
		return;
722
	}
723
	foreach ($config['captiveportal'] as $portal) {
724
		if (isset($portal['enable']) && isset($portal['httpslogin']) && ($portal['certref'] == $certref)) {
725
			return true;
726
		}
727
	}
728
	return false;
729
}
730

    
731
function cert_in_use($certref) {
732

    
733
	return (is_webgui_cert($certref) ||
734
		is_user_cert($certref) ||
735
		is_openvpn_server_cert($certref) ||
736
		is_openvpn_client_cert($certref) ||
737
		is_ipsec_cert($certref) ||
738
		is_captiveportal_cert($certref) ||
739
		is_package_cert($certref));
740
}
741

    
742
function crl_create(& $crl, $caref, $name, $serial = 0, $lifetime = 9999) {
743
	global $config;
744
	$ca =& lookup_ca($caref);
745
	if (!$ca) {
746
		return false;
747
	}
748
	$crl['descr'] = $name;
749
	$crl['caref'] = $caref;
750
	$crl['serial'] = $serial;
751
	$crl['lifetime'] = $lifetime;
752
	$crl['cert'] = array();
753
	$crl_res = crl_update($crl);
754
	$config['crl'][] = $crl;
755
	return $crl_res;
756
}
757

    
758
function crl_update(& $crl) {
759
	global $config;
760
	$ca =& lookup_ca($crl['caref']);
761
	if (!$ca) {
762
		return false;
763
	}
764
	// If we have text but no certs, it was imported and cannot be updated.
765
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert']))) {
766
		return false;
767
	}
768
	$crl['serial']++;
769
	$ca_str_crt = base64_decode($ca['crt']);
770
	$ca_str_key = base64_decode($ca['prv']);
771
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
772
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
773
		foreach ($crl['cert'] as $cert) {
774
			openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
775
		}
776
	}
777
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
778
	$crl['text'] = base64_encode($crl_text);
779
	return $crl_res;
780
}
781

    
782
function cert_revoke($cert, & $crl, $reason = OCSP_REVOKED_STATUS_UNSPECIFIED) {
783
	global $config;
784
	if (is_cert_revoked($cert, $crl['refid'])) {
785
		return true;
786
	}
787
	// If we have text but no certs, it was imported and cannot be updated.
788
	if (!is_crl_internal($crl)) {
789
		return false;
790
	}
791
	$cert["reason"] = $reason;
792
	$cert["revoke_time"] = time();
793
	$crl["cert"][] = $cert;
794
	crl_update($crl);
795
	return true;
796
}
797

    
798
function cert_unrevoke($cert, & $crl) {
799
	global $config;
800
	if (!is_crl_internal($crl)) {
801
		return false;
802
	}
803
	foreach ($crl['cert'] as $id => $rcert) {
804
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
805
			unset($crl['cert'][$id]);
806
			if (count($crl['cert']) == 0) {
807
				// Protect against accidentally switching the type to imported, for older CRLs
808
				if (!isset($crl['method'])) {
809
					$crl['method'] = "internal";
810
				}
811
				crl_update($crl);
812
			} else {
813
				crl_update($crl);
814
			}
815
			return true;
816
		}
817
	}
818
	return false;
819
}
820

    
821
/* Compare two certificates to see if they match. */
822
function cert_compare($cert1, $cert2) {
823
	/* Ensure two certs are identical by first checking that their issuers match, then
824
		subjects, then serial numbers, and finally the moduli. Anything less strict
825
		could accidentally count two similar, but different, certificates as
826
		being identical. */
827
	$c1 = base64_decode($cert1['crt']);
828
	$c2 = base64_decode($cert2['crt']);
829
	if ((cert_get_issuer($c1, false) == cert_get_issuer($c2, false)) &&
830
	    (cert_get_subject($c1, false) == cert_get_subject($c2, false)) &&
831
	    (cert_get_serial($c1, false) == cert_get_serial($c2, false)) &&
832
	    (cert_get_modulus($c1, false) == cert_get_modulus($c2, false))) {
833
		return true;
834
	}
835
	return false;
836
}
837

    
838
function is_cert_revoked($cert, $crlref = "") {
839
	global $config;
840
	if (!is_array($config['crl'])) {
841
		return false;
842
	}
843

    
844
	if (!empty($crlref)) {
845
		$crl = lookup_crl($crlref);
846
		if (!is_array($crl['cert'])) {
847
			return false;
848
		}
849
		foreach ($crl['cert'] as $rcert) {
850
			if (cert_compare($rcert, $cert)) {
851
				return true;
852
			}
853
		}
854
	} else {
855
		foreach ($config['crl'] as $crl) {
856
			if (!is_array($crl['cert'])) {
857
				continue;
858
			}
859
			foreach ($crl['cert'] as $rcert) {
860
				if (cert_compare($rcert, $cert)) {
861
					return true;
862
				}
863
			}
864
		}
865
	}
866
	return false;
867
}
868

    
869
function is_openvpn_server_crl($crlref) {
870
	global $config;
871
	if (!is_array($config['openvpn']['openvpn-server'])) {
872
		return;
873
	}
874
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
875
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref)) {
876
			return true;
877
		}
878
	}
879
	return false;
880
}
881

    
882
// Keep this general to allow for future expansion. See cert_in_use() above.
883
function crl_in_use($crlref) {
884
	return (is_openvpn_server_crl($crlref));
885
}
886

    
887
function is_crl_internal($crl) {
888
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
889
}
890

    
891
function cert_get_cn($crt, $isref = false) {
892
	/* If this is a certref, not an actual cert, look up the cert first */
893
	if ($isref) {
894
		$cert = lookup_cert($crt);
895
		/* If it's not a valid cert, bail. */
896
		if (!(is_array($cert) && !empty($cert['crt']))) {
897
			return "";
898
		}
899
		$cert = $cert['crt'];
900
	} else {
901
		$cert = $crt;
902
	}
903
	$sub = cert_get_subject_array($cert);
904
	if (is_array($sub)) {
905
		foreach ($sub as $s) {
906
			if (strtoupper($s['a']) == "CN") {
907
				return $s['v'];
908
			}
909
		}
910
	}
911
	return "";
912
}
913

    
914
?>
(8-8/65)