Project

General

Profile

Download (28 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * certs.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2008-2018 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
/* Numbers are set in the RFC: https://www.ietf.org/rfc/rfc5280.txt */
32
$openssl_crl_status = array(
33
	-1 => "No Status (default)",
34
	0  => "Unspecified",
35
	1 => "Key Compromise",
36
	2 => "CA Compromise",
37
	3 => "Affiliation Changed",
38
	4 => "Superseded",
39
	5 => "Cessation of Operation",
40
	6 => "Certificate Hold",
41
	9 => 'Privilege Withdrawn',
42
);
43

    
44
global $cert_altname_types;
45
$cert_altname_types = array(
46
	'DNS' => gettext('FQDN or Hostname'),
47
	'IP' => gettext('IP address'),
48
	'URI' => gettext('URI'),
49
	'email' => gettext('email address'),
50
);
51

    
52

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

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

    
64
	return false;
65
}
66

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

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

    
79
	return false;
80
}
81

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

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

    
93
	return false;
94
}
95

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

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

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

    
118
	return false;
119
}
120

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

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

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

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

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

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

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

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

    
209
	// generate a new key pair
210
	$res_key = openssl_pkey_new($args);
211
	if (!$res_key) {
212
		return false;
213
	}
214

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

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

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

    
233
	// return our ca information
234
	$ca['crt'] = base64_encode($str_crt);
235
	$ca['prv'] = base64_encode($str_key);
236
	$ca['serial'] = 0;
237

    
238
	return true;
239
}
240

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

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

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

    
262
	// generate a new key pair
263
	$res_key = openssl_pkey_new($args);
264
	if (!$res_key) {
265
		return false;
266
	}
267

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

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

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

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

    
292
	return true;
293
}
294

    
295
function cert_import(& $cert, $crt_str, $key_str) {
296

    
297
	$cert['crt'] = base64_encode($crt_str);
298
	$cert['prv'] = base64_encode($key_str);
299

    
300
	$subject = cert_get_subject($crt_str, false);
301
	$issuer = cert_get_issuer($crt_str, false);
302

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

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

    
315
	$cert['type'] = $type;
316

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

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

    
337
	$cert_type = cert_type_config_section($type);
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, $type = "user", $digest_alg = "sha256") {
396

    
397
	$cert_type = cert_type_config_section($type);
398

    
399
	// in case of using Subject Alternative Names use other sections (with postfix '_san')
400
	// pass subjectAltName over environment variable 'SAN'
401
	if ($dn['subjectAltName']) {
402
		putenv("SAN={$dn['subjectAltName']}"); // subjectAltName can be set _only_ via configuration file
403
		$cert_type .= '_san';
404
		unset($dn['subjectAltName']);
405
	}
406

    
407
	$args = array(
408
		"x509_extensions" => $cert_type,
409
		"req_extensions" => "req_{$cert_type}",
410
		"digest_alg" => $digest_alg,
411
		"private_key_bits" => (int)$keylen,
412
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
413
		"encrypt_key" => false);
414

    
415
	// generate a new key pair
416
	$res_key = openssl_pkey_new($args);
417
	if (!$res_key) {
418
		return false;
419
	}
420

    
421
	// generate a certificate signing request
422
	$res_csr = openssl_csr_new($dn, $res_key, $args);
423
	if (!$res_csr) {
424
		return false;
425
	}
426

    
427
	// export our request data
428
	if (!openssl_pkey_export($res_key, $str_key) ||
429
	    !openssl_csr_export($res_csr, $str_csr)) {
430
		return false;
431
	}
432

    
433
	// return our request information
434
	$cert['csr'] = base64_encode($str_csr);
435
	$cert['prv'] = base64_encode($str_key);
436

    
437
	return true;
438
}
439

    
440
function csr_sign($csr, & $ca, $duration, $type = "user", $altnames, $digest_alg = "sha256") {
441
	global $config;
442
	$old_err_level = error_reporting(0);
443

    
444
	// Gather the information required for signed cert
445
	$ca_str_crt = base64_decode($ca['crt']);
446
	$ca_str_key = base64_decode($ca['prv']);
447
	$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
448
	if (!$ca_res_key) {
449
		return false;
450
	}
451
	if (empty($ca['serial'])) {
452
		$ca['serial'] = 0;
453
	}
454
	$ca_serial = ++$ca['serial'];
455

    
456
	$cert_type = cert_type_config_section($type);
457

    
458
	if (!empty($altnames)) {
459
		putenv("SAN={$altnames}"); // subjectAltName can be set _only_ via configuration file
460
		$cert_type .= '_san';
461
	}
462

    
463
	$args = array(
464
		"x509_extensions" => $cert_type,
465
		"digest_alg" => $digest_alg,
466
		"req_extensions" => "req_{$cert_type}"
467
	);
468

    
469
	// Sign the new cert and export it in x509 format
470
	openssl_x509_export(openssl_csr_sign($csr, $ca_str_crt, $ca_str_key, $duration, $args, $ca_serial), $n509);
471
	error_reporting($old_err_level);
472

    
473
	return $n509;
474
}
475

    
476
function csr_complete(& $cert, $str_crt) {
477
	$str_key = base64_decode($cert['prv']);
478
	cert_import($cert, $str_crt, $str_key);
479
	unset($cert['csr']);
480
	return true;
481
}
482

    
483
function csr_get_subject($str_crt, $decode = true) {
484

    
485
	if ($decode) {
486
		$str_crt = base64_decode($str_crt);
487
	}
488

    
489
	$components = openssl_csr_get_subject($str_crt);
490

    
491
	if (empty($components) || !is_array($components)) {
492
		return "unknown";
493
	}
494

    
495
	ksort($components);
496
	foreach ($components as $a => $v) {
497
		if (!strlen($subject)) {
498
			$subject = "{$a}={$v}";
499
		} else {
500
			$subject = "{$a}={$v}, {$subject}";
501
		}
502
	}
503

    
504
	return $subject;
505
}
506

    
507
function cert_get_subject($str_crt, $decode = true) {
508

    
509
	if ($decode) {
510
		$str_crt = base64_decode($str_crt);
511
	}
512

    
513
	$inf_crt = openssl_x509_parse($str_crt);
514
	$components = $inf_crt['subject'];
515

    
516
	if (empty($components) || !is_array($components)) {
517
		return "unknown";
518
	}
519

    
520
	ksort($components);
521
	foreach ($components as $a => $v) {
522
		if (is_array($v)) {
523
			ksort($v);
524
			foreach ($v as $w) {
525
				$asubject = "{$a}={$w}";
526
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
527
			}
528
		} else {
529
			$asubject = "{$a}={$v}";
530
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
531
		}
532
	}
533

    
534
	return $subject;
535
}
536

    
537
function cert_get_subject_array($crt) {
538
	$str_crt = base64_decode($crt);
539
	$inf_crt = openssl_x509_parse($str_crt);
540
	$components = $inf_crt['subject'];
541

    
542
	if (!is_array($components)) {
543
		return;
544
	}
545

    
546
	$subject_array = array();
547

    
548
	foreach ($components as $a => $v) {
549
		$subject_array[] = array('a' => $a, 'v' => $v);
550
	}
551

    
552
	return $subject_array;
553
}
554

    
555
function cert_get_subject_hash($crt) {
556
	$str_crt = base64_decode($crt);
557
	$inf_crt = openssl_x509_parse($str_crt);
558
	return $inf_crt['subject'];
559
}
560

    
561
function cert_get_sans($str_crt, $decode = true) {
562
	if ($decode) {
563
		$str_crt = base64_decode($str_crt);
564
	}
565
	$sans = array();
566
	$crt_details = openssl_x509_parse($str_crt);
567
	if (!empty($crt_details['extensions']['subjectAltName'])) {
568
		$sans = explode(',', $crt_details['extensions']['subjectAltName']);
569
	}
570
	return $sans;
571
}
572

    
573
function cert_get_issuer($str_crt, $decode = true) {
574

    
575
	if ($decode) {
576
		$str_crt = base64_decode($str_crt);
577
	}
578

    
579
	$inf_crt = openssl_x509_parse($str_crt);
580
	$components = $inf_crt['issuer'];
581

    
582
	if (empty($components) || !is_array($components)) {
583
		return "unknown";
584
	}
585

    
586
	ksort($components);
587
	foreach ($components as $a => $v) {
588
		if (is_array($v)) {
589
			ksort($v);
590
			foreach ($v as $w) {
591
				$aissuer = "{$a}={$w}";
592
				$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
593
			}
594
		} else {
595
			$aissuer = "{$a}={$v}";
596
			$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
597
		}
598
	}
599

    
600
	return $issuer;
601
}
602

    
603
/* Works for both RSA and ECC (crt) and key (prv) */
604
function cert_get_publickey($str_crt, $decode = true, $type = "crt") {
605
	if ($decode) {
606
		$str_crt = base64_decode($str_crt);
607
	}
608
	$certfn = tempnam('/tmp', 'CGPK');
609
	file_put_contents($certfn, $str_crt);
610
	switch ($type) {
611
		case 'prv':
612
			exec("/usr/bin/openssl pkey -in {$certfn} -pubout", $out);
613
			break;
614
		case 'crt':
615
			exec("/usr/bin/openssl x509 -in {$certfn} -inform pem -noout -pubkey", $out);
616
			break;
617
		case 'csr':
618
			exec("/usr/bin/openssl req -in {$certfn} -inform pem -noout -pubkey", $out);
619
			break;
620
		default:
621
			$out = array();
622
			break;
623
	}
624
	unlink($certfn);
625
	return implode("\n", $out);
626
}
627

    
628
function cert_get_purpose($str_crt, $decode = true) {
629
	$extended_oids = array(
630
		"1.3.6.1.5.5.8.2.2" => "IP Security IKE Intermediate",
631
	);
632
	if ($decode) {
633
		$str_crt = base64_decode($str_crt);
634
	}
635
	$crt_details = openssl_x509_parse($str_crt);
636
	$purpose = array();
637
	if (!empty($crt_details['extensions']['keyUsage'])) {
638
		$purpose['ku'] = explode(',', $crt_details['extensions']['keyUsage']);
639
		foreach ($purpose['ku'] as & $ku) {
640
			$ku = trim($ku);
641
			if (array_key_exists($ku, $extended_oids)) {
642
				$ku = $extended_oids[$ku];
643
			}
644
		}
645
	} else {
646
		$purpose['ku'] = array();
647
	}
648
	if (!empty($crt_details['extensions']['extendedKeyUsage'])) {
649
		$purpose['eku'] = explode(',', $crt_details['extensions']['extendedKeyUsage']);
650
		foreach ($purpose['eku'] as & $eku) {
651
			$eku = trim($eku);
652
			if (array_key_exists($eku, $extended_oids)) {
653
				$eku = $extended_oids[$eku];
654
			}
655
		}
656
	} else {
657
		$purpose['eku'] = array();
658
	}
659
	$purpose['ca'] = (stristr($crt_details['extensions']['basicConstraints'], 'CA:TRUE') === false) ? 'No': 'Yes';
660
	$purpose['server'] = (in_array('TLS Web Server Authentication', $purpose['eku'])) ? 'Yes': 'No';
661

    
662
	return $purpose;
663
}
664

    
665
function cert_get_ocspstaple($str_crt, $decode = true) {
666
	if ($decode) {
667
		$str_crt = base64_decode($str_crt);
668
	}
669
	$crt_details = openssl_x509_parse($str_crt);
670
	if (!empty($crt_details['extensions']['1.3.6.1.5.5.7.1.24'])) {
671
		return true;
672
	}
673
	return false;
674
}
675

    
676
function cert_get_dates($str_crt, $decode = true) {
677
	if ($decode) {
678
		$str_crt = base64_decode($str_crt);
679
	}
680
	$crt_details = openssl_x509_parse($str_crt);
681
	if ($crt_details['validFrom_time_t'] > 0) {
682
		$start = date('r', $crt_details['validFrom_time_t']);
683
	}
684
	if ($crt_details['validTo_time_t'] > 0) {
685
		$end = date('r', $crt_details['validTo_time_t']);
686
	}
687
	return array($start, $end);
688
}
689

    
690
function cert_get_serial($str_crt, $decode = true) {
691
	if ($decode) {
692
		$str_crt = base64_decode($str_crt);
693
	}
694
	$crt_details = openssl_x509_parse($str_crt);
695
	if (isset($crt_details['serialNumber']) && !empty($crt_details['serialNumber'])) {
696
		return $crt_details['serialNumber'];
697
	} else {
698
		return NULL;
699
	}
700
}
701

    
702
function cert_get_sigtype($str_crt, $decode = true) {
703
	if ($decode) {
704
		$str_crt = base64_decode($str_crt);
705
	}
706
	$crt_details = openssl_x509_parse($str_crt);
707

    
708
	$signature = array();
709
	if (isset($crt_details['signatureTypeSN']) && !empty($crt_details['signatureTypeSN'])) {
710
		$signature['shortname'] = $crt_details['signatureTypeSN'];
711
	}
712
	if (isset($crt_details['signatureTypeLN']) && !empty($crt_details['signatureTypeLN'])) {
713
		$signature['longname'] = $crt_details['signatureTypeLN'];
714
	}
715
	if (isset($crt_details['signatureTypeNID']) && !empty($crt_details['signatureTypeNID'])) {
716
		$signature['nid'] = $crt_details['signatureTypeNID'];
717
	}
718

    
719
	return $signature;
720
}
721

    
722
function is_openvpn_server_ca($caref) {
723
	global $config;
724
	if (!is_array($config['openvpn']['openvpn-server'])) {
725
		return;
726
	}
727
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
728
		if ($ovpns['caref'] == $caref) {
729
			return true;
730
		}
731
	}
732
	return false;
733
}
734

    
735
function is_openvpn_client_ca($caref) {
736
	global $config;
737
	if (!is_array($config['openvpn']['openvpn-client'])) {
738
		return;
739
	}
740
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
741
		if ($ovpnc['caref'] == $caref) {
742
			return true;
743
		}
744
	}
745
	return false;
746
}
747

    
748
function is_ipsec_peer_ca($caref) {
749
	global $config;
750
	if (!is_array($config['ipsec']['phase1'])) {
751
		return;
752
	}
753
	foreach ($config['ipsec']['phase1'] as $ipsec) {
754
		if ($ipsec['caref'] == $caref) {
755
			return true;
756
		}
757
	}
758
	return false;
759
}
760

    
761
function is_ldap_peer_ca($caref) {
762
	global $config;
763
	if (!is_array($config['system']['authserver'])) {
764
		return;
765
	}
766
	foreach ($config['system']['authserver'] as $authserver) {
767
		if ($authserver['ldap_caref'] == $caref) {
768
			return true;
769
		}
770
	}
771
	return false;
772
}
773

    
774
function ca_in_use($caref) {
775
	return (is_openvpn_server_ca($caref) ||
776
		is_openvpn_client_ca($caref) ||
777
		is_ipsec_peer_ca($caref) ||
778
		is_ldap_peer_ca($caref));
779
}
780

    
781
function is_user_cert($certref) {
782
	global $config;
783
	if (!is_array($config['system']['user'])) {
784
		return;
785
	}
786
	foreach ($config['system']['user'] as $user) {
787
		if (!is_array($user['cert'])) {
788
			continue;
789
		}
790
		foreach ($user['cert'] as $cert) {
791
			if ($certref == $cert) {
792
				return true;
793
			}
794
		}
795
	}
796
	return false;
797
}
798

    
799
function is_openvpn_server_cert($certref) {
800
	global $config;
801
	if (!is_array($config['openvpn']['openvpn-server'])) {
802
		return;
803
	}
804
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
805
		if ($ovpns['certref'] == $certref) {
806
			return true;
807
		}
808
	}
809
	return false;
810
}
811

    
812
function is_openvpn_client_cert($certref) {
813
	global $config;
814
	if (!is_array($config['openvpn']['openvpn-client'])) {
815
		return;
816
	}
817
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
818
		if ($ovpnc['certref'] == $certref) {
819
			return true;
820
		}
821
	}
822
	return false;
823
}
824

    
825
function is_ipsec_cert($certref) {
826
	global $config;
827
	if (!is_array($config['ipsec']['phase1'])) {
828
		return;
829
	}
830
	foreach ($config['ipsec']['phase1'] as $ipsec) {
831
		if ($ipsec['certref'] == $certref) {
832
			return true;
833
		}
834
	}
835
	return false;
836
}
837

    
838
function is_webgui_cert($certref) {
839
	global $config;
840
	if (($config['system']['webgui']['ssl-certref'] == $certref) &&
841
	    ($config['system']['webgui']['protocol'] != "http")) {
842
		return true;
843
	}
844
}
845

    
846
function is_package_cert($certref) {
847
	$pluginparams = array();
848
	$pluginparams['type'] = 'certificates';
849
	$pluginparams['event'] = 'used_certificates';
850

    
851
	$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
852

    
853
	/* Check if any package is using certificate */
854
	foreach ($certificates_used_by_packages as $name => $package) {
855
		if (is_array($package['certificatelist'][$certref]) &&
856
		    isset($package['certificatelist'][$certref]) > 0) {
857
			return true;
858
		}
859
	}
860
}
861

    
862
function is_captiveportal_cert($certref) {
863
	global $config;
864
	if (!is_array($config['captiveportal'])) {
865
		return;
866
	}
867
	foreach ($config['captiveportal'] as $portal) {
868
		if (isset($portal['enable']) && isset($portal['httpslogin']) && ($portal['certref'] == $certref)) {
869
			return true;
870
		}
871
	}
872
	return false;
873
}
874

    
875
function cert_in_use($certref) {
876

    
877
	return (is_webgui_cert($certref) ||
878
		is_user_cert($certref) ||
879
		is_openvpn_server_cert($certref) ||
880
		is_openvpn_client_cert($certref) ||
881
		is_ipsec_cert($certref) ||
882
		is_captiveportal_cert($certref) ||
883
		is_package_cert($certref));
884
}
885

    
886
function cert_usedby_description($refid, $certificates_used_by_packages) {
887
	$result = "";
888
	if (is_array($certificates_used_by_packages)) {
889
		foreach ($certificates_used_by_packages as $name => $package) {
890
			if (isset($package['certificatelist'][$refid])) {
891
				$hint = "" ;
892
				if (is_array($package['certificatelist'][$refid])) {
893
					foreach ($package['certificatelist'][$refid] as $cert_used) {
894
						$hint = $hint . $cert_used['usedby']."\n";
895
					}
896
				}
897
				$count = count($package['certificatelist'][$refid]);
898
				$result .= "<div title='".htmlspecialchars($hint)."'>";
899
				$result .= htmlspecialchars($package['pkgname'])." ($count)<br />";
900
				$result .= "</div>";
901
			}
902
		}
903
	}
904
	return $result;
905
}
906

    
907
function crl_create(& $crl, $caref, $name, $serial = 0, $lifetime = 9999) {
908
	global $config;
909
	$ca =& lookup_ca($caref);
910
	if (!$ca) {
911
		return false;
912
	}
913
	$crl['descr'] = $name;
914
	$crl['caref'] = $caref;
915
	$crl['serial'] = $serial;
916
	$crl['lifetime'] = $lifetime;
917
	$crl['cert'] = array();
918
	$config['crl'][] = $crl;
919
	return $crl;
920
}
921

    
922
function crl_update(& $crl) {
923
	require_once('ASN1.php');
924
	require_once('ASN1_UTF8STRING.php');
925
	require_once('ASN1_ASCIISTRING.php');
926
	require_once('ASN1_BITSTRING.php');
927
	require_once('ASN1_BOOL.php');
928
	require_once('ASN1_GENERALTIME.php');
929
	require_once('ASN1_INT.php');
930
	require_once('ASN1_ENUM.php');
931
	require_once('ASN1_NULL.php');
932
	require_once('ASN1_OCTETSTRING.php');
933
	require_once('ASN1_OID.php');
934
	require_once('ASN1_SEQUENCE.php');
935
	require_once('ASN1_SET.php');
936
	require_once('ASN1_SIMPLE.php');
937
	require_once('ASN1_TELETEXSTRING.php');
938
	require_once('ASN1_UTCTIME.php');
939
	require_once('OID.php');
940
	require_once('X509.php');
941
	require_once('X509_CERT.php');
942
	require_once('X509_CRL.php');
943

    
944
	global $config;
945
	$ca =& lookup_ca($crl['caref']);
946
	if (!$ca) {
947
		return false;
948
	}
949
	// If we have text but no certs, it was imported and cannot be updated.
950
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert']))) {
951
		return false;
952
	}
953
	$crl['serial']++;
954
	$ca_cert = \Ukrbublik\openssl_x509_crl\X509::pem2der(base64_decode($ca['crt']));
955
	$ca_pkey = openssl_pkey_get_private(base64_decode($ca['prv']));
956

    
957
	$crlconf = array(
958
		'no' => $crl['serial'],
959
		'version' => 2,
960
		'days' => $crl['lifetime'],
961
		'alg' => OPENSSL_ALGO_SHA1,
962
		'revoked' => array()
963
	);
964

    
965
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
966
		foreach ($crl['cert'] as $cert) {
967
			$crlconf['revoked'][] = array(
968
				'serial' => cert_get_serial($cert["crt"], true),
969
				'rev_date' => $cert["revoke_time"],
970
				'reason' => ($cert["reason"] == -1) ? null : (int) $cert["reason"],
971
			);
972
		}
973
	}
974

    
975
	$crl_data = \Ukrbublik\openssl_x509_crl\X509_CRL::create($crlconf, $ca_pkey, $ca_cert);
976
	$crl['text'] = base64_encode(\Ukrbublik\openssl_x509_crl\X509::der2pem4crl($crl_data));
977

    
978
	return $crl['text'];
979
}
980

    
981
function cert_revoke($cert, & $crl, $reason = OCSP_REVOKED_STATUS_UNSPECIFIED) {
982
	global $config;
983
	if (is_cert_revoked($cert, $crl['refid'])) {
984
		return true;
985
	}
986
	// If we have text but no certs, it was imported and cannot be updated.
987
	if (!is_crl_internal($crl)) {
988
		return false;
989
	}
990
	$cert["reason"] = $reason;
991
	$cert["revoke_time"] = time();
992
	$crl["cert"][] = $cert;
993
	crl_update($crl);
994
	return true;
995
}
996

    
997
function cert_unrevoke($cert, & $crl) {
998
	global $config;
999
	if (!is_crl_internal($crl)) {
1000
		return false;
1001
	}
1002
	foreach ($crl['cert'] as $id => $rcert) {
1003
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
1004
			unset($crl['cert'][$id]);
1005
			if (count($crl['cert']) == 0) {
1006
				// Protect against accidentally switching the type to imported, for older CRLs
1007
				if (!isset($crl['method'])) {
1008
					$crl['method'] = "internal";
1009
				}
1010
				crl_update($crl);
1011
			} else {
1012
				crl_update($crl);
1013
			}
1014
			return true;
1015
		}
1016
	}
1017
	return false;
1018
}
1019

    
1020
/* Compare two certificates to see if they match. */
1021
function cert_compare($cert1, $cert2) {
1022
	/* Ensure two certs are identical by first checking that their issuers match, then
1023
		subjects, then serial numbers, and finally the moduli. Anything less strict
1024
		could accidentally count two similar, but different, certificates as
1025
		being identical. */
1026
	$c1 = base64_decode($cert1['crt']);
1027
	$c2 = base64_decode($cert2['crt']);
1028
	if ((cert_get_issuer($c1, false) == cert_get_issuer($c2, false)) &&
1029
	    (cert_get_subject($c1, false) == cert_get_subject($c2, false)) &&
1030
	    (cert_get_serial($c1, false) == cert_get_serial($c2, false)) &&
1031
	    (cert_get_publickey($c1, false) == cert_get_publickey($c2, false))) {
1032
		return true;
1033
	}
1034
	return false;
1035
}
1036

    
1037
function is_cert_revoked($cert, $crlref = "") {
1038
	global $config;
1039
	if (!is_array($config['crl'])) {
1040
		return false;
1041
	}
1042

    
1043
	if (!empty($crlref)) {
1044
		$crl = lookup_crl($crlref);
1045
		if (!is_array($crl['cert'])) {
1046
			return false;
1047
		}
1048
		foreach ($crl['cert'] as $rcert) {
1049
			if (cert_compare($rcert, $cert)) {
1050
				return true;
1051
			}
1052
		}
1053
	} else {
1054
		foreach ($config['crl'] as $crl) {
1055
			if (!is_array($crl['cert'])) {
1056
				continue;
1057
			}
1058
			foreach ($crl['cert'] as $rcert) {
1059
				if (cert_compare($rcert, $cert)) {
1060
					return true;
1061
				}
1062
			}
1063
		}
1064
	}
1065
	return false;
1066
}
1067

    
1068
function is_openvpn_server_crl($crlref) {
1069
	global $config;
1070
	if (!is_array($config['openvpn']['openvpn-server'])) {
1071
		return;
1072
	}
1073
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
1074
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref)) {
1075
			return true;
1076
		}
1077
	}
1078
	return false;
1079
}
1080

    
1081
// Keep this general to allow for future expansion. See cert_in_use() above.
1082
function crl_in_use($crlref) {
1083
	return (is_openvpn_server_crl($crlref));
1084
}
1085

    
1086
function is_crl_internal($crl) {
1087
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
1088
}
1089

    
1090
function cert_get_cn($crt, $isref = false) {
1091
	/* If this is a certref, not an actual cert, look up the cert first */
1092
	if ($isref) {
1093
		$cert = lookup_cert($crt);
1094
		/* If it's not a valid cert, bail. */
1095
		if (!(is_array($cert) && !empty($cert['crt']))) {
1096
			return "";
1097
		}
1098
		$cert = $cert['crt'];
1099
	} else {
1100
		$cert = $crt;
1101
	}
1102
	$sub = cert_get_subject_array($cert);
1103
	if (is_array($sub)) {
1104
		foreach ($sub as $s) {
1105
			if (strtoupper($s['a']) == "CN") {
1106
				return $s['v'];
1107
			}
1108
		}
1109
	}
1110
	return "";
1111
}
1112

    
1113
function cert_escape_x509_chars($str, $reverse = false) {
1114
	/* Characters which need escaped when present in x.509 fields.
1115
	 * See https://www.ietf.org/rfc/rfc4514.txt
1116
	 *
1117
	 * The backslash (\) must be listed first in these arrays!
1118
	 */
1119
	$cert_directory_string_special_chars = array('\\', '"', '#', '+', ',', ';', '<', '=', '>');
1120
	$cert_directory_string_special_chars_esc = array('\\\\', '\"', '\#', '\+', '\,', '\;', '\<', '\=', '\>');
1121
	if ($reverse) {
1122
		return str_replace($cert_directory_string_special_chars_esc, $cert_directory_string_special_chars, $str);
1123
	} else {
1124
		/* First unescape and then escape again, to prevent possible double escaping. */
1125
		return str_replace($cert_directory_string_special_chars, $cert_directory_string_special_chars_esc, cert_escape_x509_chars($str, true));
1126
	}
1127
}
1128

    
1129
function cert_add_altname_type($str) {
1130
	$type = "";
1131
	if (is_ipaddr($str)) {
1132
		$type = "IP";
1133
	} elseif (is_hostname($str, true)) {
1134
		$type = "DNS";
1135
	} elseif (is_URL($str)) {
1136
		$type = "URI";
1137
	} elseif (filter_var($str, FILTER_VALIDATE_EMAIL)) {
1138
		$type = "email";
1139
	}
1140
	if (!empty($type)) {
1141
		return "{$type}:" . cert_escape_x509_chars($str);
1142
	} else {
1143
		return null;
1144
	}
1145
}
1146

    
1147
function cert_type_config_section($type) {
1148
	switch ($type) {
1149
		case "ca":
1150
			$cert_type = "v3_ca";
1151
			break;
1152
		case "server":
1153
		case "self-signed":
1154
			$cert_type = "server";
1155
			break;
1156
		default:
1157
			$cert_type = "usr_cert";
1158
			break;
1159
	}
1160
	return $cert_type;
1161
}
1162

    
1163
?>
(7-7/60)