Project

General

Profile

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

    
9
	Redistribution and use in source and binary forms, with or without
10
	modification, 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 the
17
	   documentation and/or other materials provided with the distribution.
18

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

    
30
	pfSense_MODULE:	certificate_manager
31
*/
32

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

    
35
require_once("functions.inc");
36

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

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

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

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

    
63
	return false;
64
}
65

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

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

    
78
	return false;
79
}
80

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

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

    
92
	return false;
93
}
94

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

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

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

    
117
	return false;
118
}
119

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

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

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

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

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

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

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

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

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

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

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

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

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

    
235
	return true;
236
}
237

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

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

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

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

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

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

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

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

    
289
	return true;
290
}
291

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
397
	return true;
398
}
399

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

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

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

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

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

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

    
431
	return true;
432
}
433

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

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

    
440
	return true;
441
}
442

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

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

    
449
	$components = openssl_csr_get_subject($str_crt);
450

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

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

    
464
	return $subject;
465
}
466

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

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

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

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

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

    
494
	return $subject;
495
}
496

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

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

    
506
	$subject_array = array();
507

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

    
512
	return $subject_array;
513
}
514

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

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

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

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

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

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

    
548
	return $issuer;
549
}
550

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
868
?>
(8-8/68)