Project

General

Profile

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