Project

General

Profile

Download (27.1 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
	$crl_res = crl_update($crl);
919
	$config['crl'][] = $crl;
920
	return $crl_res;
921
}
922

    
923
function crl_update(& $crl) {
924
	global $config;
925
	$ca =& lookup_ca($crl['caref']);
926
	if (!$ca) {
927
		return false;
928
	}
929
	// If we have text but no certs, it was imported and cannot be updated.
930
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert']))) {
931
		return false;
932
	}
933
	$crl['serial']++;
934
	$ca_str_crt = base64_decode($ca['crt']);
935
	$ca_str_key = base64_decode($ca['prv']);
936
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
937
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
938
		foreach ($crl['cert'] as $cert) {
939
			openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
940
		}
941
	}
942
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
943
	$crl['text'] = base64_encode($crl_text);
944
	return $crl_res;
945
}
946

    
947
function cert_revoke($cert, & $crl, $reason = OCSP_REVOKED_STATUS_UNSPECIFIED) {
948
	global $config;
949
	if (is_cert_revoked($cert, $crl['refid'])) {
950
		return true;
951
	}
952
	// If we have text but no certs, it was imported and cannot be updated.
953
	if (!is_crl_internal($crl)) {
954
		return false;
955
	}
956
	$cert["reason"] = $reason;
957
	$cert["revoke_time"] = time();
958
	$crl["cert"][] = $cert;
959
	crl_update($crl);
960
	return true;
961
}
962

    
963
function cert_unrevoke($cert, & $crl) {
964
	global $config;
965
	if (!is_crl_internal($crl)) {
966
		return false;
967
	}
968
	foreach ($crl['cert'] as $id => $rcert) {
969
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
970
			unset($crl['cert'][$id]);
971
			if (count($crl['cert']) == 0) {
972
				// Protect against accidentally switching the type to imported, for older CRLs
973
				if (!isset($crl['method'])) {
974
					$crl['method'] = "internal";
975
				}
976
				crl_update($crl);
977
			} else {
978
				crl_update($crl);
979
			}
980
			return true;
981
		}
982
	}
983
	return false;
984
}
985

    
986
/* Compare two certificates to see if they match. */
987
function cert_compare($cert1, $cert2) {
988
	/* Ensure two certs are identical by first checking that their issuers match, then
989
		subjects, then serial numbers, and finally the moduli. Anything less strict
990
		could accidentally count two similar, but different, certificates as
991
		being identical. */
992
	$c1 = base64_decode($cert1['crt']);
993
	$c2 = base64_decode($cert2['crt']);
994
	if ((cert_get_issuer($c1, false) == cert_get_issuer($c2, false)) &&
995
	    (cert_get_subject($c1, false) == cert_get_subject($c2, false)) &&
996
	    (cert_get_serial($c1, false) == cert_get_serial($c2, false)) &&
997
	    (cert_get_publickey($c1, false) == cert_get_publickey($c2, false))) {
998
		return true;
999
	}
1000
	return false;
1001
}
1002

    
1003
function is_cert_revoked($cert, $crlref = "") {
1004
	global $config;
1005
	if (!is_array($config['crl'])) {
1006
		return false;
1007
	}
1008

    
1009
	if (!empty($crlref)) {
1010
		$crl = lookup_crl($crlref);
1011
		if (!is_array($crl['cert'])) {
1012
			return false;
1013
		}
1014
		foreach ($crl['cert'] as $rcert) {
1015
			if (cert_compare($rcert, $cert)) {
1016
				return true;
1017
			}
1018
		}
1019
	} else {
1020
		foreach ($config['crl'] as $crl) {
1021
			if (!is_array($crl['cert'])) {
1022
				continue;
1023
			}
1024
			foreach ($crl['cert'] as $rcert) {
1025
				if (cert_compare($rcert, $cert)) {
1026
					return true;
1027
				}
1028
			}
1029
		}
1030
	}
1031
	return false;
1032
}
1033

    
1034
function is_openvpn_server_crl($crlref) {
1035
	global $config;
1036
	if (!is_array($config['openvpn']['openvpn-server'])) {
1037
		return;
1038
	}
1039
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
1040
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref)) {
1041
			return true;
1042
		}
1043
	}
1044
	return false;
1045
}
1046

    
1047
// Keep this general to allow for future expansion. See cert_in_use() above.
1048
function crl_in_use($crlref) {
1049
	return (is_openvpn_server_crl($crlref));
1050
}
1051

    
1052
function is_crl_internal($crl) {
1053
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
1054
}
1055

    
1056
function cert_get_cn($crt, $isref = false) {
1057
	/* If this is a certref, not an actual cert, look up the cert first */
1058
	if ($isref) {
1059
		$cert = lookup_cert($crt);
1060
		/* If it's not a valid cert, bail. */
1061
		if (!(is_array($cert) && !empty($cert['crt']))) {
1062
			return "";
1063
		}
1064
		$cert = $cert['crt'];
1065
	} else {
1066
		$cert = $crt;
1067
	}
1068
	$sub = cert_get_subject_array($cert);
1069
	if (is_array($sub)) {
1070
		foreach ($sub as $s) {
1071
			if (strtoupper($s['a']) == "CN") {
1072
				return $s['v'];
1073
			}
1074
		}
1075
	}
1076
	return "";
1077
}
1078

    
1079
function cert_escape_x509_chars($str, $reverse = false) {
1080
	/* Characters which need escaped when present in x.509 fields.
1081
	 * See https://www.ietf.org/rfc/rfc4514.txt
1082
	 *
1083
	 * The backslash (\) must be listed first in these arrays!
1084
	 */
1085
	$cert_directory_string_special_chars = array('\\', '"', '#', '+', ',', ';', '<', '=', '>');
1086
	$cert_directory_string_special_chars_esc = array('\\\\', '\"', '\#', '\+', '\,', '\;', '\<', '\=', '\>');
1087
	if ($reverse) {
1088
		return str_replace($cert_directory_string_special_chars_esc, $cert_directory_string_special_chars, $str);
1089
	} else {
1090
		/* First unescape and then escape again, to prevent possible double escaping. */
1091
		return str_replace($cert_directory_string_special_chars, $cert_directory_string_special_chars_esc, cert_escape_x509_chars($str, true));
1092
	}
1093
}
1094

    
1095
function cert_add_altname_type($str) {
1096
	$type = "";
1097
	if (is_ipaddr($str)) {
1098
		$type = "IP";
1099
	} elseif (is_hostname($str, true)) {
1100
		$type = "DNS";
1101
	} elseif (is_URL($str)) {
1102
		$type = "URI";
1103
	} elseif (filter_var($str, FILTER_VALIDATE_EMAIL)) {
1104
		$type = "email";
1105
	}
1106
	if (!empty($type)) {
1107
		return "{$type}:" . cert_escape_x509_chars($str);
1108
	} else {
1109
		return null;
1110
	}
1111
}
1112

    
1113
function cert_type_config_section($type) {
1114
	switch ($type) {
1115
		case "ca":
1116
			$cert_type = "v3_ca";
1117
			break;
1118
		case "server":
1119
		case "self-signed":
1120
			$cert_type = "server";
1121
			break;
1122
		default:
1123
			$cert_type = "usr_cert";
1124
			break;
1125
	}
1126
	return $cert_type;
1127
}
1128

    
1129
?>
(6-6/60)