Project

General

Profile

Download (20.6 KB) Statistics
| Branch: | Tag: | Revision:
1 d43ad788 Scott Ullrich
<?php
2
/* $Id$ */
3
/*
4 ce77a9c4 Phil Davis
	certs.inc
5 c5f010aa jim-p
	Copyright (C) 2008 Shrew Soft Inc
6
	Copyright (C) 2010 Jim Pingle <jimp@pfsense.org>
7
	All rights reserved.
8 d43ad788 Scott Ullrich
9 ce77a9c4 Phil Davis
	Redistribution and use in source and binary forms, with or without
10
	modification, are permitted provided that the following conditions are met:
11 d43ad788 Scott Ullrich
12 ce77a9c4 Phil Davis
	1. Redistributions of source code must retain the above copyright notice,
13
	   this list of conditions and the following disclaimer.
14 d43ad788 Scott Ullrich
15 ce77a9c4 Phil Davis
	2. Redistributions in binary form must reproduce the above copyright
16
	   notice, this list of conditions and the following disclaimer in the
17
	   documentation and/or other materials provided with the distribution.
18 d43ad788 Scott Ullrich
19 ce77a9c4 Phil Davis
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
	POSSIBILITY OF SUCH DAMAGE.
29 d43ad788 Scott Ullrich
30 ce77a9c4 Phil Davis
	pfSense_MODULE:	certificate_manager
31 d43ad788 Scott Ullrich
*/
32
33 87b4deb2 jim-p
define("OPEN_SSL_CONF_PATH", "/etc/ssl/openssl.cnf");
34
35 d43ad788 Scott Ullrich
require_once("functions.inc");
36
37 e09b941d jim-p
global $openssl_digest_algs;
38 ca621902 jim-p
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
39
40 e09b941d jim-p
global $openssl_crl_status;
41
$openssl_crl_status = array(
42
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
43
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
44
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
45
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
46
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
47
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
48
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
49
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
50
);
51
52 d43ad788 Scott Ullrich
function & lookup_ca($refid) {
53
	global $config;
54
55 1e0b1727 Phil Davis
	if (is_array($config['ca'])) {
56
		foreach ($config['ca'] as & $ca) {
57
			if ($ca['refid'] == $refid) {
58 d43ad788 Scott Ullrich
				return $ca;
59 1e0b1727 Phil Davis
			}
60
		}
61
	}
62 d43ad788 Scott Ullrich
63
	return false;
64
}
65
66
function & lookup_ca_by_subject($subject) {
67
	global $config;
68
69 1e0b1727 Phil Davis
	if (is_array($config['ca'])) {
70
		foreach ($config['ca'] as & $ca) {
71 d43ad788 Scott Ullrich
			$ca_subject = cert_get_subject($ca['crt']);
72 1e0b1727 Phil Davis
			if ($ca_subject == $subject) {
73 d43ad788 Scott Ullrich
				return $ca;
74 1e0b1727 Phil Davis
			}
75 d43ad788 Scott Ullrich
		}
76 1e0b1727 Phil Davis
	}
77 d43ad788 Scott Ullrich
78
	return false;
79
}
80
81
function & lookup_cert($refid) {
82
	global $config;
83
84 1e0b1727 Phil Davis
	if (is_array($config['cert'])) {
85
		foreach ($config['cert'] as & $cert) {
86
			if ($cert['refid'] == $refid) {
87 d43ad788 Scott Ullrich
				return $cert;
88 1e0b1727 Phil Davis
			}
89
		}
90
	}
91 d43ad788 Scott Ullrich
92
	return false;
93
}
94
95 c5f010aa jim-p
function & lookup_cert_by_name($name) {
96
	global $config;
97 1e0b1727 Phil Davis
	if (is_array($config['cert'])) {
98
		foreach ($config['cert'] as & $cert) {
99
			if ($cert['descr'] == $name) {
100 c5f010aa jim-p
				return $cert;
101 1e0b1727 Phil Davis
			}
102
		}
103
	}
104 c5f010aa jim-p
}
105
106
function & lookup_crl($refid) {
107
	global $config;
108
109 1e0b1727 Phil Davis
	if (is_array($config['crl'])) {
110
		foreach ($config['crl'] as & $crl) {
111
			if ($crl['refid'] == $refid) {
112 c5f010aa jim-p
				return $crl;
113 1e0b1727 Phil Davis
			}
114
		}
115
	}
116 c5f010aa jim-p
117
	return false;
118
}
119
120 d43ad788 Scott Ullrich
function ca_chain_array(& $cert) {
121 1e0b1727 Phil Davis
	if ($cert['caref']) {
122 d43ad788 Scott Ullrich
		$chain = array();
123 5289dc57 jim-p
		$crt = lookup_ca($cert['caref']);
124 d43ad788 Scott Ullrich
		$chain[] = $crt;
125
		while ($crt) {
126
			$caref = $crt['caref'];
127 1e0b1727 Phil Davis
			if ($caref) {
128 5289dc57 jim-p
				$crt = lookup_ca($caref);
129 1e0b1727 Phil Davis
			} else {
130 d43ad788 Scott Ullrich
				$crt = false;
131 1e0b1727 Phil Davis
			}
132
			if ($crt) {
133 d43ad788 Scott Ullrich
				$chain[] = $crt;
134 1e0b1727 Phil Davis
			}
135 d43ad788 Scott Ullrich
		}
136
		return $chain;
137
	}
138
	return false;
139
}
140
141
function ca_chain(& $cert) {
142 1e0b1727 Phil Davis
	if ($cert['caref']) {
143 d43ad788 Scott Ullrich
		$ca = "";
144
		$cas = ca_chain_array($cert);
145 1e0b1727 Phil Davis
		if (is_array($cas)) {
146
			foreach ($cas as & $ca_cert) {
147 d43ad788 Scott Ullrich
				$ca .= base64_decode($ca_cert['crt']);
148
				$ca .= "\n";
149
			}
150 1e0b1727 Phil Davis
		}
151 d43ad788 Scott Ullrich
		return $ca;
152
	}
153
	return "";
154
}
155
156 6c07db48 Phil Davis
function ca_import(& $ca, $str, $key = "", $serial = 0) {
157 d43ad788 Scott Ullrich
	global $config;
158
159
	$ca['crt'] = base64_encode($str);
160 1e0b1727 Phil Davis
	if (!empty($key)) {
161 ecefc738 jim-p
		$ca['prv'] = base64_encode($key);
162 1e0b1727 Phil Davis
	}
163
	if (!empty($serial)) {
164 bfa992bc jim-p
		$ca['serial'] = $serial;
165 1e0b1727 Phil Davis
	}
166 d43ad788 Scott Ullrich
	$subject = cert_get_subject($str, false);
167
	$issuer = cert_get_issuer($str, false);
168 1e0b1727 Phil Davis
169 d43ad788 Scott Ullrich
	// Find my issuer unless self-signed
170 1e0b1727 Phil Davis
	if ($issuer <> $subject) {
171 d43ad788 Scott Ullrich
		$issuer_crt =& lookup_ca_by_subject($issuer);
172 1e0b1727 Phil Davis
		if ($issuer_crt) {
173 d43ad788 Scott Ullrich
			$ca['caref'] = $issuer_crt['refid'];
174 1e0b1727 Phil Davis
		}
175 d43ad788 Scott Ullrich
	}
176
177
	/* Correct if child certificate was loaded first */
178 1e0b1727 Phil Davis
	if (is_array($config['ca'])) {
179
		foreach ($config['ca'] as & $oca) {
180 d43ad788 Scott Ullrich
			$issuer = cert_get_issuer($oca['crt']);
181 086cf944 Phil Davis
			if ($ca['refid'] <> $oca['refid'] && $issuer == $subject) {
182 d43ad788 Scott Ullrich
				$oca['caref'] = $ca['refid'];
183 1e0b1727 Phil Davis
			}
184 d43ad788 Scott Ullrich
		}
185 1e0b1727 Phil Davis
	}
186
	if (is_array($config['cert'])) {
187
		foreach ($config['cert'] as & $cert) {
188 d43ad788 Scott Ullrich
			$issuer = cert_get_issuer($cert['crt']);
189 086cf944 Phil Davis
			if ($issuer == $subject) {
190 d43ad788 Scott Ullrich
				$cert['caref'] = $ca['refid'];
191 1e0b1727 Phil Davis
			}
192 d43ad788 Scott Ullrich
		}
193 1e0b1727 Phil Davis
	}
194 d43ad788 Scott Ullrich
	return true;
195
}
196
197 ca621902 jim-p
function ca_create(& $ca, $keylen, $lifetime, $dn, $digest_alg = "sha256") {
198 d43ad788 Scott Ullrich
199
	$args = array(
200 87b4deb2 jim-p
		"x509_extensions" => "v3_ca",
201 ca621902 jim-p
		"digest_alg" => $digest_alg,
202 51dbdcde Ermal Lu?i
		"private_key_bits" => (int)$keylen,
203 d43ad788 Scott Ullrich
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
204
		"encrypt_key" => false);
205
206
	// generate a new key pair
207 838e27bf jim-p
	$res_key = openssl_pkey_new($args);
208 1e0b1727 Phil Davis
	if (!$res_key) {
209
		return false;
210
	}
211 d43ad788 Scott Ullrich
212
	// generate a certificate signing request
213
	$res_csr = openssl_csr_new($dn, $res_key, $args);
214 1e0b1727 Phil Davis
	if (!$res_csr) {
215
		return false;
216
	}
217 d43ad788 Scott Ullrich
218
	// self sign the certificate
219
	$res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args);
220 1e0b1727 Phil Davis
	if (!$res_crt) {
221
		return false;
222
	}
223 d43ad788 Scott Ullrich
224
	// export our certificate data
225 1b6d9fa5 Evgeny Yurchenko
	if (!openssl_pkey_export($res_key, $str_key) ||
226 ae52d165 Renato Botelho
	    !openssl_x509_export($res_crt, $str_crt)) {
227 1b6d9fa5 Evgeny Yurchenko
		return false;
228 1e0b1727 Phil Davis
	}
229 d43ad788 Scott Ullrich
230
	// return our ca information
231
	$ca['crt'] = base64_encode($str_crt);
232
	$ca['prv'] = base64_encode($str_key);
233
	$ca['serial'] = 0;
234
235
	return true;
236
}
237
238 ca621902 jim-p
function ca_inter_create(& $ca, $keylen, $lifetime, $dn, $caref, $digest_alg = "sha256") {
239 95c8cf48 Evgeny Yurchenko
	// Create Intermediate Certificate Authority
240
	$signing_ca =& lookup_ca($caref);
241 1e0b1727 Phil Davis
	if (!$signing_ca) {
242 95c8cf48 Evgeny Yurchenko
		return false;
243 1e0b1727 Phil Davis
	}
244 95c8cf48 Evgeny Yurchenko
245
	$signing_ca_res_crt = openssl_x509_read(base64_decode($signing_ca['crt']));
246
	$signing_ca_res_key = openssl_pkey_get_private(array(0 => base64_decode($signing_ca['prv']) , 1 => ""));
247 1e0b1727 Phil Davis
	if (!$signing_ca_res_crt || !$signing_ca_res_key) {
248
		return false;
249
	}
250 95c8cf48 Evgeny Yurchenko
	$signing_ca_serial = ++$signing_ca['serial'];
251
252
	$args = array(
253 87b4deb2 jim-p
		"x509_extensions" => "v3_ca",
254 ca621902 jim-p
		"digest_alg" => $digest_alg,
255 95c8cf48 Evgeny Yurchenko
		"private_key_bits" => (int)$keylen,
256
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
257
		"encrypt_key" => false);
258
259
	// generate a new key pair
260
	$res_key = openssl_pkey_new($args);
261 1e0b1727 Phil Davis
	if (!$res_key) {
262
		return false;
263
	}
264 95c8cf48 Evgeny Yurchenko
265
	// generate a certificate signing request
266
	$res_csr = openssl_csr_new($dn, $res_key, $args);
267 1e0b1727 Phil Davis
	if (!$res_csr) {
268
		return false;
269
	}
270 95c8cf48 Evgeny Yurchenko
271
	// Sign the certificate
272
	$res_crt = openssl_csr_sign($res_csr, $signing_ca_res_crt, $signing_ca_res_key, $lifetime, $args, $signing_ca_serial);
273 1e0b1727 Phil Davis
	if (!$res_crt) {
274
		return false;
275
	}
276 95c8cf48 Evgeny Yurchenko
277
	// export our certificate data
278
	if (!openssl_pkey_export($res_key, $str_key) ||
279 ae52d165 Renato Botelho
	    !openssl_x509_export($res_crt, $str_crt)) {
280 95c8cf48 Evgeny Yurchenko
		return false;
281 1e0b1727 Phil Davis
	}
282 95c8cf48 Evgeny Yurchenko
283
	// return our ca information
284
	$ca['crt'] = base64_encode($str_crt);
285
	$ca['prv'] = base64_encode($str_key);
286
	$ca['serial'] = 0;
287
288
	return true;
289
}
290
291 d43ad788 Scott Ullrich
function cert_import(& $cert, $crt_str, $key_str) {
292
293
	$cert['crt'] = base64_encode($crt_str);
294
	$cert['prv'] = base64_encode($key_str);
295
296
	$subject = cert_get_subject($crt_str, false);
297
	$issuer = cert_get_issuer($crt_str, false);
298 1e0b1727 Phil Davis
299 d43ad788 Scott Ullrich
	// Find my issuer unless self-signed
300 1e0b1727 Phil Davis
	if ($issuer <> $subject) {
301 d43ad788 Scott Ullrich
		$issuer_crt =& lookup_ca_by_subject($issuer);
302 1e0b1727 Phil Davis
		if ($issuer_crt) {
303 d43ad788 Scott Ullrich
			$cert['caref'] = $issuer_crt['refid'];
304 1e0b1727 Phil Davis
		}
305 d43ad788 Scott Ullrich
	}
306
	return true;
307
}
308
309 6c07db48 Phil Davis
function cert_create(& $cert, $caref, $keylen, $lifetime, $dn, $type = "user", $digest_alg = "sha256") {
310 d43ad788 Scott Ullrich
311 7c4c77ee jim-p
	$cert['type'] = $type;
312 d43ad788 Scott Ullrich
313 7c4c77ee jim-p
	if ($type != "self-signed") {
314
		$cert['caref'] = $caref;
315
		$ca =& lookup_ca($caref);
316 1e0b1727 Phil Davis
		if (!$ca) {
317 7c4c77ee jim-p
			return false;
318 1e0b1727 Phil Davis
		}
319 7c4c77ee jim-p
320
		$ca_str_crt = base64_decode($ca['crt']);
321
		$ca_str_key = base64_decode($ca['prv']);
322
		$ca_res_crt = openssl_x509_read($ca_str_crt);
323
		$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
324 1e0b1727 Phil Davis
		if (!$ca_res_key) {
325
			return false;
326
		}
327 7c4c77ee jim-p
		$ca_serial = ++$ca['serial'];
328
	}
329 d43ad788 Scott Ullrich
330 7aaabd69 jim-p
	switch ($type) {
331
		case "ca":
332
			$cert_type = "v3_ca";
333
			break;
334
		case "server":
335 7c4c77ee jim-p
		case "self-signed":
336 7aaabd69 jim-p
			$cert_type = "server";
337
			break;
338
		default:
339
			$cert_type = "usr_cert";
340
			break;
341
	}
342
343 3cb773da yarick123
	// in case of using Subject Alternative Names use other sections (with postfix '_san')
344
	// pass subjectAltName over environment variable 'SAN'
345
	if ($dn['subjectAltName']) {
346
		putenv("SAN={$dn['subjectAltName']}"); // subjectAltName can be set _only_ via configuration file
347
		$cert_type .= '_san';
348
		unset($dn['subjectAltName']);
349
	}
350
351 d43ad788 Scott Ullrich
	$args = array(
352 7aaabd69 jim-p
		"x509_extensions" => $cert_type,
353 ca621902 jim-p
		"digest_alg" => $digest_alg,
354 51dbdcde Ermal Lu?i
		"private_key_bits" => (int)$keylen,
355 d43ad788 Scott Ullrich
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
356
		"encrypt_key" => false);
357
358
	// generate a new key pair
359 838e27bf jim-p
	$res_key = openssl_pkey_new($args);
360 1e0b1727 Phil Davis
	if (!$res_key) {
361
		return false;
362
	}
363 d43ad788 Scott Ullrich
364 7c4c77ee jim-p
	// If this is a self-signed cert, blank out the CA and sign with the cert's key
365
	if ($type == "self-signed") {
366
		$ca           = null;
367
		$ca_res_crt   = null;
368
		$ca_res_key   = $res_key;
369
		$ca_serial    = 0;
370
		$cert['type'] = "server";
371
	}
372
373 d43ad788 Scott Ullrich
	// generate a certificate signing request
374
	$res_csr = openssl_csr_new($dn, $res_key, $args);
375 1e0b1727 Phil Davis
	if (!$res_csr) {
376
		return false;
377
	}
378 d43ad788 Scott Ullrich
379 7c4c77ee jim-p
	// sign the certificate using an internal CA
380 d43ad788 Scott Ullrich
	$res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime,
381
				 $args, $ca_serial);
382 1e0b1727 Phil Davis
	if (!$res_crt) {
383
		return false;
384
	}
385 d43ad788 Scott Ullrich
386
	// export our certificate data
387 22b380aa Evgeny Yurchenko
	if (!openssl_pkey_export($res_key, $str_key) ||
388 ae52d165 Renato Botelho
	    !openssl_x509_export($res_crt, $str_crt)) {
389 22b380aa Evgeny Yurchenko
		return false;
390 1e0b1727 Phil Davis
	}
391 d43ad788 Scott Ullrich
392
	// return our certificate information
393
	$cert['crt'] = base64_encode($str_crt);
394
	$cert['prv'] = base64_encode($str_key);
395
396
	return true;
397
}
398
399 ca621902 jim-p
function csr_generate(& $cert, $keylen, $dn, $digest_alg = "sha256") {
400 d43ad788 Scott Ullrich
401
	$args = array(
402 87b4deb2 jim-p
		"x509_extensions" => "v3_req",
403 ca621902 jim-p
		"digest_alg" => $digest_alg,
404 3198b8d3 Ermal Lu?i
		"private_key_bits" => (int)$keylen,
405 d43ad788 Scott Ullrich
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
406
		"encrypt_key" => false);
407
408
	// generate a new key pair
409 838e27bf jim-p
	$res_key = openssl_pkey_new($args);
410 1e0b1727 Phil Davis
	if (!$res_key) {
411
		return false;
412
	}
413 d43ad788 Scott Ullrich
414
	// generate a certificate signing request
415
	$res_csr = openssl_csr_new($dn, $res_key, $args);
416 1e0b1727 Phil Davis
	if (!$res_csr) {
417
		return false;
418
	}
419 d43ad788 Scott Ullrich
420
	// export our request data
421 22b380aa Evgeny Yurchenko
	if (!openssl_pkey_export($res_key, $str_key) ||
422 ae52d165 Renato Botelho
	    !openssl_csr_export($res_csr, $str_csr)) {
423 22b380aa Evgeny Yurchenko
		return false;
424 1e0b1727 Phil Davis
	}
425 d43ad788 Scott Ullrich
426
	// return our request information
427
	$cert['csr'] = base64_encode($str_csr);
428
	$cert['prv'] = base64_encode($str_key);
429
430
	return true;
431
}
432
433
function csr_complete(& $cert, $str_crt) {
434
435
	// return our request information
436
	$cert['crt'] = base64_encode($str_crt);
437
	unset($cert['csr']);
438
439
	return true;
440
}
441
442
function csr_get_subject($str_crt, $decode = true) {
443
444 1e0b1727 Phil Davis
	if ($decode) {
445 d43ad788 Scott Ullrich
		$str_crt = base64_decode($str_crt);
446 1e0b1727 Phil Davis
	}
447 d43ad788 Scott Ullrich
448
	$components = openssl_csr_get_subject($str_crt);
449
450 1e0b1727 Phil Davis
	if (empty($components) || !is_array($components)) {
451 d43ad788 Scott Ullrich
		return "unknown";
452 1e0b1727 Phil Davis
	}
453 d43ad788 Scott Ullrich
454 311f93cd Ermal
	ksort($components);
455 d43ad788 Scott Ullrich
	foreach ($components as $a => $v) {
456 1e0b1727 Phil Davis
		if (!strlen($subject)) {
457 d43ad788 Scott Ullrich
			$subject = "{$a}={$v}";
458 1e0b1727 Phil Davis
		} else {
459 d43ad788 Scott Ullrich
			$subject = "{$a}={$v}, {$subject}";
460 1e0b1727 Phil Davis
		}
461 d43ad788 Scott Ullrich
	}
462
463
	return $subject;
464
}
465
466
function cert_get_subject($str_crt, $decode = true) {
467
468 1e0b1727 Phil Davis
	if ($decode) {
469 d43ad788 Scott Ullrich
		$str_crt = base64_decode($str_crt);
470 1e0b1727 Phil Davis
	}
471 d43ad788 Scott Ullrich
472
	$inf_crt = openssl_x509_parse($str_crt);
473
	$components = $inf_crt['subject'];
474
475 1e0b1727 Phil Davis
	if (empty($components) || !is_array($components)) {
476 d43ad788 Scott Ullrich
		return "unknown";
477 1e0b1727 Phil Davis
	}
478 d43ad788 Scott Ullrich
479 b89c34aa Ermal
	ksort($components);
480 d43ad788 Scott Ullrich
	foreach ($components as $a => $v) {
481 b89c34aa Ermal
		if (is_array($v)) {
482
			ksort($v);
483 5479df47 jim-p
			foreach ($v as $w) {
484
				$asubject = "{$a}={$w}";
485
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
486
			}
487 b89c34aa Ermal
		} else {
488 5479df47 jim-p
			$asubject = "{$a}={$v}";
489
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
490
		}
491 d43ad788 Scott Ullrich
	}
492
493
	return $subject;
494
}
495
496
function cert_get_subject_array($crt) {
497
	$str_crt = base64_decode($crt);
498
	$inf_crt = openssl_x509_parse($str_crt);
499
	$components = $inf_crt['subject'];
500 e4d7a064 jim-p
501 1e0b1727 Phil Davis
	if (!is_array($components)) {
502 e4d7a064 jim-p
		return;
503 1e0b1727 Phil Davis
	}
504 e4d7a064 jim-p
505 d43ad788 Scott Ullrich
	$subject_array = array();
506
507 1e0b1727 Phil Davis
	foreach ($components as $a => $v) {
508 d43ad788 Scott Ullrich
		$subject_array[] = array('a' => $a, 'v' => $v);
509 1e0b1727 Phil Davis
	}
510 d43ad788 Scott Ullrich
511
	return $subject_array;
512
}
513
514 a84eb838 jim-p
function cert_get_subject_hash($crt) {
515
	$str_crt = base64_decode($crt);
516
	$inf_crt = openssl_x509_parse($str_crt);
517
	return $inf_crt['subject'];
518
}
519
520 d43ad788 Scott Ullrich
function cert_get_issuer($str_crt, $decode = true) {
521
522 1e0b1727 Phil Davis
	if ($decode) {
523 d43ad788 Scott Ullrich
		$str_crt = base64_decode($str_crt);
524 1e0b1727 Phil Davis
	}
525 d43ad788 Scott Ullrich
526
	$inf_crt = openssl_x509_parse($str_crt);
527
	$components = $inf_crt['issuer'];
528 1e0b1727 Phil Davis
529
	if (empty($components) || !is_array($components)) {
530 d43ad788 Scott Ullrich
		return "unknown";
531 1e0b1727 Phil Davis
	}
532 5ca13f69 Ermal
533
	ksort($components);
534 d43ad788 Scott Ullrich
	foreach ($components as $a => $v) {
535 2a08b814 vsquared56
		if (is_array($v)) {
536
			ksort($v);
537
			foreach ($v as $w) {
538
				$aissuer = "{$a}={$w}";
539
				$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
540
			}
541
		} else {
542
			$aissuer = "{$a}={$v}";
543
			$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
544
		}
545 d43ad788 Scott Ullrich
	}
546
547
	return $issuer;
548
}
549
550 a828210b yakatz
/* this function works on x509 (crt), rsa key (prv), and req(csr) */
551 1e0b1727 Phil Davis
function cert_get_modulus($str_crt, $decode = true, $type = "crt") {
552
	if ($decode) {
553 a828210b yakatz
		$str_crt = base64_decode($str_crt);
554 1e0b1727 Phil Davis
	}
555 a828210b yakatz
556
	$modulus = "";
557 086cf944 Phil Davis
	if (in_array($type, array("crt", "prv", "csr"))) {
558
		$type = str_replace(array("crt", "prv", "csr"), array("x509", "rsa", "req"), $type);
559 1e0b1727 Phil Davis
		$modulus = exec("echo \"{$str_crt}\" | openssl {$type} -noout -modulus");
560 a828210b yakatz
	}
561
	return $modulus;
562
}
563 1e0b1727 Phil Davis
function csr_get_modulus($str_crt, $decode = true) {
564 a828210b yakatz
	return cert_get_modulus($str_crt, $decode, "csr");
565
}
566 1379d66f jim-p
567
function cert_get_purpose($str_crt, $decode = true) {
568 1e0b1727 Phil Davis
	if ($decode) {
569 1379d66f jim-p
		$str_crt = base64_decode($str_crt);
570 1e0b1727 Phil Davis
	}
571 1379d66f jim-p
	$crt_details = openssl_x509_parse($str_crt);
572
	$purpose = array();
573
	$purpose['ca'] = (stristr($crt_details['extensions']['basicConstraints'], 'CA:TRUE') === false) ? 'No': 'Yes';
574
	$purpose['server'] = ($crt_details['extensions']['nsCertType'] == "SSL Server") ? 'Yes': 'No';
575
	return $purpose;
576
}
577
578 2b333210 jim-p
function cert_get_dates($str_crt, $decode = true) {
579 1e0b1727 Phil Davis
	if ($decode) {
580 2b333210 jim-p
		$str_crt = base64_decode($str_crt);
581 1e0b1727 Phil Davis
	}
582 2b333210 jim-p
	$crt_details = openssl_x509_parse($str_crt);
583 1e0b1727 Phil Davis
	if ($crt_details['validFrom_time_t'] > 0) {
584 2b333210 jim-p
		$start = date('r', $crt_details['validFrom_time_t']);
585 1e0b1727 Phil Davis
	}
586
	if ($crt_details['validTo_time_t'] > 0) {
587 2b333210 jim-p
		$end = date('r', $crt_details['validTo_time_t']);
588 1e0b1727 Phil Davis
	}
589 2b333210 jim-p
	return array($start, $end);
590
}
591
592 04761344 jim-p
function cert_get_serial($str_crt, $decode = true) {
593 1e0b1727 Phil Davis
	if ($decode) {
594 04761344 jim-p
		$str_crt = base64_decode($str_crt);
595 1e0b1727 Phil Davis
	}
596 04761344 jim-p
	$crt_details = openssl_x509_parse($str_crt);
597 1e0b1727 Phil Davis
	if (isset($crt_details['serialNumber']) && !empty($crt_details['serialNumber'])) {
598 04761344 jim-p
		return $crt_details['serialNumber'];
599 1e0b1727 Phil Davis
	} else {
600 04761344 jim-p
		return NULL;
601 1e0b1727 Phil Davis
	}
602 04761344 jim-p
}
603
604 1e0b1727 Phil Davis
function prv_get_modulus($str_crt, $decode = true) {
605 a828210b yakatz
	return cert_get_modulus($str_crt, $decode, "prv");
606
}
607
608 dea98903 jim-p
function is_user_cert($certref) {
609 dab2e769 jim-p
	global $config;
610 1e0b1727 Phil Davis
	if (!is_array($config['system']['user'])) {
611 dab2e769 jim-p
		return;
612 1e0b1727 Phil Davis
	}
613 dab2e769 jim-p
	foreach ($config['system']['user'] as $user) {
614 1e0b1727 Phil Davis
		if (!is_array($user['cert'])) {
615 dab2e769 jim-p
			continue;
616 1e0b1727 Phil Davis
		}
617 dab2e769 jim-p
		foreach ($user['cert'] as $cert) {
618 1e0b1727 Phil Davis
			if ($certref == $cert) {
619 dea98903 jim-p
				return true;
620 1e0b1727 Phil Davis
			}
621 dab2e769 jim-p
		}
622
	}
623 dea98903 jim-p
	return false;
624 dab2e769 jim-p
}
625
626 dea98903 jim-p
function is_openvpn_server_cert($certref) {
627 dab2e769 jim-p
	global $config;
628 1e0b1727 Phil Davis
	if (!is_array($config['openvpn']['openvpn-server'])) {
629 dea98903 jim-p
		return;
630 1e0b1727 Phil Davis
	}
631 dea98903 jim-p
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
632 1e0b1727 Phil Davis
		if ($ovpns['certref'] == $certref) {
633 dea98903 jim-p
			return true;
634 1e0b1727 Phil Davis
		}
635 dea98903 jim-p
	}
636
	return false;
637
}
638
639
function is_openvpn_client_cert($certref) {
640
	global $config;
641 1e0b1727 Phil Davis
	if (!is_array($config['openvpn']['openvpn-client'])) {
642 dea98903 jim-p
		return;
643 1e0b1727 Phil Davis
	}
644 dea98903 jim-p
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
645 1e0b1727 Phil Davis
		if ($ovpnc['certref'] == $certref) {
646 dea98903 jim-p
			return true;
647 1e0b1727 Phil Davis
		}
648 dea98903 jim-p
	}
649
	return false;
650
}
651
652
function is_ipsec_cert($certref) {
653
	global $config;
654 1e0b1727 Phil Davis
	if (!is_array($config['ipsec']['phase1'])) {
655 dea98903 jim-p
		return;
656 1e0b1727 Phil Davis
	}
657 dea98903 jim-p
	foreach ($config['ipsec']['phase1'] as $ipsec) {
658 1e0b1727 Phil Davis
		if ($ipsec['certref'] == $certref) {
659 dea98903 jim-p
			return true;
660 1e0b1727 Phil Davis
		}
661 dea98903 jim-p
	}
662
	return false;
663
}
664
665
function is_webgui_cert($certref) {
666
	global $config;
667 ae52d165 Renato Botelho
	if (($config['system']['webgui']['ssl-certref'] == $certref) &&
668
	    ($config['system']['webgui']['protocol'] != "http")) {
669 dea98903 jim-p
		return true;
670 1e0b1727 Phil Davis
	}
671 dea98903 jim-p
}
672
673 36f6ed35 bcyrill
function is_captiveportal_cert($certref) {
674
	global $config;
675 1e0b1727 Phil Davis
	if (!is_array($config['captiveportal'])) {
676 36f6ed35 bcyrill
		return;
677 1e0b1727 Phil Davis
	}
678 36f6ed35 bcyrill
	foreach ($config['captiveportal'] as $portal) {
679 1e0b1727 Phil Davis
		if (isset($portal['enable']) && isset($portal['httpslogin']) && ($portal['certref'] == $certref)) {
680 36f6ed35 bcyrill
			return true;
681 1e0b1727 Phil Davis
		}
682 36f6ed35 bcyrill
	}
683
	return false;
684
}
685
686 dea98903 jim-p
function cert_in_use($certref) {
687
	return (is_webgui_cert($certref) ||
688
		is_user_cert($certref) ||
689
		is_openvpn_server_cert($certref) ||
690
		is_openvpn_client_cert($certref) ||
691 36f6ed35 bcyrill
		is_ipsec_cert($certref) ||
692
		is_captiveportal_cert($certref));
693 dab2e769 jim-p
}
694
695 6c07db48 Phil Davis
function crl_create(& $crl, $caref, $name, $serial = 0, $lifetime = 9999) {
696 c5f010aa jim-p
	global $config;
697
	$ca =& lookup_ca($caref);
698 1e0b1727 Phil Davis
	if (!$ca) {
699 c5f010aa jim-p
		return false;
700 1e0b1727 Phil Davis
	}
701 f2a86ca9 jim-p
	$crl['descr'] = $name;
702 c5f010aa jim-p
	$crl['caref'] = $caref;
703
	$crl['serial'] = $serial;
704
	$crl['lifetime'] = $lifetime;
705
	$crl['cert'] = array();
706
	$crl_res = crl_update($crl);
707
	$config['crl'][] = $crl;
708
	return $crl_res;
709
}
710
711
function crl_update(& $crl) {
712
	global $config;
713
	$ca =& lookup_ca($crl['caref']);
714 1e0b1727 Phil Davis
	if (!$ca) {
715 c5f010aa jim-p
		return false;
716 1e0b1727 Phil Davis
	}
717 7b757d1b jim-p
	// If we have text but no certs, it was imported and cannot be updated.
718 1e0b1727 Phil Davis
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert']))) {
719 7b757d1b jim-p
		return false;
720 1e0b1727 Phil Davis
	}
721 c5f010aa jim-p
	$crl['serial']++;
722
	$ca_str_crt = base64_decode($ca['crt']);
723
	$ca_str_key = base64_decode($ca['prv']);
724
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
725 4bc2c676 jim-p
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
726
		foreach ($crl['cert'] as $cert) {
727
			openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
728
		}
729 c5f010aa jim-p
	}
730
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
731
	$crl['text'] = base64_encode($crl_text);
732
	return $crl_res;
733
}
734
735 6c07db48 Phil Davis
function cert_revoke($cert, & $crl, $reason = OCSP_REVOKED_STATUS_UNSPECIFIED) {
736 c5f010aa jim-p
	global $config;
737 1e0b1727 Phil Davis
	if (is_cert_revoked($cert, $crl['refid'])) {
738 c5f010aa jim-p
		return true;
739 1e0b1727 Phil Davis
	}
740 7b757d1b jim-p
	// If we have text but no certs, it was imported and cannot be updated.
741 1e0b1727 Phil Davis
	if (!is_crl_internal($crl)) {
742 7b757d1b jim-p
		return false;
743 1e0b1727 Phil Davis
	}
744 c5f010aa jim-p
	$cert["reason"] = $reason;
745
	$cert["revoke_time"] = time();
746
	$crl["cert"][] = $cert;
747
	crl_update($crl);
748 fb3f1993 jim-p
	return true;
749 c5f010aa jim-p
}
750
751
function cert_unrevoke($cert, & $crl) {
752
	global $config;
753 1e0b1727 Phil Davis
	if (!is_crl_internal($crl)) {
754 7b757d1b jim-p
		return false;
755 1e0b1727 Phil Davis
	}
756 c5f010aa jim-p
	foreach ($crl['cert'] as $id => $rcert) {
757 f2a86ca9 jim-p
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
758 c5f010aa jim-p
			unset($crl['cert'][$id]);
759 728003c8 jim-p
			if (count($crl['cert']) == 0) {
760
				// Protect against accidentally switching the type to imported, for older CRLs
761 1e0b1727 Phil Davis
				if (!isset($crl['method'])) {
762 728003c8 jim-p
					$crl['method'] = "internal";
763 1e0b1727 Phil Davis
				}
764 728003c8 jim-p
				crl_update($crl);
765 1e0b1727 Phil Davis
			} else {
766 a59831e7 jim-p
				crl_update($crl);
767 1e0b1727 Phil Davis
			}
768 c5f010aa jim-p
			return true;
769
		}
770
	}
771
	return false;
772
}
773
774 04761344 jim-p
/* Compare two certificates to see if they match. */
775
function cert_compare($cert1, $cert2) {
776
	/* Ensure two certs are identical by first checking that their issuers match, then
777
		subjects, then serial numbers, and finally the moduli. Anything less strict
778
		could accidentally count two similar, but different, certificates as
779
		being identical. */
780
	$c1 = base64_decode($cert1['crt']);
781
	$c2 = base64_decode($cert2['crt']);
782 ae52d165 Renato Botelho
	if ((cert_get_issuer($c1, false) == cert_get_issuer($c2, false)) &&
783
	    (cert_get_subject($c1, false) == cert_get_subject($c2, false)) &&
784
	    (cert_get_serial($c1, false) == cert_get_serial($c2, false)) &&
785
	    (cert_get_modulus($c1, false) == cert_get_modulus($c2, false))) {
786 04761344 jim-p
		return true;
787 1e0b1727 Phil Davis
	}
788 04761344 jim-p
	return false;
789
}
790
791 fb3f1993 jim-p
function is_cert_revoked($cert, $crlref = "") {
792 c5f010aa jim-p
	global $config;
793 1e0b1727 Phil Davis
	if (!is_array($config['crl'])) {
794 c5f010aa jim-p
		return false;
795 1e0b1727 Phil Davis
	}
796 c5f010aa jim-p
797 fb3f1993 jim-p
	if (!empty($crlref)) {
798 28ff7ace jim-p
		$crl = lookup_crl($crlref);
799 1e0b1727 Phil Davis
		if (!is_array($crl['cert'])) {
800 fb3f1993 jim-p
			return false;
801 1e0b1727 Phil Davis
		}
802 088ce869 jim-p
		foreach ($crl['cert'] as $rcert) {
803 1e0b1727 Phil Davis
			if (cert_compare($rcert, $cert)) {
804 c5f010aa jim-p
				return true;
805 1e0b1727 Phil Davis
			}
806 c5f010aa jim-p
		}
807 fb3f1993 jim-p
	} else {
808
		foreach ($config['crl'] as $crl) {
809 1e0b1727 Phil Davis
			if (!is_array($crl['cert'])) {
810 fb3f1993 jim-p
				continue;
811 1e0b1727 Phil Davis
			}
812 fb3f1993 jim-p
			foreach ($crl['cert'] as $rcert) {
813 1e0b1727 Phil Davis
				if (cert_compare($rcert, $cert)) {
814 fb3f1993 jim-p
					return true;
815 1e0b1727 Phil Davis
				}
816 fb3f1993 jim-p
			}
817
		}
818
	}
819
	return false;
820
}
821
822
function is_openvpn_server_crl($crlref) {
823
	global $config;
824 1e0b1727 Phil Davis
	if (!is_array($config['openvpn']['openvpn-server'])) {
825 fb3f1993 jim-p
		return;
826 1e0b1727 Phil Davis
	}
827 fb3f1993 jim-p
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
828 1e0b1727 Phil Davis
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref)) {
829 fb3f1993 jim-p
			return true;
830 1e0b1727 Phil Davis
		}
831 c5f010aa jim-p
	}
832
	return false;
833
}
834
835 fb3f1993 jim-p
// Keep this general to allow for future expansion. See cert_in_use() above.
836
function crl_in_use($crlref) {
837
	return (is_openvpn_server_crl($crlref));
838
}
839
840
function is_crl_internal($crl) {
841 728003c8 jim-p
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
842 fb3f1993 jim-p
}
843
844 b34b2b7d jim-p
function cert_get_cn($crt, $isref = false) {
845
	/* If this is a certref, not an actual cert, look up the cert first */
846
	if ($isref) {
847
		$cert = lookup_cert($crt);
848
		/* If it's not a valid cert, bail. */
849 1e0b1727 Phil Davis
		if (!(is_array($cert) && !empty($cert['crt']))) {
850 b34b2b7d jim-p
			return "";
851 1e0b1727 Phil Davis
		}
852 b34b2b7d jim-p
		$cert = $cert['crt'];
853
	} else {
854
		$cert = $crt;
855
	}
856
	$sub = cert_get_subject_array($cert);
857
	if (is_array($sub)) {
858
		foreach ($sub as $s) {
859 1e0b1727 Phil Davis
			if (strtoupper($s['a']) == "CN") {
860 b34b2b7d jim-p
				return $s['v'];
861 1e0b1727 Phil Davis
			}
862 b34b2b7d jim-p
		}
863
	}
864
	return "";
865
}
866
867 b89c34aa Ermal
?>