Project

General

Profile

Download (20.6 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	certs.inc
4
	Copyright (C) 2008 Shrew Soft Inc
5
	Copyright (C) 2010 Jim Pingle <jimp@pfsense.org>
6
	All rights reserved.
7

    
8
	Redistribution and use in source and binary forms, with or without
9
	modification, are permitted provided that the following conditions are met:
10

    
11
	1. Redistributions of source code must retain the above copyright notice,
12
	   this list of conditions and the following disclaimer.
13

    
14
	2. Redistributions in binary form must reproduce the above copyright
15
	   notice, this list of conditions and the following disclaimer in the
16
	   documentation and/or other materials provided with the distribution.
17

    
18
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
	POSSIBILITY OF SUCH DAMAGE.
28

    
29
	pfSense_MODULE:	certificate_manager
30
*/
31

    
32
define("OPEN_SSL_CONF_PATH", "/etc/ssl/openssl.cnf");
33

    
34
require_once("functions.inc");
35

    
36
global $openssl_digest_algs;
37
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
38

    
39
global $openssl_crl_status;
40
$openssl_crl_status = array(
41
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
42
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
43
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
44
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
45
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
46
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
47
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
48
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
49
);
50

    
51
function & lookup_ca($refid) {
52
	global $config;
53

    
54
	if (is_array($config['ca'])) {
55
		foreach ($config['ca'] as & $ca) {
56
			if ($ca['refid'] == $refid) {
57
				return $ca;
58
			}
59
		}
60
	}
61

    
62
	return false;
63
}
64

    
65
function & lookup_ca_by_subject($subject) {
66
	global $config;
67

    
68
	if (is_array($config['ca'])) {
69
		foreach ($config['ca'] as & $ca) {
70
			$ca_subject = cert_get_subject($ca['crt']);
71
			if ($ca_subject == $subject) {
72
				return $ca;
73
			}
74
		}
75
	}
76

    
77
	return false;
78
}
79

    
80
function & lookup_cert($refid) {
81
	global $config;
82

    
83
	if (is_array($config['cert'])) {
84
		foreach ($config['cert'] as & $cert) {
85
			if ($cert['refid'] == $refid) {
86
				return $cert;
87
			}
88
		}
89
	}
90

    
91
	return false;
92
}
93

    
94
function & lookup_cert_by_name($name) {
95
	global $config;
96
	if (is_array($config['cert'])) {
97
		foreach ($config['cert'] as & $cert) {
98
			if ($cert['descr'] == $name) {
99
				return $cert;
100
			}
101
		}
102
	}
103
}
104

    
105
function & lookup_crl($refid) {
106
	global $config;
107

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

    
116
	return false;
117
}
118

    
119
function ca_chain_array(& $cert) {
120
	if ($cert['caref']) {
121
		$chain = array();
122
		$crt = lookup_ca($cert['caref']);
123
		$chain[] = $crt;
124
		while ($crt) {
125
			$caref = $crt['caref'];
126
			if ($caref) {
127
				$crt = lookup_ca($caref);
128
			} else {
129
				$crt = false;
130
			}
131
			if ($crt) {
132
				$chain[] = $crt;
133
			}
134
		}
135
		return $chain;
136
	}
137
	return false;
138
}
139

    
140
function ca_chain(& $cert) {
141
	if ($cert['caref']) {
142
		$ca = "";
143
		$cas = ca_chain_array($cert);
144
		if (is_array($cas)) {
145
			foreach ($cas as & $ca_cert) {
146
				$ca .= base64_decode($ca_cert['crt']);
147
				$ca .= "\n";
148
			}
149
		}
150
		return $ca;
151
	}
152
	return "";
153
}
154

    
155
function ca_import(& $ca, $str, $key = "", $serial = 0) {
156
	global $config;
157

    
158
	$ca['crt'] = base64_encode($str);
159
	if (!empty($key)) {
160
		$ca['prv'] = base64_encode($key);
161
	}
162
	if (!empty($serial)) {
163
		$ca['serial'] = $serial;
164
	}
165
	$subject = cert_get_subject($str, false);
166
	$issuer = cert_get_issuer($str, false);
167

    
168
	// Find my issuer unless self-signed
169
	if ($issuer <> $subject) {
170
		$issuer_crt =& lookup_ca_by_subject($issuer);
171
		if ($issuer_crt) {
172
			$ca['caref'] = $issuer_crt['refid'];
173
		}
174
	}
175

    
176
	/* Correct if child certificate was loaded first */
177
	if (is_array($config['ca'])) {
178
		foreach ($config['ca'] as & $oca) {
179
			$issuer = cert_get_issuer($oca['crt']);
180
			if ($ca['refid'] <> $oca['refid'] && $issuer == $subject) {
181
				$oca['caref'] = $ca['refid'];
182
			}
183
		}
184
	}
185
	if (is_array($config['cert'])) {
186
		foreach ($config['cert'] as & $cert) {
187
			$issuer = cert_get_issuer($cert['crt']);
188
			if ($issuer == $subject) {
189
				$cert['caref'] = $ca['refid'];
190
			}
191
		}
192
	}
193
	return true;
194
}
195

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

    
198
	$args = array(
199
		"x509_extensions" => "v3_ca",
200
		"digest_alg" => $digest_alg,
201
		"private_key_bits" => (int)$keylen,
202
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
203
		"encrypt_key" => false);
204

    
205
	// generate a new key pair
206
	$res_key = openssl_pkey_new($args);
207
	if (!$res_key) {
208
		return false;
209
	}
210

    
211
	// generate a certificate signing request
212
	$res_csr = openssl_csr_new($dn, $res_key, $args);
213
	if (!$res_csr) {
214
		return false;
215
	}
216

    
217
	// self sign the certificate
218
	$res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args);
219
	if (!$res_crt) {
220
		return false;
221
	}
222

    
223
	// export our certificate data
224
	if (!openssl_pkey_export($res_key, $str_key) ||
225
	    !openssl_x509_export($res_crt, $str_crt)) {
226
		return false;
227
	}
228

    
229
	// return our ca information
230
	$ca['crt'] = base64_encode($str_crt);
231
	$ca['prv'] = base64_encode($str_key);
232
	$ca['serial'] = 0;
233

    
234
	return true;
235
}
236

    
237
function ca_inter_create(& $ca, $keylen, $lifetime, $dn, $caref, $digest_alg = "sha256") {
238
	// Create Intermediate Certificate Authority
239
	$signing_ca =& lookup_ca($caref);
240
	if (!$signing_ca) {
241
		return false;
242
	}
243

    
244
	$signing_ca_res_crt = openssl_x509_read(base64_decode($signing_ca['crt']));
245
	$signing_ca_res_key = openssl_pkey_get_private(array(0 => base64_decode($signing_ca['prv']) , 1 => ""));
246
	if (!$signing_ca_res_crt || !$signing_ca_res_key) {
247
		return false;
248
	}
249
	$signing_ca_serial = ++$signing_ca['serial'];
250

    
251
	$args = array(
252
		"x509_extensions" => "v3_ca",
253
		"digest_alg" => $digest_alg,
254
		"private_key_bits" => (int)$keylen,
255
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
256
		"encrypt_key" => false);
257

    
258
	// generate a new key pair
259
	$res_key = openssl_pkey_new($args);
260
	if (!$res_key) {
261
		return false;
262
	}
263

    
264
	// generate a certificate signing request
265
	$res_csr = openssl_csr_new($dn, $res_key, $args);
266
	if (!$res_csr) {
267
		return false;
268
	}
269

    
270
	// Sign the certificate
271
	$res_crt = openssl_csr_sign($res_csr, $signing_ca_res_crt, $signing_ca_res_key, $lifetime, $args, $signing_ca_serial);
272
	if (!$res_crt) {
273
		return false;
274
	}
275

    
276
	// export our certificate data
277
	if (!openssl_pkey_export($res_key, $str_key) ||
278
	    !openssl_x509_export($res_crt, $str_crt)) {
279
		return false;
280
	}
281

    
282
	// return our ca information
283
	$ca['crt'] = base64_encode($str_crt);
284
	$ca['prv'] = base64_encode($str_key);
285
	$ca['serial'] = 0;
286
	$ca['caref'] = $caref;
287

    
288
	return true;
289
}
290

    
291
function cert_import(& $cert, $crt_str, $key_str) {
292

    
293
	$cert['crt'] = base64_encode($crt_str);
294
	$cert['prv'] = base64_encode($key_str);
295

    
296
	$subject = cert_get_subject($crt_str, false);
297
	$issuer = cert_get_issuer($crt_str, false);
298

    
299
	// Find my issuer unless self-signed
300
	if ($issuer <> $subject) {
301
		$issuer_crt =& lookup_ca_by_subject($issuer);
302
		if ($issuer_crt) {
303
			$cert['caref'] = $issuer_crt['refid'];
304
		}
305
	}
306
	return true;
307
}
308

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

    
311
	$cert['type'] = $type;
312

    
313
	if ($type != "self-signed") {
314
		$cert['caref'] = $caref;
315
		$ca =& lookup_ca($caref);
316
		if (!$ca) {
317
			return false;
318
		}
319

    
320
		$ca_str_crt = base64_decode($ca['crt']);
321
		$ca_str_key = base64_decode($ca['prv']);
322
		$ca_res_crt = openssl_x509_read($ca_str_crt);
323
		$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
324
		if (!$ca_res_key) {
325
			return false;
326
		}
327
		$ca_serial = ++$ca['serial'];
328
	}
329

    
330
	switch ($type) {
331
		case "ca":
332
			$cert_type = "v3_ca";
333
			break;
334
		case "server":
335
		case "self-signed":
336
			$cert_type = "server";
337
			break;
338
		default:
339
			$cert_type = "usr_cert";
340
			break;
341
	}
342

    
343
	// in case of using Subject Alternative Names use other sections (with postfix '_san')
344
	// pass subjectAltName over environment variable 'SAN'
345
	if ($dn['subjectAltName']) {
346
		putenv("SAN={$dn['subjectAltName']}"); // subjectAltName can be set _only_ via configuration file
347
		$cert_type .= '_san';
348
		unset($dn['subjectAltName']);
349
	}
350

    
351
	$args = array(
352
		"x509_extensions" => $cert_type,
353
		"digest_alg" => $digest_alg,
354
		"private_key_bits" => (int)$keylen,
355
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
356
		"encrypt_key" => false);
357

    
358
	// generate a new key pair
359
	$res_key = openssl_pkey_new($args);
360
	if (!$res_key) {
361
		return false;
362
	}
363

    
364
	// If this is a self-signed cert, blank out the CA and sign with the cert's key
365
	if ($type == "self-signed") {
366
		$ca           = null;
367
		$ca_res_crt   = null;
368
		$ca_res_key   = $res_key;
369
		$ca_serial    = 0;
370
		$cert['type'] = "server";
371
	}
372

    
373
	// generate a certificate signing request
374
	$res_csr = openssl_csr_new($dn, $res_key, $args);
375
	if (!$res_csr) {
376
		return false;
377
	}
378

    
379
	// sign the certificate using an internal CA
380
	$res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime,
381
				 $args, $ca_serial);
382
	if (!$res_crt) {
383
		return false;
384
	}
385

    
386
	// export our certificate data
387
	if (!openssl_pkey_export($res_key, $str_key) ||
388
	    !openssl_x509_export($res_crt, $str_crt)) {
389
		return false;
390
	}
391

    
392
	// return our certificate information
393
	$cert['crt'] = base64_encode($str_crt);
394
	$cert['prv'] = base64_encode($str_key);
395

    
396
	return true;
397
}
398

    
399
function csr_generate(& $cert, $keylen, $dn, $digest_alg = "sha256") {
400

    
401
	$args = array(
402
		"x509_extensions" => "v3_req",
403
		"digest_alg" => $digest_alg,
404
		"private_key_bits" => (int)$keylen,
405
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
406
		"encrypt_key" => false);
407

    
408
	// generate a new key pair
409
	$res_key = openssl_pkey_new($args);
410
	if (!$res_key) {
411
		return false;
412
	}
413

    
414
	// generate a certificate signing request
415
	$res_csr = openssl_csr_new($dn, $res_key, $args);
416
	if (!$res_csr) {
417
		return false;
418
	}
419

    
420
	// export our request data
421
	if (!openssl_pkey_export($res_key, $str_key) ||
422
	    !openssl_csr_export($res_csr, $str_csr)) {
423
		return false;
424
	}
425

    
426
	// return our request information
427
	$cert['csr'] = base64_encode($str_csr);
428
	$cert['prv'] = base64_encode($str_key);
429

    
430
	return true;
431
}
432

    
433
function csr_complete(& $cert, $str_crt) {
434

    
435
	// return our request information
436
	$cert['crt'] = base64_encode($str_crt);
437
	unset($cert['csr']);
438

    
439
	return true;
440
}
441

    
442
function csr_get_subject($str_crt, $decode = true) {
443

    
444
	if ($decode) {
445
		$str_crt = base64_decode($str_crt);
446
	}
447

    
448
	$components = openssl_csr_get_subject($str_crt);
449

    
450
	if (empty($components) || !is_array($components)) {
451
		return "unknown";
452
	}
453

    
454
	ksort($components);
455
	foreach ($components as $a => $v) {
456
		if (!strlen($subject)) {
457
			$subject = "{$a}={$v}";
458
		} else {
459
			$subject = "{$a}={$v}, {$subject}";
460
		}
461
	}
462

    
463
	return $subject;
464
}
465

    
466
function cert_get_subject($str_crt, $decode = true) {
467

    
468
	if ($decode) {
469
		$str_crt = base64_decode($str_crt);
470
	}
471

    
472
	$inf_crt = openssl_x509_parse($str_crt);
473
	$components = $inf_crt['subject'];
474

    
475
	if (empty($components) || !is_array($components)) {
476
		return "unknown";
477
	}
478

    
479
	ksort($components);
480
	foreach ($components as $a => $v) {
481
		if (is_array($v)) {
482
			ksort($v);
483
			foreach ($v as $w) {
484
				$asubject = "{$a}={$w}";
485
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
486
			}
487
		} else {
488
			$asubject = "{$a}={$v}";
489
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
490
		}
491
	}
492

    
493
	return $subject;
494
}
495

    
496
function cert_get_subject_array($crt) {
497
	$str_crt = base64_decode($crt);
498
	$inf_crt = openssl_x509_parse($str_crt);
499
	$components = $inf_crt['subject'];
500

    
501
	if (!is_array($components)) {
502
		return;
503
	}
504

    
505
	$subject_array = array();
506

    
507
	foreach ($components as $a => $v) {
508
		$subject_array[] = array('a' => $a, 'v' => $v);
509
	}
510

    
511
	return $subject_array;
512
}
513

    
514
function cert_get_subject_hash($crt) {
515
	$str_crt = base64_decode($crt);
516
	$inf_crt = openssl_x509_parse($str_crt);
517
	return $inf_crt['subject'];
518
}
519

    
520
function cert_get_issuer($str_crt, $decode = true) {
521

    
522
	if ($decode) {
523
		$str_crt = base64_decode($str_crt);
524
	}
525

    
526
	$inf_crt = openssl_x509_parse($str_crt);
527
	$components = $inf_crt['issuer'];
528

    
529
	if (empty($components) || !is_array($components)) {
530
		return "unknown";
531
	}
532

    
533
	ksort($components);
534
	foreach ($components as $a => $v) {
535
		if (is_array($v)) {
536
			ksort($v);
537
			foreach ($v as $w) {
538
				$aissuer = "{$a}={$w}";
539
				$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
540
			}
541
		} else {
542
			$aissuer = "{$a}={$v}";
543
			$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
544
		}
545
	}
546

    
547
	return $issuer;
548
}
549

    
550
/* this function works on x509 (crt), rsa key (prv), and req(csr) */
551
function cert_get_modulus($str_crt, $decode = true, $type = "crt") {
552
	if ($decode) {
553
		$str_crt = base64_decode($str_crt);
554
	}
555

    
556
	$modulus = "";
557
	if (in_array($type, array("crt", "prv", "csr"))) {
558
		$type = str_replace(array("crt", "prv", "csr"), array("x509", "rsa", "req"), $type);
559
		$modulus = exec("echo \"{$str_crt}\" | openssl {$type} -noout -modulus");
560
	}
561
	return $modulus;
562
}
563
function csr_get_modulus($str_crt, $decode = true) {
564
	return cert_get_modulus($str_crt, $decode, "csr");
565
}
566

    
567
function cert_get_purpose($str_crt, $decode = true) {
568
	if ($decode) {
569
		$str_crt = base64_decode($str_crt);
570
	}
571
	$crt_details = openssl_x509_parse($str_crt);
572
	$purpose = array();
573
	$purpose['ca'] = (stristr($crt_details['extensions']['basicConstraints'], 'CA:TRUE') === false) ? 'No': 'Yes';
574
	$purpose['server'] = ($crt_details['extensions']['nsCertType'] == "SSL Server") ? 'Yes': 'No';
575
	return $purpose;
576
}
577

    
578
function cert_get_dates($str_crt, $decode = true) {
579
	if ($decode) {
580
		$str_crt = base64_decode($str_crt);
581
	}
582
	$crt_details = openssl_x509_parse($str_crt);
583
	if ($crt_details['validFrom_time_t'] > 0) {
584
		$start = date('r', $crt_details['validFrom_time_t']);
585
	}
586
	if ($crt_details['validTo_time_t'] > 0) {
587
		$end = date('r', $crt_details['validTo_time_t']);
588
	}
589
	return array($start, $end);
590
}
591

    
592
function cert_get_serial($str_crt, $decode = true) {
593
	if ($decode) {
594
		$str_crt = base64_decode($str_crt);
595
	}
596
	$crt_details = openssl_x509_parse($str_crt);
597
	if (isset($crt_details['serialNumber']) && !empty($crt_details['serialNumber'])) {
598
		return $crt_details['serialNumber'];
599
	} else {
600
		return NULL;
601
	}
602
}
603

    
604
function prv_get_modulus($str_crt, $decode = true) {
605
	return cert_get_modulus($str_crt, $decode, "prv");
606
}
607

    
608
function is_user_cert($certref) {
609
	global $config;
610
	if (!is_array($config['system']['user'])) {
611
		return;
612
	}
613
	foreach ($config['system']['user'] as $user) {
614
		if (!is_array($user['cert'])) {
615
			continue;
616
		}
617
		foreach ($user['cert'] as $cert) {
618
			if ($certref == $cert) {
619
				return true;
620
			}
621
		}
622
	}
623
	return false;
624
}
625

    
626
function is_openvpn_server_cert($certref) {
627
	global $config;
628
	if (!is_array($config['openvpn']['openvpn-server'])) {
629
		return;
630
	}
631
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
632
		if ($ovpns['certref'] == $certref) {
633
			return true;
634
		}
635
	}
636
	return false;
637
}
638

    
639
function is_openvpn_client_cert($certref) {
640
	global $config;
641
	if (!is_array($config['openvpn']['openvpn-client'])) {
642
		return;
643
	}
644
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
645
		if ($ovpnc['certref'] == $certref) {
646
			return true;
647
		}
648
	}
649
	return false;
650
}
651

    
652
function is_ipsec_cert($certref) {
653
	global $config;
654
	if (!is_array($config['ipsec']['phase1'])) {
655
		return;
656
	}
657
	foreach ($config['ipsec']['phase1'] as $ipsec) {
658
		if ($ipsec['certref'] == $certref) {
659
			return true;
660
		}
661
	}
662
	return false;
663
}
664

    
665
function is_webgui_cert($certref) {
666
	global $config;
667
	if (($config['system']['webgui']['ssl-certref'] == $certref) &&
668
	    ($config['system']['webgui']['protocol'] != "http")) {
669
		return true;
670
	}
671
}
672

    
673
function is_captiveportal_cert($certref) {
674
	global $config;
675
	if (!is_array($config['captiveportal'])) {
676
		return;
677
	}
678
	foreach ($config['captiveportal'] as $portal) {
679
		if (isset($portal['enable']) && isset($portal['httpslogin']) && ($portal['certref'] == $certref)) {
680
			return true;
681
		}
682
	}
683
	return false;
684
}
685

    
686
function cert_in_use($certref) {
687
	return (is_webgui_cert($certref) ||
688
		is_user_cert($certref) ||
689
		is_openvpn_server_cert($certref) ||
690
		is_openvpn_client_cert($certref) ||
691
		is_ipsec_cert($certref) ||
692
		is_captiveportal_cert($certref));
693
}
694

    
695
function crl_create(& $crl, $caref, $name, $serial = 0, $lifetime = 9999) {
696
	global $config;
697
	$ca =& lookup_ca($caref);
698
	if (!$ca) {
699
		return false;
700
	}
701
	$crl['descr'] = $name;
702
	$crl['caref'] = $caref;
703
	$crl['serial'] = $serial;
704
	$crl['lifetime'] = $lifetime;
705
	$crl['cert'] = array();
706
	$crl_res = crl_update($crl);
707
	$config['crl'][] = $crl;
708
	return $crl_res;
709
}
710

    
711
function crl_update(& $crl) {
712
	global $config;
713
	$ca =& lookup_ca($crl['caref']);
714
	if (!$ca) {
715
		return false;
716
	}
717
	// If we have text but no certs, it was imported and cannot be updated.
718
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert']))) {
719
		return false;
720
	}
721
	$crl['serial']++;
722
	$ca_str_crt = base64_decode($ca['crt']);
723
	$ca_str_key = base64_decode($ca['prv']);
724
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
725
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
726
		foreach ($crl['cert'] as $cert) {
727
			openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
728
		}
729
	}
730
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
731
	$crl['text'] = base64_encode($crl_text);
732
	return $crl_res;
733
}
734

    
735
function cert_revoke($cert, & $crl, $reason = OCSP_REVOKED_STATUS_UNSPECIFIED) {
736
	global $config;
737
	if (is_cert_revoked($cert, $crl['refid'])) {
738
		return true;
739
	}
740
	// If we have text but no certs, it was imported and cannot be updated.
741
	if (!is_crl_internal($crl)) {
742
		return false;
743
	}
744
	$cert["reason"] = $reason;
745
	$cert["revoke_time"] = time();
746
	$crl["cert"][] = $cert;
747
	crl_update($crl);
748
	return true;
749
}
750

    
751
function cert_unrevoke($cert, & $crl) {
752
	global $config;
753
	if (!is_crl_internal($crl)) {
754
		return false;
755
	}
756
	foreach ($crl['cert'] as $id => $rcert) {
757
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
758
			unset($crl['cert'][$id]);
759
			if (count($crl['cert']) == 0) {
760
				// Protect against accidentally switching the type to imported, for older CRLs
761
				if (!isset($crl['method'])) {
762
					$crl['method'] = "internal";
763
				}
764
				crl_update($crl);
765
			} else {
766
				crl_update($crl);
767
			}
768
			return true;
769
		}
770
	}
771
	return false;
772
}
773

    
774
/* Compare two certificates to see if they match. */
775
function cert_compare($cert1, $cert2) {
776
	/* Ensure two certs are identical by first checking that their issuers match, then
777
		subjects, then serial numbers, and finally the moduli. Anything less strict
778
		could accidentally count two similar, but different, certificates as
779
		being identical. */
780
	$c1 = base64_decode($cert1['crt']);
781
	$c2 = base64_decode($cert2['crt']);
782
	if ((cert_get_issuer($c1, false) == cert_get_issuer($c2, false)) &&
783
	    (cert_get_subject($c1, false) == cert_get_subject($c2, false)) &&
784
	    (cert_get_serial($c1, false) == cert_get_serial($c2, false)) &&
785
	    (cert_get_modulus($c1, false) == cert_get_modulus($c2, false))) {
786
		return true;
787
	}
788
	return false;
789
}
790

    
791
function is_cert_revoked($cert, $crlref = "") {
792
	global $config;
793
	if (!is_array($config['crl'])) {
794
		return false;
795
	}
796

    
797
	if (!empty($crlref)) {
798
		$crl = lookup_crl($crlref);
799
		if (!is_array($crl['cert'])) {
800
			return false;
801
		}
802
		foreach ($crl['cert'] as $rcert) {
803
			if (cert_compare($rcert, $cert)) {
804
				return true;
805
			}
806
		}
807
	} else {
808
		foreach ($config['crl'] as $crl) {
809
			if (!is_array($crl['cert'])) {
810
				continue;
811
			}
812
			foreach ($crl['cert'] as $rcert) {
813
				if (cert_compare($rcert, $cert)) {
814
					return true;
815
				}
816
			}
817
		}
818
	}
819
	return false;
820
}
821

    
822
function is_openvpn_server_crl($crlref) {
823
	global $config;
824
	if (!is_array($config['openvpn']['openvpn-server'])) {
825
		return;
826
	}
827
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
828
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref)) {
829
			return true;
830
		}
831
	}
832
	return false;
833
}
834

    
835
// Keep this general to allow for future expansion. See cert_in_use() above.
836
function crl_in_use($crlref) {
837
	return (is_openvpn_server_crl($crlref));
838
}
839

    
840
function is_crl_internal($crl) {
841
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
842
}
843

    
844
function cert_get_cn($crt, $isref = false) {
845
	/* If this is a certref, not an actual cert, look up the cert first */
846
	if ($isref) {
847
		$cert = lookup_cert($crt);
848
		/* If it's not a valid cert, bail. */
849
		if (!(is_array($cert) && !empty($cert['crt']))) {
850
			return "";
851
		}
852
		$cert = $cert['crt'];
853
	} else {
854
		$cert = $crt;
855
	}
856
	$sub = cert_get_subject_array($cert);
857
	if (is_array($sub)) {
858
		foreach ($sub as $s) {
859
			if (strtoupper($s['a']) == "CN") {
860
				return $s['v'];
861
			}
862
		}
863
	}
864
	return "";
865
}
866

    
867
?>
(8-8/65)