Project

General

Profile

Download (24.2 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
 * 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
define("OPEN_SSL_CONF_PATH", "/etc/ssl/openssl.cnf");
24

    
25
require_once("functions.inc");
26

    
27
global $openssl_digest_algs;
28
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
29

    
30
global $openssl_crl_status;
31
$openssl_crl_status = array(
32
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
33
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
34
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
35
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
36
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
37
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
38
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
39
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
40
);
41

    
42
function & lookup_ca($refid) {
43
	global $config;
44

    
45
	if (is_array($config['ca'])) {
46
		foreach ($config['ca'] as & $ca) {
47
			if ($ca['refid'] == $refid) {
48
				return $ca;
49
			}
50
		}
51
	}
52

    
53
	return false;
54
}
55

    
56
function & lookup_ca_by_subject($subject) {
57
	global $config;
58

    
59
	if (is_array($config['ca'])) {
60
		foreach ($config['ca'] as & $ca) {
61
			$ca_subject = cert_get_subject($ca['crt']);
62
			if ($ca_subject == $subject) {
63
				return $ca;
64
			}
65
		}
66
	}
67

    
68
	return false;
69
}
70

    
71
function & lookup_cert($refid) {
72
	global $config;
73

    
74
	if (is_array($config['cert'])) {
75
		foreach ($config['cert'] as & $cert) {
76
			if ($cert['refid'] == $refid) {
77
				return $cert;
78
			}
79
		}
80
	}
81

    
82
	return false;
83
}
84

    
85
function & lookup_cert_by_name($name) {
86
	global $config;
87
	if (is_array($config['cert'])) {
88
		foreach ($config['cert'] as & $cert) {
89
			if ($cert['descr'] == $name) {
90
				return $cert;
91
			}
92
		}
93
	}
94
}
95

    
96
function & lookup_crl($refid) {
97
	global $config;
98

    
99
	if (is_array($config['crl'])) {
100
		foreach ($config['crl'] as & $crl) {
101
			if ($crl['refid'] == $refid) {
102
				return $crl;
103
			}
104
		}
105
	}
106

    
107
	return false;
108
}
109

    
110
function ca_chain_array(& $cert) {
111
	if ($cert['caref']) {
112
		$chain = array();
113
		$crt = lookup_ca($cert['caref']);
114
		$chain[] = $crt;
115
		while ($crt) {
116
			$caref = $crt['caref'];
117
			if ($caref) {
118
				$crt = lookup_ca($caref);
119
			} else {
120
				$crt = false;
121
			}
122
			if ($crt) {
123
				$chain[] = $crt;
124
			}
125
		}
126
		return $chain;
127
	}
128
	return false;
129
}
130

    
131
function ca_chain(& $cert) {
132
	if ($cert['caref']) {
133
		$ca = "";
134
		$cas = ca_chain_array($cert);
135
		if (is_array($cas)) {
136
			foreach ($cas as & $ca_cert) {
137
				$ca .= base64_decode($ca_cert['crt']);
138
				$ca .= "\n";
139
			}
140
		}
141
		return $ca;
142
	}
143
	return "";
144
}
145

    
146
function ca_import(& $ca, $str, $key = "", $serial = "") {
147
	global $config;
148

    
149
	$ca['crt'] = base64_encode($str);
150
	if (!empty($key)) {
151
		$ca['prv'] = base64_encode($key);
152
	}
153
	if (empty($serial)) {
154
		$ca['serial'] = 0;
155
	} else {
156
		$ca['serial'] = $serial;
157
	}
158
	$subject = cert_get_subject($str, false);
159
	$issuer = cert_get_issuer($str, false);
160

    
161
	// Find my issuer unless self-signed
162
	if ($issuer <> $subject) {
163
		$issuer_crt =& lookup_ca_by_subject($issuer);
164
		if ($issuer_crt) {
165
			$ca['caref'] = $issuer_crt['refid'];
166
		}
167
	}
168

    
169
	/* Correct if child certificate was loaded first */
170
	if (is_array($config['ca'])) {
171
		foreach ($config['ca'] as & $oca) {
172
			$issuer = cert_get_issuer($oca['crt']);
173
			if ($ca['refid'] <> $oca['refid'] && $issuer == $subject) {
174
				$oca['caref'] = $ca['refid'];
175
			}
176
		}
177
	}
178
	if (is_array($config['cert'])) {
179
		foreach ($config['cert'] as & $cert) {
180
			$issuer = cert_get_issuer($cert['crt']);
181
			if ($issuer == $subject) {
182
				$cert['caref'] = $ca['refid'];
183
			}
184
		}
185
	}
186
	return true;
187
}
188

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

    
191
	$args = array(
192
		"x509_extensions" => "v3_ca",
193
		"digest_alg" => $digest_alg,
194
		"private_key_bits" => (int)$keylen,
195
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
196
		"encrypt_key" => false);
197

    
198
	// generate a new key pair
199
	$res_key = openssl_pkey_new($args);
200
	if (!$res_key) {
201
		return false;
202
	}
203

    
204
	// generate a certificate signing request
205
	$res_csr = openssl_csr_new($dn, $res_key, $args);
206
	if (!$res_csr) {
207
		return false;
208
	}
209

    
210
	// self sign the certificate
211
	$res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args);
212
	if (!$res_crt) {
213
		return false;
214
	}
215

    
216
	// export our certificate data
217
	if (!openssl_pkey_export($res_key, $str_key) ||
218
	    !openssl_x509_export($res_crt, $str_crt)) {
219
		return false;
220
	}
221

    
222
	// return our ca information
223
	$ca['crt'] = base64_encode($str_crt);
224
	$ca['prv'] = base64_encode($str_key);
225
	$ca['serial'] = 0;
226

    
227
	return true;
228
}
229

    
230
function ca_inter_create(& $ca, $keylen, $lifetime, $dn, $caref, $digest_alg = "sha256") {
231
	// Create Intermediate Certificate Authority
232
	$signing_ca =& lookup_ca($caref);
233
	if (!$signing_ca) {
234
		return false;
235
	}
236

    
237
	$signing_ca_res_crt = openssl_x509_read(base64_decode($signing_ca['crt']));
238
	$signing_ca_res_key = openssl_pkey_get_private(array(0 => base64_decode($signing_ca['prv']) , 1 => ""));
239
	if (!$signing_ca_res_crt || !$signing_ca_res_key) {
240
		return false;
241
	}
242
	$signing_ca_serial = ++$signing_ca['serial'];
243

    
244
	$args = array(
245
		"x509_extensions" => "v3_ca",
246
		"digest_alg" => $digest_alg,
247
		"private_key_bits" => (int)$keylen,
248
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
249
		"encrypt_key" => false);
250

    
251
	// generate a new key pair
252
	$res_key = openssl_pkey_new($args);
253
	if (!$res_key) {
254
		return false;
255
	}
256

    
257
	// generate a certificate signing request
258
	$res_csr = openssl_csr_new($dn, $res_key, $args);
259
	if (!$res_csr) {
260
		return false;
261
	}
262

    
263
	// Sign the certificate
264
	$res_crt = openssl_csr_sign($res_csr, $signing_ca_res_crt, $signing_ca_res_key, $lifetime, $args, $signing_ca_serial);
265
	if (!$res_crt) {
266
		return false;
267
	}
268

    
269
	// export our certificate data
270
	if (!openssl_pkey_export($res_key, $str_key) ||
271
	    !openssl_x509_export($res_crt, $str_crt)) {
272
		return false;
273
	}
274

    
275
	// return our ca information
276
	$ca['crt'] = base64_encode($str_crt);
277
	$ca['prv'] = base64_encode($str_key);
278
	$ca['serial'] = 0;
279
	$ca['caref'] = $caref;
280

    
281
	return true;
282
}
283

    
284
function cert_import(& $cert, $crt_str, $key_str) {
285

    
286
	$cert['crt'] = base64_encode($crt_str);
287
	$cert['prv'] = base64_encode($key_str);
288

    
289
	$subject = cert_get_subject($crt_str, false);
290
	$issuer = cert_get_issuer($crt_str, false);
291

    
292
	// Find my issuer unless self-signed
293
	if ($issuer <> $subject) {
294
		$issuer_crt =& lookup_ca_by_subject($issuer);
295
		if ($issuer_crt) {
296
			$cert['caref'] = $issuer_crt['refid'];
297
		}
298
	}
299
	return true;
300
}
301

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

    
304
	$cert['type'] = $type;
305

    
306
	if ($type != "self-signed") {
307
		$cert['caref'] = $caref;
308
		$ca =& lookup_ca($caref);
309
		if (!$ca) {
310
			return false;
311
		}
312

    
313
		$ca_str_crt = base64_decode($ca['crt']);
314
		$ca_str_key = base64_decode($ca['prv']);
315
		$ca_res_crt = openssl_x509_read($ca_str_crt);
316
		$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
317
		if (!$ca_res_key) {
318
			return false;
319
		}
320
		if (empty($ca['serial'])) {
321
			$ca['serial'] = 0;
322
		}
323
		$ca_serial = ++$ca['serial'];
324
	}
325

    
326
	switch ($type) {
327
		case "ca":
328
			$cert_type = "v3_ca";
329
			break;
330
		case "server":
331
		case "self-signed":
332
			$cert_type = "server";
333
			break;
334
		default:
335
			$cert_type = "usr_cert";
336
			break;
337
	}
338

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

    
347
	$args = array(
348
		"x509_extensions" => $cert_type,
349
		"digest_alg" => $digest_alg,
350
		"private_key_bits" => (int)$keylen,
351
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
352
		"encrypt_key" => false);
353

    
354
	// generate a new key pair
355
	$res_key = openssl_pkey_new($args);
356
	if (!$res_key) {
357
		return false;
358
	}
359

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

    
369
	// generate a certificate signing request
370
	$res_csr = openssl_csr_new($dn, $res_key, $args);
371
	if (!$res_csr) {
372
		return false;
373
	}
374

    
375
	// sign the certificate using an internal CA
376
	$res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime,
377
				 $args, $ca_serial);
378
	if (!$res_crt) {
379
		return false;
380
	}
381

    
382
	// export our certificate data
383
	if (!openssl_pkey_export($res_key, $str_key) ||
384
	    !openssl_x509_export($res_crt, $str_crt)) {
385
		return false;
386
	}
387

    
388
	// return our certificate information
389
	$cert['crt'] = base64_encode($str_crt);
390
	$cert['prv'] = base64_encode($str_key);
391

    
392
	return true;
393
}
394

    
395
function csr_generate(& $cert, $keylen, $dn, $digest_alg = "sha256") {
396

    
397
	$args = array(
398
		"x509_extensions" => "v3_req",
399
		"digest_alg" => $digest_alg,
400
		"private_key_bits" => (int)$keylen,
401
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
402
		"encrypt_key" => false);
403

    
404
	// generate a new key pair
405
	$res_key = openssl_pkey_new($args);
406
	if (!$res_key) {
407
		return false;
408
	}
409

    
410
	// generate a certificate signing request
411
	$res_csr = openssl_csr_new($dn, $res_key, $args);
412
	if (!$res_csr) {
413
		return false;
414
	}
415

    
416
	// export our request data
417
	if (!openssl_pkey_export($res_key, $str_key) ||
418
	    !openssl_csr_export($res_csr, $str_csr)) {
419
		return false;
420
	}
421

    
422
	// return our request information
423
	$cert['csr'] = base64_encode($str_csr);
424
	$cert['prv'] = base64_encode($str_key);
425

    
426
	return true;
427
}
428

    
429
function csr_complete(& $cert, $str_crt) {
430
	$str_key = base64_decode($cert['prv']);
431
	cert_import($cert, $str_crt, $str_key);
432
	unset($cert['csr']);
433
	return true;
434
}
435

    
436
function csr_get_subject($str_crt, $decode = true) {
437

    
438
	if ($decode) {
439
		$str_crt = base64_decode($str_crt);
440
	}
441

    
442
	$components = openssl_csr_get_subject($str_crt);
443

    
444
	if (empty($components) || !is_array($components)) {
445
		return "unknown";
446
	}
447

    
448
	ksort($components);
449
	foreach ($components as $a => $v) {
450
		if (!strlen($subject)) {
451
			$subject = "{$a}={$v}";
452
		} else {
453
			$subject = "{$a}={$v}, {$subject}";
454
		}
455
	}
456

    
457
	return $subject;
458
}
459

    
460
function cert_get_subject($str_crt, $decode = true) {
461

    
462
	if ($decode) {
463
		$str_crt = base64_decode($str_crt);
464
	}
465

    
466
	$inf_crt = openssl_x509_parse($str_crt);
467
	$components = $inf_crt['subject'];
468

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

    
473
	ksort($components);
474
	foreach ($components as $a => $v) {
475
		if (is_array($v)) {
476
			ksort($v);
477
			foreach ($v as $w) {
478
				$asubject = "{$a}={$w}";
479
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
480
			}
481
		} else {
482
			$asubject = "{$a}={$v}";
483
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
484
		}
485
	}
486

    
487
	return $subject;
488
}
489

    
490
function cert_get_subject_array($crt) {
491
	$str_crt = base64_decode($crt);
492
	$inf_crt = openssl_x509_parse($str_crt);
493
	$components = $inf_crt['subject'];
494

    
495
	if (!is_array($components)) {
496
		return;
497
	}
498

    
499
	$subject_array = array();
500

    
501
	foreach ($components as $a => $v) {
502
		$subject_array[] = array('a' => $a, 'v' => $v);
503
	}
504

    
505
	return $subject_array;
506
}
507

    
508
function cert_get_subject_hash($crt) {
509
	$str_crt = base64_decode($crt);
510
	$inf_crt = openssl_x509_parse($str_crt);
511
	return $inf_crt['subject'];
512
}
513

    
514
function cert_get_sans($str_crt, $decode = true) {
515
	if ($decode) {
516
		$str_crt = base64_decode($str_crt);
517
	}
518
	$sans = array();
519
	$crt_details = openssl_x509_parse($str_crt);
520
	if (!empty($crt_details['extensions']['subjectAltName'])) {
521
		$sans = explode(',', $crt_details['extensions']['subjectAltName']);
522
	}
523
	return $sans;
524
}
525

    
526
function cert_get_issuer($str_crt, $decode = true) {
527

    
528
	if ($decode) {
529
		$str_crt = base64_decode($str_crt);
530
	}
531

    
532
	$inf_crt = openssl_x509_parse($str_crt);
533
	$components = $inf_crt['issuer'];
534

    
535
	if (empty($components) || !is_array($components)) {
536
		return "unknown";
537
	}
538

    
539
	ksort($components);
540
	foreach ($components as $a => $v) {
541
		if (is_array($v)) {
542
			ksort($v);
543
			foreach ($v as $w) {
544
				$aissuer = "{$a}={$w}";
545
				$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
546
			}
547
		} else {
548
			$aissuer = "{$a}={$v}";
549
			$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
550
		}
551
	}
552

    
553
	return $issuer;
554
}
555

    
556
/* Works for both RSA and ECC (crt) and key (prv) */
557
function cert_get_publickey($str_crt, $decode = true, $type = "crt") {
558
	if ($decode) {
559
		$str_crt = base64_decode($str_crt);
560
	}
561
	switch ($type) {
562
		case 'prv':
563
			exec("echo \"{$str_crt}\" | openssl pkey -pubout", $out);
564
			break;
565
		case 'crt':
566
			exec("echo \"{$str_crt}\" | openssl x509 -inform pem -noout -pubkey", $out);
567
			break;
568
		case 'csr':
569
			exec("echo \"{$str_crt}\" | openssl req -inform pem -noout -pubkey", $out);
570
			break;
571
		default:
572
			$out = array();
573
			break;
574
	}
575
	return implode("\n", $out);
576
}
577

    
578
function cert_get_purpose($str_crt, $decode = true) {
579
	$extended_oids = array(
580
		"1.3.6.1.5.5.8.2.2" => "IP Security IKE Intermediate",
581
	);
582
	if ($decode) {
583
		$str_crt = base64_decode($str_crt);
584
	}
585
	$crt_details = openssl_x509_parse($str_crt);
586
	$purpose = array();
587
	if (!empty($crt_details['extensions']['keyUsage'])) {
588
		$purpose['ku'] = explode(',', $crt_details['extensions']['keyUsage']);
589
		foreach ($purpose['ku'] as & $ku) {
590
			$ku = trim($ku);
591
			if (array_key_exists($ku, $extended_oids)) {
592
				$ku = $extended_oids[$ku];
593
			}
594
		}
595
	} else {
596
		$purpose['ku'] = array();
597
	}
598
	if (!empty($crt_details['extensions']['extendedKeyUsage'])) {
599
		$purpose['eku'] = explode(',', $crt_details['extensions']['extendedKeyUsage']);
600
		foreach ($purpose['eku'] as & $eku) {
601
			$eku = trim($eku);
602
			if (array_key_exists($eku, $extended_oids)) {
603
				$eku = $extended_oids[$eku];
604
			}
605
		}
606
	} else {
607
		$purpose['eku'] = array();
608
	}
609
	$purpose['ca'] = (stristr($crt_details['extensions']['basicConstraints'], 'CA:TRUE') === false) ? 'No': 'Yes';
610
	$purpose['server'] = (in_array('TLS Web Server Authentication', $purpose['eku'])) ? 'Yes': 'No';
611

    
612
	return $purpose;
613
}
614

    
615
function cert_get_dates($str_crt, $decode = true) {
616
	if ($decode) {
617
		$str_crt = base64_decode($str_crt);
618
	}
619
	$crt_details = openssl_x509_parse($str_crt);
620
	if ($crt_details['validFrom_time_t'] > 0) {
621
		$start = date('r', $crt_details['validFrom_time_t']);
622
	}
623
	if ($crt_details['validTo_time_t'] > 0) {
624
		$end = date('r', $crt_details['validTo_time_t']);
625
	}
626
	return array($start, $end);
627
}
628

    
629
function cert_get_serial($str_crt, $decode = true) {
630
	if ($decode) {
631
		$str_crt = base64_decode($str_crt);
632
	}
633
	$crt_details = openssl_x509_parse($str_crt);
634
	if (isset($crt_details['serialNumber']) && !empty($crt_details['serialNumber'])) {
635
		return $crt_details['serialNumber'];
636
	} else {
637
		return NULL;
638
	}
639
}
640

    
641
function is_openvpn_server_ca($caref) {
642
	global $config;
643
	if (!is_array($config['openvpn']['openvpn-server'])) {
644
		return;
645
	}
646
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
647
		if ($ovpns['caref'] == $caref) {
648
			return true;
649
		}
650
	}
651
	return false;
652
}
653

    
654
function is_openvpn_client_ca($caref) {
655
	global $config;
656
	if (!is_array($config['openvpn']['openvpn-client'])) {
657
		return;
658
	}
659
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
660
		if ($ovpnc['caref'] == $caref) {
661
			return true;
662
		}
663
	}
664
	return false;
665
}
666

    
667
function is_ipsec_peer_ca($caref) {
668
	global $config;
669
	if (!is_array($config['ipsec']['phase1'])) {
670
		return;
671
	}
672
	foreach ($config['ipsec']['phase1'] as $ipsec) {
673
		if ($ipsec['caref'] == $caref) {
674
			return true;
675
		}
676
	}
677
	return false;
678
}
679

    
680
function is_ldap_peer_ca($caref) {
681
	global $config;
682
	if (!is_array($config['system']['authserver'])) {
683
		return;
684
	}
685
	foreach ($config['system']['authserver'] as $authserver) {
686
		if ($authserver['ldap_caref'] == $caref) {
687
			return true;
688
		}
689
	}
690
	return false;
691
}
692

    
693
function ca_in_use($caref) {
694
	return (is_openvpn_server_ca($caref) ||
695
		is_openvpn_client_ca($caref) ||
696
		is_ipsec_peer_ca($caref) ||
697
		is_ldap_peer_ca($caref));
698
}
699

    
700
function is_user_cert($certref) {
701
	global $config;
702
	if (!is_array($config['system']['user'])) {
703
		return;
704
	}
705
	foreach ($config['system']['user'] as $user) {
706
		if (!is_array($user['cert'])) {
707
			continue;
708
		}
709
		foreach ($user['cert'] as $cert) {
710
			if ($certref == $cert) {
711
				return true;
712
			}
713
		}
714
	}
715
	return false;
716
}
717

    
718
function is_openvpn_server_cert($certref) {
719
	global $config;
720
	if (!is_array($config['openvpn']['openvpn-server'])) {
721
		return;
722
	}
723
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
724
		if ($ovpns['certref'] == $certref) {
725
			return true;
726
		}
727
	}
728
	return false;
729
}
730

    
731
function is_openvpn_client_cert($certref) {
732
	global $config;
733
	if (!is_array($config['openvpn']['openvpn-client'])) {
734
		return;
735
	}
736
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
737
		if ($ovpnc['certref'] == $certref) {
738
			return true;
739
		}
740
	}
741
	return false;
742
}
743

    
744
function is_ipsec_cert($certref) {
745
	global $config;
746
	if (!is_array($config['ipsec']['phase1'])) {
747
		return;
748
	}
749
	foreach ($config['ipsec']['phase1'] as $ipsec) {
750
		if ($ipsec['certref'] == $certref) {
751
			return true;
752
		}
753
	}
754
	return false;
755
}
756

    
757
function is_webgui_cert($certref) {
758
	global $config;
759
	if (($config['system']['webgui']['ssl-certref'] == $certref) &&
760
	    ($config['system']['webgui']['protocol'] != "http")) {
761
		return true;
762
	}
763
}
764

    
765
function is_package_cert($certref) {
766
	$pluginparams = array();
767
	$pluginparams['type'] = 'certificates';
768
	$pluginparams['event'] = 'used_certificates';
769

    
770
	$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
771

    
772
	/* Check if any package is using certificate */
773
	foreach ($certificates_used_by_packages as $name => $package) {
774
		if (is_array($package['certificatelist'][$certref]) &&
775
		    isset($package['certificatelist'][$certref]) > 0) {
776
			return true;
777
		}
778
	}
779
}
780

    
781
function is_captiveportal_cert($certref) {
782
	global $config;
783
	if (!is_array($config['captiveportal'])) {
784
		return;
785
	}
786
	foreach ($config['captiveportal'] as $portal) {
787
		if (isset($portal['enable']) && isset($portal['httpslogin']) && ($portal['certref'] == $certref)) {
788
			return true;
789
		}
790
	}
791
	return false;
792
}
793

    
794
function cert_in_use($certref) {
795

    
796
	return (is_webgui_cert($certref) ||
797
		is_user_cert($certref) ||
798
		is_openvpn_server_cert($certref) ||
799
		is_openvpn_client_cert($certref) ||
800
		is_ipsec_cert($certref) ||
801
		is_captiveportal_cert($certref) ||
802
		is_package_cert($certref));
803
}
804

    
805
function cert_usedby_description($refid, $certificates_used_by_packages) {
806
	$result = "";
807
	if (is_array($certificates_used_by_packages)) {
808
		foreach ($certificates_used_by_packages as $name => $package) {
809
			if (isset($package['certificatelist'][$refid])) {
810
				$hint = "" ;
811
				if (is_array($package['certificatelist'][$refid])) {
812
					foreach ($package['certificatelist'][$refid] as $cert_used) {
813
						$hint = $hint . $cert_used['usedby']."\n";
814
					}
815
				}
816
				$count = count($package['certificatelist'][$refid]);
817
				$result .= "<div title='".htmlspecialchars($hint)."'>";
818
				$result .= htmlspecialchars($package['pkgname'])." ($count)<br />";
819
				$result .= "</div>";
820
			}
821
		}
822
	}
823
	return $result;
824
}
825

    
826
function crl_create(& $crl, $caref, $name, $serial = 0, $lifetime = 9999) {
827
	global $config;
828
	$ca =& lookup_ca($caref);
829
	if (!$ca) {
830
		return false;
831
	}
832
	$crl['descr'] = $name;
833
	$crl['caref'] = $caref;
834
	$crl['serial'] = $serial;
835
	$crl['lifetime'] = $lifetime;
836
	$crl['cert'] = array();
837
	$crl_res = crl_update($crl);
838
	$config['crl'][] = $crl;
839
	return $crl_res;
840
}
841

    
842
function crl_update(& $crl) {
843
	global $config;
844
	$ca =& lookup_ca($crl['caref']);
845
	if (!$ca) {
846
		return false;
847
	}
848
	// If we have text but no certs, it was imported and cannot be updated.
849
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert']))) {
850
		return false;
851
	}
852
	$crl['serial']++;
853
	$ca_str_crt = base64_decode($ca['crt']);
854
	$ca_str_key = base64_decode($ca['prv']);
855
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
856
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
857
		foreach ($crl['cert'] as $cert) {
858
			openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
859
		}
860
	}
861
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
862
	$crl['text'] = base64_encode($crl_text);
863
	return $crl_res;
864
}
865

    
866
function cert_revoke($cert, & $crl, $reason = OCSP_REVOKED_STATUS_UNSPECIFIED) {
867
	global $config;
868
	if (is_cert_revoked($cert, $crl['refid'])) {
869
		return true;
870
	}
871
	// If we have text but no certs, it was imported and cannot be updated.
872
	if (!is_crl_internal($crl)) {
873
		return false;
874
	}
875
	$cert["reason"] = $reason;
876
	$cert["revoke_time"] = time();
877
	$crl["cert"][] = $cert;
878
	crl_update($crl);
879
	return true;
880
}
881

    
882
function cert_unrevoke($cert, & $crl) {
883
	global $config;
884
	if (!is_crl_internal($crl)) {
885
		return false;
886
	}
887
	foreach ($crl['cert'] as $id => $rcert) {
888
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
889
			unset($crl['cert'][$id]);
890
			if (count($crl['cert']) == 0) {
891
				// Protect against accidentally switching the type to imported, for older CRLs
892
				if (!isset($crl['method'])) {
893
					$crl['method'] = "internal";
894
				}
895
				crl_update($crl);
896
			} else {
897
				crl_update($crl);
898
			}
899
			return true;
900
		}
901
	}
902
	return false;
903
}
904

    
905
/* Compare two certificates to see if they match. */
906
function cert_compare($cert1, $cert2) {
907
	/* Ensure two certs are identical by first checking that their issuers match, then
908
		subjects, then serial numbers, and finally the moduli. Anything less strict
909
		could accidentally count two similar, but different, certificates as
910
		being identical. */
911
	$c1 = base64_decode($cert1['crt']);
912
	$c2 = base64_decode($cert2['crt']);
913
	if ((cert_get_issuer($c1, false) == cert_get_issuer($c2, false)) &&
914
	    (cert_get_subject($c1, false) == cert_get_subject($c2, false)) &&
915
	    (cert_get_serial($c1, false) == cert_get_serial($c2, false)) &&
916
	    (cert_get_publickey($c1, false) == cert_get_publickey($c2, false))) {
917
		return true;
918
	}
919
	return false;
920
}
921

    
922
function is_cert_revoked($cert, $crlref = "") {
923
	global $config;
924
	if (!is_array($config['crl'])) {
925
		return false;
926
	}
927

    
928
	if (!empty($crlref)) {
929
		$crl = lookup_crl($crlref);
930
		if (!is_array($crl['cert'])) {
931
			return false;
932
		}
933
		foreach ($crl['cert'] as $rcert) {
934
			if (cert_compare($rcert, $cert)) {
935
				return true;
936
			}
937
		}
938
	} else {
939
		foreach ($config['crl'] as $crl) {
940
			if (!is_array($crl['cert'])) {
941
				continue;
942
			}
943
			foreach ($crl['cert'] as $rcert) {
944
				if (cert_compare($rcert, $cert)) {
945
					return true;
946
				}
947
			}
948
		}
949
	}
950
	return false;
951
}
952

    
953
function is_openvpn_server_crl($crlref) {
954
	global $config;
955
	if (!is_array($config['openvpn']['openvpn-server'])) {
956
		return;
957
	}
958
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
959
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref)) {
960
			return true;
961
		}
962
	}
963
	return false;
964
}
965

    
966
// Keep this general to allow for future expansion. See cert_in_use() above.
967
function crl_in_use($crlref) {
968
	return (is_openvpn_server_crl($crlref));
969
}
970

    
971
function is_crl_internal($crl) {
972
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
973
}
974

    
975
function cert_get_cn($crt, $isref = false) {
976
	/* If this is a certref, not an actual cert, look up the cert first */
977
	if ($isref) {
978
		$cert = lookup_cert($crt);
979
		/* If it's not a valid cert, bail. */
980
		if (!(is_array($cert) && !empty($cert['crt']))) {
981
			return "";
982
		}
983
		$cert = $cert['crt'];
984
	} else {
985
		$cert = $crt;
986
	}
987
	$sub = cert_get_subject_array($cert);
988
	if (is_array($sub)) {
989
		foreach ($sub as $s) {
990
			if (strtoupper($s['a']) == "CN") {
991
				return $s['v'];
992
			}
993
		}
994
	}
995
	return "";
996
}
997

    
998
function cert_escape_x509_chars($str, $reverse = false) {
999
	/* Characters which need escaped when present in x.509 fields.
1000
	 * See https://www.ietf.org/rfc/rfc4514.txt
1001
	 *
1002
	 * The backslash (\) must be listed first in these arrays!
1003
	 */
1004
	$cert_directory_string_special_chars = array('\\', '"', '#', '+', ',', ';', '<', '=', '>');
1005
	$cert_directory_string_special_chars_esc = array('\\\\', '\"', '\#', '\+', '\,', '\;', '\<', '\=', '\>');
1006
	if ($reverse) {
1007
		return str_replace($cert_directory_string_special_chars_esc, $cert_directory_string_special_chars, $str);
1008
	} else {
1009
		/* First unescape and then escape again, to prevent possible double escaping. */
1010
		return str_replace($cert_directory_string_special_chars, $cert_directory_string_special_chars_esc, cert_escape_x509_chars($str, true));
1011
	}
1012
}
1013

    
1014
?>
(6-6/54)