Project

General

Profile

Download (27 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
$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
global $cert_altname_types;
43
$cert_altname_types = array(
44
	'DNS' => gettext('FQDN or Hostname'),
45
	'IP' => gettext('IP address'),
46
	'URI' => gettext('URI'),
47
	'email' => gettext('email address'),
48
);
49

    
50

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

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

    
62
	return false;
63
}
64

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

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

    
77
	return false;
78
}
79

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

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

    
91
	return false;
92
}
93

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

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

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

    
116
	return false;
117
}
118

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

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

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

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

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

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

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

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

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

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

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

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

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

    
236
	return true;
237
}
238

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

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

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

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

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

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

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

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

    
290
	return true;
291
}
292

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

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

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

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

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

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

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

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

    
335
	$cert_type = cert_type_config_section($type);
336

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

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

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

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

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

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

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

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

    
390
	return true;
391
}
392

    
393
function csr_generate(& $cert, $keylen, $dn, $type = "user", $digest_alg = "sha256") {
394

    
395
	$cert_type = cert_type_config_section($type);
396

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

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

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

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

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

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

    
435
	return true;
436
}
437

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

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

    
454
	$cert_type = cert_type_config_section($type);
455

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

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

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

    
471
	return $n509;
472
}
473

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

    
481
function csr_get_subject($str_crt, $decode = true) {
482

    
483
	if ($decode) {
484
		$str_crt = base64_decode($str_crt);
485
	}
486

    
487
	$components = openssl_csr_get_subject($str_crt);
488

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

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

    
502
	return $subject;
503
}
504

    
505
function cert_get_subject($str_crt, $decode = true) {
506

    
507
	if ($decode) {
508
		$str_crt = base64_decode($str_crt);
509
	}
510

    
511
	$inf_crt = openssl_x509_parse($str_crt);
512
	$components = $inf_crt['subject'];
513

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

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

    
532
	return $subject;
533
}
534

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

    
540
	if (!is_array($components)) {
541
		return;
542
	}
543

    
544
	$subject_array = array();
545

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

    
550
	return $subject_array;
551
}
552

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

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

    
571
function cert_get_issuer($str_crt, $decode = true) {
572

    
573
	if ($decode) {
574
		$str_crt = base64_decode($str_crt);
575
	}
576

    
577
	$inf_crt = openssl_x509_parse($str_crt);
578
	$components = $inf_crt['issuer'];
579

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

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

    
598
	return $issuer;
599
}
600

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

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

    
660
	return $purpose;
661
}
662

    
663
function cert_get_dates($str_crt, $decode = true) {
664
	if ($decode) {
665
		$str_crt = base64_decode($str_crt);
666
	}
667
	$crt_details = openssl_x509_parse($str_crt);
668
	if ($crt_details['validFrom_time_t'] > 0) {
669
		$start = date('r', $crt_details['validFrom_time_t']);
670
	}
671
	if ($crt_details['validTo_time_t'] > 0) {
672
		$end = date('r', $crt_details['validTo_time_t']);
673
	}
674
	return array($start, $end);
675
}
676

    
677
function cert_get_serial($str_crt, $decode = true) {
678
	if ($decode) {
679
		$str_crt = base64_decode($str_crt);
680
	}
681
	$crt_details = openssl_x509_parse($str_crt);
682
	if (isset($crt_details['serialNumber']) && !empty($crt_details['serialNumber'])) {
683
		return $crt_details['serialNumber'];
684
	} else {
685
		return NULL;
686
	}
687
}
688

    
689
function cert_get_sigtype($str_crt, $decode = true) {
690
	if ($decode) {
691
		$str_crt = base64_decode($str_crt);
692
	}
693
	$crt_details = openssl_x509_parse($str_crt);
694

    
695
	$signature = array();
696
	if (isset($crt_details['signatureTypeSN']) && !empty($crt_details['signatureTypeSN'])) {
697
		$signature['shortname'] = $crt_details['signatureTypeSN'];
698
	}
699
	if (isset($crt_details['signatureTypeLN']) && !empty($crt_details['signatureTypeLN'])) {
700
		$signature['longname'] = $crt_details['signatureTypeLN'];
701
	}
702
	if (isset($crt_details['signatureTypeNID']) && !empty($crt_details['signatureTypeNID'])) {
703
		$signature['nid'] = $crt_details['signatureTypeNID'];
704
	}
705

    
706
	return $signature;
707
}
708

    
709
function is_openvpn_server_ca($caref) {
710
	global $config;
711
	if (!is_array($config['openvpn']['openvpn-server'])) {
712
		return;
713
	}
714
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
715
		if ($ovpns['caref'] == $caref) {
716
			return true;
717
		}
718
	}
719
	return false;
720
}
721

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

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

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

    
761
function ca_in_use($caref) {
762
	return (is_openvpn_server_ca($caref) ||
763
		is_openvpn_client_ca($caref) ||
764
		is_ipsec_peer_ca($caref) ||
765
		is_ldap_peer_ca($caref));
766
}
767

    
768
function is_user_cert($certref) {
769
	global $config;
770
	if (!is_array($config['system']['user'])) {
771
		return;
772
	}
773
	foreach ($config['system']['user'] as $user) {
774
		if (!is_array($user['cert'])) {
775
			continue;
776
		}
777
		foreach ($user['cert'] as $cert) {
778
			if ($certref == $cert) {
779
				return true;
780
			}
781
		}
782
	}
783
	return false;
784
}
785

    
786
function is_openvpn_server_cert($certref) {
787
	global $config;
788
	if (!is_array($config['openvpn']['openvpn-server'])) {
789
		return;
790
	}
791
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
792
		if ($ovpns['certref'] == $certref) {
793
			return true;
794
		}
795
	}
796
	return false;
797
}
798

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

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

    
825
function is_webgui_cert($certref) {
826
	global $config;
827
	if (($config['system']['webgui']['ssl-certref'] == $certref) &&
828
	    ($config['system']['webgui']['protocol'] != "http")) {
829
		return true;
830
	}
831
}
832

    
833
function is_package_cert($certref) {
834
	$pluginparams = array();
835
	$pluginparams['type'] = 'certificates';
836
	$pluginparams['event'] = 'used_certificates';
837

    
838
	$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
839

    
840
	/* Check if any package is using certificate */
841
	foreach ($certificates_used_by_packages as $name => $package) {
842
		if (is_array($package['certificatelist'][$certref]) &&
843
		    isset($package['certificatelist'][$certref]) > 0) {
844
			return true;
845
		}
846
	}
847
}
848

    
849
function is_captiveportal_cert($certref) {
850
	global $config;
851
	if (!is_array($config['captiveportal'])) {
852
		return;
853
	}
854
	foreach ($config['captiveportal'] as $portal) {
855
		if (isset($portal['enable']) && isset($portal['httpslogin']) && ($portal['certref'] == $certref)) {
856
			return true;
857
		}
858
	}
859
	return false;
860
}
861

    
862
function cert_in_use($certref) {
863

    
864
	return (is_webgui_cert($certref) ||
865
		is_user_cert($certref) ||
866
		is_openvpn_server_cert($certref) ||
867
		is_openvpn_client_cert($certref) ||
868
		is_ipsec_cert($certref) ||
869
		is_captiveportal_cert($certref) ||
870
		is_package_cert($certref));
871
}
872

    
873
function cert_usedby_description($refid, $certificates_used_by_packages) {
874
	$result = "";
875
	if (is_array($certificates_used_by_packages)) {
876
		foreach ($certificates_used_by_packages as $name => $package) {
877
			if (isset($package['certificatelist'][$refid])) {
878
				$hint = "" ;
879
				if (is_array($package['certificatelist'][$refid])) {
880
					foreach ($package['certificatelist'][$refid] as $cert_used) {
881
						$hint = $hint . $cert_used['usedby']."\n";
882
					}
883
				}
884
				$count = count($package['certificatelist'][$refid]);
885
				$result .= "<div title='".htmlspecialchars($hint)."'>";
886
				$result .= htmlspecialchars($package['pkgname'])." ($count)<br />";
887
				$result .= "</div>";
888
			}
889
		}
890
	}
891
	return $result;
892
}
893

    
894
function crl_create(& $crl, $caref, $name, $serial = 0, $lifetime = 9999) {
895
	global $config;
896
	$ca =& lookup_ca($caref);
897
	if (!$ca) {
898
		return false;
899
	}
900
	$crl['descr'] = $name;
901
	$crl['caref'] = $caref;
902
	$crl['serial'] = $serial;
903
	$crl['lifetime'] = $lifetime;
904
	$crl['cert'] = array();
905
	$crl_res = crl_update($crl);
906
	$config['crl'][] = $crl;
907
	return $crl_res;
908
}
909

    
910
function crl_update(& $crl) {
911
	global $config;
912
	$ca =& lookup_ca($crl['caref']);
913
	if (!$ca) {
914
		return false;
915
	}
916
	// If we have text but no certs, it was imported and cannot be updated.
917
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert']))) {
918
		return false;
919
	}
920
	$crl['serial']++;
921
	$ca_str_crt = base64_decode($ca['crt']);
922
	$ca_str_key = base64_decode($ca['prv']);
923
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
924
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
925
		foreach ($crl['cert'] as $cert) {
926
			openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
927
		}
928
	}
929
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
930
	$crl['text'] = base64_encode($crl_text);
931
	return $crl_res;
932
}
933

    
934
function cert_revoke($cert, & $crl, $reason = OCSP_REVOKED_STATUS_UNSPECIFIED) {
935
	global $config;
936
	if (is_cert_revoked($cert, $crl['refid'])) {
937
		return true;
938
	}
939
	// If we have text but no certs, it was imported and cannot be updated.
940
	if (!is_crl_internal($crl)) {
941
		return false;
942
	}
943
	$cert["reason"] = $reason;
944
	$cert["revoke_time"] = time();
945
	$crl["cert"][] = $cert;
946
	crl_update($crl);
947
	return true;
948
}
949

    
950
function cert_unrevoke($cert, & $crl) {
951
	global $config;
952
	if (!is_crl_internal($crl)) {
953
		return false;
954
	}
955
	foreach ($crl['cert'] as $id => $rcert) {
956
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
957
			unset($crl['cert'][$id]);
958
			if (count($crl['cert']) == 0) {
959
				// Protect against accidentally switching the type to imported, for older CRLs
960
				if (!isset($crl['method'])) {
961
					$crl['method'] = "internal";
962
				}
963
				crl_update($crl);
964
			} else {
965
				crl_update($crl);
966
			}
967
			return true;
968
		}
969
	}
970
	return false;
971
}
972

    
973
/* Compare two certificates to see if they match. */
974
function cert_compare($cert1, $cert2) {
975
	/* Ensure two certs are identical by first checking that their issuers match, then
976
		subjects, then serial numbers, and finally the moduli. Anything less strict
977
		could accidentally count two similar, but different, certificates as
978
		being identical. */
979
	$c1 = base64_decode($cert1['crt']);
980
	$c2 = base64_decode($cert2['crt']);
981
	if ((cert_get_issuer($c1, false) == cert_get_issuer($c2, false)) &&
982
	    (cert_get_subject($c1, false) == cert_get_subject($c2, false)) &&
983
	    (cert_get_serial($c1, false) == cert_get_serial($c2, false)) &&
984
	    (cert_get_publickey($c1, false) == cert_get_publickey($c2, false))) {
985
		return true;
986
	}
987
	return false;
988
}
989

    
990
function is_cert_revoked($cert, $crlref = "") {
991
	global $config;
992
	if (!is_array($config['crl'])) {
993
		return false;
994
	}
995

    
996
	if (!empty($crlref)) {
997
		$crl = lookup_crl($crlref);
998
		if (!is_array($crl['cert'])) {
999
			return false;
1000
		}
1001
		foreach ($crl['cert'] as $rcert) {
1002
			if (cert_compare($rcert, $cert)) {
1003
				return true;
1004
			}
1005
		}
1006
	} else {
1007
		foreach ($config['crl'] as $crl) {
1008
			if (!is_array($crl['cert'])) {
1009
				continue;
1010
			}
1011
			foreach ($crl['cert'] as $rcert) {
1012
				if (cert_compare($rcert, $cert)) {
1013
					return true;
1014
				}
1015
			}
1016
		}
1017
	}
1018
	return false;
1019
}
1020

    
1021
function is_openvpn_server_crl($crlref) {
1022
	global $config;
1023
	if (!is_array($config['openvpn']['openvpn-server'])) {
1024
		return;
1025
	}
1026
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
1027
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref)) {
1028
			return true;
1029
		}
1030
	}
1031
	return false;
1032
}
1033

    
1034
// Keep this general to allow for future expansion. See cert_in_use() above.
1035
function crl_in_use($crlref) {
1036
	return (is_openvpn_server_crl($crlref));
1037
}
1038

    
1039
function is_crl_internal($crl) {
1040
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
1041
}
1042

    
1043
function cert_get_cn($crt, $isref = false) {
1044
	/* If this is a certref, not an actual cert, look up the cert first */
1045
	if ($isref) {
1046
		$cert = lookup_cert($crt);
1047
		/* If it's not a valid cert, bail. */
1048
		if (!(is_array($cert) && !empty($cert['crt']))) {
1049
			return "";
1050
		}
1051
		$cert = $cert['crt'];
1052
	} else {
1053
		$cert = $crt;
1054
	}
1055
	$sub = cert_get_subject_array($cert);
1056
	if (is_array($sub)) {
1057
		foreach ($sub as $s) {
1058
			if (strtoupper($s['a']) == "CN") {
1059
				return $s['v'];
1060
			}
1061
		}
1062
	}
1063
	return "";
1064
}
1065

    
1066
function cert_escape_x509_chars($str, $reverse = false) {
1067
	/* Characters which need escaped when present in x.509 fields.
1068
	 * See https://www.ietf.org/rfc/rfc4514.txt
1069
	 *
1070
	 * The backslash (\) must be listed first in these arrays!
1071
	 */
1072
	$cert_directory_string_special_chars = array('\\', '"', '#', '+', ',', ';', '<', '=', '>');
1073
	$cert_directory_string_special_chars_esc = array('\\\\', '\"', '\#', '\+', '\,', '\;', '\<', '\=', '\>');
1074
	if ($reverse) {
1075
		return str_replace($cert_directory_string_special_chars_esc, $cert_directory_string_special_chars, $str);
1076
	} else {
1077
		/* First unescape and then escape again, to prevent possible double escaping. */
1078
		return str_replace($cert_directory_string_special_chars, $cert_directory_string_special_chars_esc, cert_escape_x509_chars($str, true));
1079
	}
1080
}
1081

    
1082
function cert_add_altname_type($str) {
1083
	$type = "";
1084
	if (is_ipaddr($str)) {
1085
		$type = "IP";
1086
	} elseif (is_hostname($str, true)) {
1087
		$type = "DNS";
1088
	} elseif (is_URL($str)) {
1089
		$type = "URI";
1090
	} elseif (filter_var($str, FILTER_VALIDATE_EMAIL)) {
1091
		$type = "email";
1092
	}
1093
	if (!empty($type)) {
1094
		return "{$type}:" . cert_escape_x509_chars($str);
1095
	} else {
1096
		return null;
1097
	}
1098
}
1099

    
1100
function cert_type_config_section($type) {
1101
	switch ($type) {
1102
		case "ca":
1103
			$cert_type = "v3_ca";
1104
			break;
1105
		case "server":
1106
		case "self-signed":
1107
			$cert_type = "server";
1108
			break;
1109
		default:
1110
			$cert_type = "usr_cert";
1111
			break;
1112
	}
1113
	return $cert_type;
1114
}
1115

    
1116
?>
(6-6/55)