Project

General

Profile

Download (24.2 KB) Statistics
| Branch: | Tag: | Revision:
1 d43ad788 Scott Ullrich
<?php
2
/*
3 ac24dc24 Renato Botelho
 * certs.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6 81299b5c Renato Botelho
 * Copyright (c) 2008-2016 Rubicon Communications, LLC (Netgate)
7 ac24dc24 Renato Botelho
 * Copyright (c) 2008 Shrew Soft Inc. All rights reserved.
8
 * All rights reserved.
9
 *
10 b12ea3fb Renato Botelho
 * 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 ac24dc24 Renato Botelho
 *
14 b12ea3fb Renato Botelho
 * http://www.apache.org/licenses/LICENSE-2.0
15 ac24dc24 Renato Botelho
 *
16 b12ea3fb Renato Botelho
 * 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 ac24dc24 Renato Botelho
 */
22 d43ad788 Scott Ullrich
23 87b4deb2 jim-p
define("OPEN_SSL_CONF_PATH", "/etc/ssl/openssl.cnf");
24
25 d43ad788 Scott Ullrich
require_once("functions.inc");
26
27 e09b941d jim-p
global $openssl_digest_algs;
28 84141846 jim-p
$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512");
29 ca621902 jim-p
30 e09b941d jim-p
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 d43ad788 Scott Ullrich
function & lookup_ca($refid) {
43
	global $config;
44
45 1e0b1727 Phil Davis
	if (is_array($config['ca'])) {
46
		foreach ($config['ca'] as & $ca) {
47
			if ($ca['refid'] == $refid) {
48 d43ad788 Scott Ullrich
				return $ca;
49 1e0b1727 Phil Davis
			}
50
		}
51
	}
52 d43ad788 Scott Ullrich
53
	return false;
54
}
55
56
function & lookup_ca_by_subject($subject) {
57
	global $config;
58
59 1e0b1727 Phil Davis
	if (is_array($config['ca'])) {
60
		foreach ($config['ca'] as & $ca) {
61 d43ad788 Scott Ullrich
			$ca_subject = cert_get_subject($ca['crt']);
62 1e0b1727 Phil Davis
			if ($ca_subject == $subject) {
63 d43ad788 Scott Ullrich
				return $ca;
64 1e0b1727 Phil Davis
			}
65 d43ad788 Scott Ullrich
		}
66 1e0b1727 Phil Davis
	}
67 d43ad788 Scott Ullrich
68
	return false;
69
}
70
71
function & lookup_cert($refid) {
72
	global $config;
73
74 1e0b1727 Phil Davis
	if (is_array($config['cert'])) {
75
		foreach ($config['cert'] as & $cert) {
76
			if ($cert['refid'] == $refid) {
77 d43ad788 Scott Ullrich
				return $cert;
78 1e0b1727 Phil Davis
			}
79
		}
80
	}
81 d43ad788 Scott Ullrich
82
	return false;
83
}
84
85 c5f010aa jim-p
function & lookup_cert_by_name($name) {
86
	global $config;
87 1e0b1727 Phil Davis
	if (is_array($config['cert'])) {
88
		foreach ($config['cert'] as & $cert) {
89
			if ($cert['descr'] == $name) {
90 c5f010aa jim-p
				return $cert;
91 1e0b1727 Phil Davis
			}
92
		}
93
	}
94 c5f010aa jim-p
}
95
96
function & lookup_crl($refid) {
97
	global $config;
98
99 1e0b1727 Phil Davis
	if (is_array($config['crl'])) {
100
		foreach ($config['crl'] as & $crl) {
101
			if ($crl['refid'] == $refid) {
102 c5f010aa jim-p
				return $crl;
103 1e0b1727 Phil Davis
			}
104
		}
105
	}
106 c5f010aa jim-p
107
	return false;
108
}
109
110 d43ad788 Scott Ullrich
function ca_chain_array(& $cert) {
111 1e0b1727 Phil Davis
	if ($cert['caref']) {
112 d43ad788 Scott Ullrich
		$chain = array();
113 5289dc57 jim-p
		$crt = lookup_ca($cert['caref']);
114 d43ad788 Scott Ullrich
		$chain[] = $crt;
115
		while ($crt) {
116
			$caref = $crt['caref'];
117 1e0b1727 Phil Davis
			if ($caref) {
118 5289dc57 jim-p
				$crt = lookup_ca($caref);
119 1e0b1727 Phil Davis
			} else {
120 d43ad788 Scott Ullrich
				$crt = false;
121 1e0b1727 Phil Davis
			}
122
			if ($crt) {
123 d43ad788 Scott Ullrich
				$chain[] = $crt;
124 1e0b1727 Phil Davis
			}
125 d43ad788 Scott Ullrich
		}
126
		return $chain;
127
	}
128
	return false;
129
}
130
131
function ca_chain(& $cert) {
132 1e0b1727 Phil Davis
	if ($cert['caref']) {
133 d43ad788 Scott Ullrich
		$ca = "";
134
		$cas = ca_chain_array($cert);
135 1e0b1727 Phil Davis
		if (is_array($cas)) {
136
			foreach ($cas as & $ca_cert) {
137 d43ad788 Scott Ullrich
				$ca .= base64_decode($ca_cert['crt']);
138
				$ca .= "\n";
139
			}
140 1e0b1727 Phil Davis
		}
141 d43ad788 Scott Ullrich
		return $ca;
142
	}
143
	return "";
144
}
145
146 ab63443a jim-p
function ca_import(& $ca, $str, $key = "", $serial = "") {
147 d43ad788 Scott Ullrich
	global $config;
148
149
	$ca['crt'] = base64_encode($str);
150 1e0b1727 Phil Davis
	if (!empty($key)) {
151 ecefc738 jim-p
		$ca['prv'] = base64_encode($key);
152 1e0b1727 Phil Davis
	}
153 ab63443a jim-p
	if (empty($serial)) {
154
		$ca['serial'] = 0;
155
	} else {
156 bfa992bc jim-p
		$ca['serial'] = $serial;
157 1e0b1727 Phil Davis
	}
158 d43ad788 Scott Ullrich
	$subject = cert_get_subject($str, false);
159
	$issuer = cert_get_issuer($str, false);
160 1e0b1727 Phil Davis
161 d43ad788 Scott Ullrich
	// Find my issuer unless self-signed
162 1e0b1727 Phil Davis
	if ($issuer <> $subject) {
163 d43ad788 Scott Ullrich
		$issuer_crt =& lookup_ca_by_subject($issuer);
164 1e0b1727 Phil Davis
		if ($issuer_crt) {
165 d43ad788 Scott Ullrich
			$ca['caref'] = $issuer_crt['refid'];
166 1e0b1727 Phil Davis
		}
167 d43ad788 Scott Ullrich
	}
168
169
	/* Correct if child certificate was loaded first */
170 1e0b1727 Phil Davis
	if (is_array($config['ca'])) {
171
		foreach ($config['ca'] as & $oca) {
172 d43ad788 Scott Ullrich
			$issuer = cert_get_issuer($oca['crt']);
173 086cf944 Phil Davis
			if ($ca['refid'] <> $oca['refid'] && $issuer == $subject) {
174 d43ad788 Scott Ullrich
				$oca['caref'] = $ca['refid'];
175 1e0b1727 Phil Davis
			}
176 d43ad788 Scott Ullrich
		}
177 1e0b1727 Phil Davis
	}
178
	if (is_array($config['cert'])) {
179
		foreach ($config['cert'] as & $cert) {
180 d43ad788 Scott Ullrich
			$issuer = cert_get_issuer($cert['crt']);
181 086cf944 Phil Davis
			if ($issuer == $subject) {
182 d43ad788 Scott Ullrich
				$cert['caref'] = $ca['refid'];
183 1e0b1727 Phil Davis
			}
184 d43ad788 Scott Ullrich
		}
185 1e0b1727 Phil Davis
	}
186 d43ad788 Scott Ullrich
	return true;
187
}
188
189 ca621902 jim-p
function ca_create(& $ca, $keylen, $lifetime, $dn, $digest_alg = "sha256") {
190 d43ad788 Scott Ullrich
191
	$args = array(
192 87b4deb2 jim-p
		"x509_extensions" => "v3_ca",
193 ca621902 jim-p
		"digest_alg" => $digest_alg,
194 51dbdcde Ermal Lu?i
		"private_key_bits" => (int)$keylen,
195 d43ad788 Scott Ullrich
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
196
		"encrypt_key" => false);
197
198
	// generate a new key pair
199 838e27bf jim-p
	$res_key = openssl_pkey_new($args);
200 1e0b1727 Phil Davis
	if (!$res_key) {
201
		return false;
202
	}
203 d43ad788 Scott Ullrich
204
	// generate a certificate signing request
205
	$res_csr = openssl_csr_new($dn, $res_key, $args);
206 1e0b1727 Phil Davis
	if (!$res_csr) {
207
		return false;
208
	}
209 d43ad788 Scott Ullrich
210
	// self sign the certificate
211
	$res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args);
212 1e0b1727 Phil Davis
	if (!$res_crt) {
213
		return false;
214
	}
215 d43ad788 Scott Ullrich
216
	// export our certificate data
217 1b6d9fa5 Evgeny Yurchenko
	if (!openssl_pkey_export($res_key, $str_key) ||
218 ae52d165 Renato Botelho
	    !openssl_x509_export($res_crt, $str_crt)) {
219 1b6d9fa5 Evgeny Yurchenko
		return false;
220 1e0b1727 Phil Davis
	}
221 d43ad788 Scott Ullrich
222
	// return our ca information
223
	$ca['crt'] = base64_encode($str_crt);
224
	$ca['prv'] = base64_encode($str_key);
225
	$ca['serial'] = 0;
226
227
	return true;
228
}
229
230 ca621902 jim-p
function ca_inter_create(& $ca, $keylen, $lifetime, $dn, $caref, $digest_alg = "sha256") {
231 95c8cf48 Evgeny Yurchenko
	// Create Intermediate Certificate Authority
232
	$signing_ca =& lookup_ca($caref);
233 1e0b1727 Phil Davis
	if (!$signing_ca) {
234 95c8cf48 Evgeny Yurchenko
		return false;
235 1e0b1727 Phil Davis
	}
236 95c8cf48 Evgeny Yurchenko
237
	$signing_ca_res_crt = openssl_x509_read(base64_decode($signing_ca['crt']));
238
	$signing_ca_res_key = openssl_pkey_get_private(array(0 => base64_decode($signing_ca['prv']) , 1 => ""));
239 1e0b1727 Phil Davis
	if (!$signing_ca_res_crt || !$signing_ca_res_key) {
240
		return false;
241
	}
242 95c8cf48 Evgeny Yurchenko
	$signing_ca_serial = ++$signing_ca['serial'];
243
244
	$args = array(
245 87b4deb2 jim-p
		"x509_extensions" => "v3_ca",
246 ca621902 jim-p
		"digest_alg" => $digest_alg,
247 95c8cf48 Evgeny Yurchenko
		"private_key_bits" => (int)$keylen,
248
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
249
		"encrypt_key" => false);
250
251
	// generate a new key pair
252
	$res_key = openssl_pkey_new($args);
253 1e0b1727 Phil Davis
	if (!$res_key) {
254
		return false;
255
	}
256 95c8cf48 Evgeny Yurchenko
257
	// generate a certificate signing request
258
	$res_csr = openssl_csr_new($dn, $res_key, $args);
259 1e0b1727 Phil Davis
	if (!$res_csr) {
260
		return false;
261
	}
262 95c8cf48 Evgeny Yurchenko
263
	// Sign the certificate
264
	$res_crt = openssl_csr_sign($res_csr, $signing_ca_res_crt, $signing_ca_res_key, $lifetime, $args, $signing_ca_serial);
265 1e0b1727 Phil Davis
	if (!$res_crt) {
266
		return false;
267
	}
268 95c8cf48 Evgeny Yurchenko
269
	// export our certificate data
270
	if (!openssl_pkey_export($res_key, $str_key) ||
271 ae52d165 Renato Botelho
	    !openssl_x509_export($res_crt, $str_crt)) {
272 95c8cf48 Evgeny Yurchenko
		return false;
273 1e0b1727 Phil Davis
	}
274 95c8cf48 Evgeny Yurchenko
275
	// return our ca information
276
	$ca['crt'] = base64_encode($str_crt);
277
	$ca['prv'] = base64_encode($str_key);
278
	$ca['serial'] = 0;
279 dd76084d Matt Smith
	$ca['caref'] = $caref;
280 95c8cf48 Evgeny Yurchenko
281
	return true;
282
}
283
284 d43ad788 Scott Ullrich
function cert_import(& $cert, $crt_str, $key_str) {
285
286
	$cert['crt'] = base64_encode($crt_str);
287
	$cert['prv'] = base64_encode($key_str);
288
289
	$subject = cert_get_subject($crt_str, false);
290
	$issuer = cert_get_issuer($crt_str, false);
291 1e0b1727 Phil Davis
292 d43ad788 Scott Ullrich
	// Find my issuer unless self-signed
293 1e0b1727 Phil Davis
	if ($issuer <> $subject) {
294 d43ad788 Scott Ullrich
		$issuer_crt =& lookup_ca_by_subject($issuer);
295 1e0b1727 Phil Davis
		if ($issuer_crt) {
296 d43ad788 Scott Ullrich
			$cert['caref'] = $issuer_crt['refid'];
297 1e0b1727 Phil Davis
		}
298 d43ad788 Scott Ullrich
	}
299
	return true;
300
}
301
302 6c07db48 Phil Davis
function cert_create(& $cert, $caref, $keylen, $lifetime, $dn, $type = "user", $digest_alg = "sha256") {
303 d43ad788 Scott Ullrich
304 7c4c77ee jim-p
	$cert['type'] = $type;
305 d43ad788 Scott Ullrich
306 7c4c77ee jim-p
	if ($type != "self-signed") {
307
		$cert['caref'] = $caref;
308
		$ca =& lookup_ca($caref);
309 1e0b1727 Phil Davis
		if (!$ca) {
310 7c4c77ee jim-p
			return false;
311 1e0b1727 Phil Davis
		}
312 7c4c77ee jim-p
313
		$ca_str_crt = base64_decode($ca['crt']);
314
		$ca_str_key = base64_decode($ca['prv']);
315
		$ca_res_crt = openssl_x509_read($ca_str_crt);
316
		$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
317 1e0b1727 Phil Davis
		if (!$ca_res_key) {
318
			return false;
319
		}
320 ab63443a jim-p
		if (empty($ca['serial'])) {
321
			$ca['serial'] = 0;
322
		}
323 7c4c77ee jim-p
		$ca_serial = ++$ca['serial'];
324
	}
325 d43ad788 Scott Ullrich
326 7aaabd69 jim-p
	switch ($type) {
327
		case "ca":
328
			$cert_type = "v3_ca";
329
			break;
330
		case "server":
331 7c4c77ee jim-p
		case "self-signed":
332 7aaabd69 jim-p
			$cert_type = "server";
333
			break;
334
		default:
335
			$cert_type = "usr_cert";
336
			break;
337
	}
338
339 3cb773da yarick123
	// in case of using Subject Alternative Names use other sections (with postfix '_san')
340
	// pass subjectAltName over environment variable 'SAN'
341
	if ($dn['subjectAltName']) {
342
		putenv("SAN={$dn['subjectAltName']}"); // subjectAltName can be set _only_ via configuration file
343
		$cert_type .= '_san';
344
		unset($dn['subjectAltName']);
345
	}
346
347 d43ad788 Scott Ullrich
	$args = array(
348 7aaabd69 jim-p
		"x509_extensions" => $cert_type,
349 ca621902 jim-p
		"digest_alg" => $digest_alg,
350 51dbdcde Ermal Lu?i
		"private_key_bits" => (int)$keylen,
351 d43ad788 Scott Ullrich
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
352
		"encrypt_key" => false);
353
354
	// generate a new key pair
355 838e27bf jim-p
	$res_key = openssl_pkey_new($args);
356 1e0b1727 Phil Davis
	if (!$res_key) {
357
		return false;
358
	}
359 d43ad788 Scott Ullrich
360 7c4c77ee jim-p
	// If this is a self-signed cert, blank out the CA and sign with the cert's key
361
	if ($type == "self-signed") {
362
		$ca           = null;
363
		$ca_res_crt   = null;
364
		$ca_res_key   = $res_key;
365
		$ca_serial    = 0;
366
		$cert['type'] = "server";
367
	}
368
369 d43ad788 Scott Ullrich
	// generate a certificate signing request
370
	$res_csr = openssl_csr_new($dn, $res_key, $args);
371 1e0b1727 Phil Davis
	if (!$res_csr) {
372
		return false;
373
	}
374 d43ad788 Scott Ullrich
375 7c4c77ee jim-p
	// sign the certificate using an internal CA
376 d43ad788 Scott Ullrich
	$res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime,
377
				 $args, $ca_serial);
378 1e0b1727 Phil Davis
	if (!$res_crt) {
379
		return false;
380
	}
381 d43ad788 Scott Ullrich
382
	// export our certificate data
383 22b380aa Evgeny Yurchenko
	if (!openssl_pkey_export($res_key, $str_key) ||
384 ae52d165 Renato Botelho
	    !openssl_x509_export($res_crt, $str_crt)) {
385 22b380aa Evgeny Yurchenko
		return false;
386 1e0b1727 Phil Davis
	}
387 d43ad788 Scott Ullrich
388
	// return our certificate information
389
	$cert['crt'] = base64_encode($str_crt);
390
	$cert['prv'] = base64_encode($str_key);
391
392
	return true;
393
}
394
395 ca621902 jim-p
function csr_generate(& $cert, $keylen, $dn, $digest_alg = "sha256") {
396 d43ad788 Scott Ullrich
397
	$args = array(
398 87b4deb2 jim-p
		"x509_extensions" => "v3_req",
399 ca621902 jim-p
		"digest_alg" => $digest_alg,
400 3198b8d3 Ermal Lu?i
		"private_key_bits" => (int)$keylen,
401 d43ad788 Scott Ullrich
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
402
		"encrypt_key" => false);
403
404
	// generate a new key pair
405 838e27bf jim-p
	$res_key = openssl_pkey_new($args);
406 1e0b1727 Phil Davis
	if (!$res_key) {
407
		return false;
408
	}
409 d43ad788 Scott Ullrich
410
	// generate a certificate signing request
411
	$res_csr = openssl_csr_new($dn, $res_key, $args);
412 1e0b1727 Phil Davis
	if (!$res_csr) {
413
		return false;
414
	}
415 d43ad788 Scott Ullrich
416
	// export our request data
417 22b380aa Evgeny Yurchenko
	if (!openssl_pkey_export($res_key, $str_key) ||
418 ae52d165 Renato Botelho
	    !openssl_csr_export($res_csr, $str_csr)) {
419 22b380aa Evgeny Yurchenko
		return false;
420 1e0b1727 Phil Davis
	}
421 d43ad788 Scott Ullrich
422
	// return our request information
423
	$cert['csr'] = base64_encode($str_csr);
424
	$cert['prv'] = base64_encode($str_key);
425
426
	return true;
427
}
428
429
function csr_complete(& $cert, $str_crt) {
430 7fd7fbcf PiBa-NL
	$str_key = base64_decode($cert['prv']);
431
	cert_import($cert, $str_crt, $str_key);
432 d43ad788 Scott Ullrich
	unset($cert['csr']);
433
	return true;
434
}
435
436
function csr_get_subject($str_crt, $decode = true) {
437
438 1e0b1727 Phil Davis
	if ($decode) {
439 d43ad788 Scott Ullrich
		$str_crt = base64_decode($str_crt);
440 1e0b1727 Phil Davis
	}
441 d43ad788 Scott Ullrich
442
	$components = openssl_csr_get_subject($str_crt);
443
444 1e0b1727 Phil Davis
	if (empty($components) || !is_array($components)) {
445 d43ad788 Scott Ullrich
		return "unknown";
446 1e0b1727 Phil Davis
	}
447 d43ad788 Scott Ullrich
448 311f93cd Ermal
	ksort($components);
449 d43ad788 Scott Ullrich
	foreach ($components as $a => $v) {
450 1e0b1727 Phil Davis
		if (!strlen($subject)) {
451 d43ad788 Scott Ullrich
			$subject = "{$a}={$v}";
452 1e0b1727 Phil Davis
		} else {
453 d43ad788 Scott Ullrich
			$subject = "{$a}={$v}, {$subject}";
454 1e0b1727 Phil Davis
		}
455 d43ad788 Scott Ullrich
	}
456
457
	return $subject;
458
}
459
460
function cert_get_subject($str_crt, $decode = true) {
461
462 1e0b1727 Phil Davis
	if ($decode) {
463 d43ad788 Scott Ullrich
		$str_crt = base64_decode($str_crt);
464 1e0b1727 Phil Davis
	}
465 d43ad788 Scott Ullrich
466
	$inf_crt = openssl_x509_parse($str_crt);
467
	$components = $inf_crt['subject'];
468
469 1e0b1727 Phil Davis
	if (empty($components) || !is_array($components)) {
470 d43ad788 Scott Ullrich
		return "unknown";
471 1e0b1727 Phil Davis
	}
472 d43ad788 Scott Ullrich
473 b89c34aa Ermal
	ksort($components);
474 d43ad788 Scott Ullrich
	foreach ($components as $a => $v) {
475 b89c34aa Ermal
		if (is_array($v)) {
476
			ksort($v);
477 5479df47 jim-p
			foreach ($v as $w) {
478
				$asubject = "{$a}={$w}";
479
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
480
			}
481 b89c34aa Ermal
		} else {
482 5479df47 jim-p
			$asubject = "{$a}={$v}";
483
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
484
		}
485 d43ad788 Scott Ullrich
	}
486
487
	return $subject;
488
}
489
490
function cert_get_subject_array($crt) {
491
	$str_crt = base64_decode($crt);
492
	$inf_crt = openssl_x509_parse($str_crt);
493
	$components = $inf_crt['subject'];
494 e4d7a064 jim-p
495 1e0b1727 Phil Davis
	if (!is_array($components)) {
496 e4d7a064 jim-p
		return;
497 1e0b1727 Phil Davis
	}
498 e4d7a064 jim-p
499 d43ad788 Scott Ullrich
	$subject_array = array();
500
501 1e0b1727 Phil Davis
	foreach ($components as $a => $v) {
502 d43ad788 Scott Ullrich
		$subject_array[] = array('a' => $a, 'v' => $v);
503 1e0b1727 Phil Davis
	}
504 d43ad788 Scott Ullrich
505
	return $subject_array;
506
}
507
508 a84eb838 jim-p
function cert_get_subject_hash($crt) {
509
	$str_crt = base64_decode($crt);
510
	$inf_crt = openssl_x509_parse($str_crt);
511
	return $inf_crt['subject'];
512
}
513
514 4906f4ee jim-p
function cert_get_sans($str_crt, $decode = true) {
515
	if ($decode) {
516
		$str_crt = base64_decode($str_crt);
517
	}
518
	$sans = array();
519
	$crt_details = openssl_x509_parse($str_crt);
520
	if (!empty($crt_details['extensions']['subjectAltName'])) {
521
		$sans = explode(',', $crt_details['extensions']['subjectAltName']);
522
	}
523
	return $sans;
524
}
525
526 d43ad788 Scott Ullrich
function cert_get_issuer($str_crt, $decode = true) {
527
528 1e0b1727 Phil Davis
	if ($decode) {
529 d43ad788 Scott Ullrich
		$str_crt = base64_decode($str_crt);
530 1e0b1727 Phil Davis
	}
531 d43ad788 Scott Ullrich
532
	$inf_crt = openssl_x509_parse($str_crt);
533
	$components = $inf_crt['issuer'];
534 1e0b1727 Phil Davis
535
	if (empty($components) || !is_array($components)) {
536 d43ad788 Scott Ullrich
		return "unknown";
537 1e0b1727 Phil Davis
	}
538 5ca13f69 Ermal
539
	ksort($components);
540 d43ad788 Scott Ullrich
	foreach ($components as $a => $v) {
541 2a08b814 vsquared56
		if (is_array($v)) {
542
			ksort($v);
543
			foreach ($v as $w) {
544
				$aissuer = "{$a}={$w}";
545
				$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
546
			}
547
		} else {
548
			$aissuer = "{$a}={$v}";
549
			$issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer;
550
		}
551 d43ad788 Scott Ullrich
	}
552
553
	return $issuer;
554
}
555
556 1746c5ce PiBa-NL
/* Works for both RSA and ECC (crt) and key (prv) */
557
function cert_get_publickey($str_crt, $decode = true, $type = "crt") {
558 1e0b1727 Phil Davis
	if ($decode) {
559 a828210b yakatz
		$str_crt = base64_decode($str_crt);
560 1e0b1727 Phil Davis
	}
561 6d6ba660 PiBa-NL
	switch ($type) {
562
		case 'prv':
563
			exec("echo \"{$str_crt}\" | openssl pkey -pubout", $out);
564
			break;
565
		case 'crt':
566
			exec("echo \"{$str_crt}\" | openssl x509 -inform pem -noout -pubkey", $out);
567
			break;
568
		case 'csr':
569
			exec("echo \"{$str_crt}\" | openssl req -inform pem -noout -pubkey", $out);
570
			break;
571
		default:
572
			$out = array();
573
			break;
574 1746c5ce PiBa-NL
	}
575 6d6ba660 PiBa-NL
	return implode("\n", $out);
576 a828210b yakatz
}
577 1379d66f jim-p
578
function cert_get_purpose($str_crt, $decode = true) {
579 4906f4ee jim-p
	$extended_oids = array(
580
		"1.3.6.1.5.5.8.2.2" => "IP Security IKE Intermediate",
581
	);
582 1e0b1727 Phil Davis
	if ($decode) {
583 1379d66f jim-p
		$str_crt = base64_decode($str_crt);
584 1e0b1727 Phil Davis
	}
585 1379d66f jim-p
	$crt_details = openssl_x509_parse($str_crt);
586
	$purpose = array();
587 4906f4ee jim-p
	if (!empty($crt_details['extensions']['keyUsage'])) {
588
		$purpose['ku'] = explode(',', $crt_details['extensions']['keyUsage']);
589
		foreach ($purpose['ku'] as & $ku) {
590
			$ku = trim($ku);
591
			if (array_key_exists($ku, $extended_oids)) {
592
				$ku = $extended_oids[$ku];
593
			}
594
		}
595
	} else {
596
		$purpose['ku'] = array();
597
	}
598
	if (!empty($crt_details['extensions']['extendedKeyUsage'])) {
599
		$purpose['eku'] = explode(',', $crt_details['extensions']['extendedKeyUsage']);
600
		foreach ($purpose['eku'] as & $eku) {
601
			$eku = trim($eku);
602
			if (array_key_exists($eku, $extended_oids)) {
603
				$eku = $extended_oids[$eku];
604
			}
605
		}
606
	} else {
607
		$purpose['eku'] = array();
608
	}
609 1379d66f jim-p
	$purpose['ca'] = (stristr($crt_details['extensions']['basicConstraints'], 'CA:TRUE') === false) ? 'No': 'Yes';
610 4906f4ee jim-p
	$purpose['server'] = (in_array('TLS Web Server Authentication', $purpose['eku'])) ? 'Yes': 'No';
611
612 1379d66f jim-p
	return $purpose;
613
}
614
615 2b333210 jim-p
function cert_get_dates($str_crt, $decode = true) {
616 1e0b1727 Phil Davis
	if ($decode) {
617 2b333210 jim-p
		$str_crt = base64_decode($str_crt);
618 1e0b1727 Phil Davis
	}
619 2b333210 jim-p
	$crt_details = openssl_x509_parse($str_crt);
620 1e0b1727 Phil Davis
	if ($crt_details['validFrom_time_t'] > 0) {
621 2b333210 jim-p
		$start = date('r', $crt_details['validFrom_time_t']);
622 1e0b1727 Phil Davis
	}
623
	if ($crt_details['validTo_time_t'] > 0) {
624 2b333210 jim-p
		$end = date('r', $crt_details['validTo_time_t']);
625 1e0b1727 Phil Davis
	}
626 2b333210 jim-p
	return array($start, $end);
627
}
628
629 04761344 jim-p
function cert_get_serial($str_crt, $decode = true) {
630 1e0b1727 Phil Davis
	if ($decode) {
631 04761344 jim-p
		$str_crt = base64_decode($str_crt);
632 1e0b1727 Phil Davis
	}
633 04761344 jim-p
	$crt_details = openssl_x509_parse($str_crt);
634 1e0b1727 Phil Davis
	if (isset($crt_details['serialNumber']) && !empty($crt_details['serialNumber'])) {
635 04761344 jim-p
		return $crt_details['serialNumber'];
636 1e0b1727 Phil Davis
	} else {
637 04761344 jim-p
		return NULL;
638 1e0b1727 Phil Davis
	}
639 04761344 jim-p
}
640
641 e2c718c8 jim-p
function is_openvpn_server_ca($caref) {
642
	global $config;
643
	if (!is_array($config['openvpn']['openvpn-server'])) {
644
		return;
645
	}
646
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
647
		if ($ovpns['caref'] == $caref) {
648
			return true;
649
		}
650
	}
651
	return false;
652
}
653
654
function is_openvpn_client_ca($caref) {
655
	global $config;
656
	if (!is_array($config['openvpn']['openvpn-client'])) {
657
		return;
658
	}
659
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
660
		if ($ovpnc['caref'] == $caref) {
661
			return true;
662
		}
663
	}
664
	return false;
665
}
666
667
function is_ipsec_peer_ca($caref) {
668
	global $config;
669
	if (!is_array($config['ipsec']['phase1'])) {
670
		return;
671
	}
672
	foreach ($config['ipsec']['phase1'] as $ipsec) {
673
		if ($ipsec['caref'] == $caref) {
674
			return true;
675
		}
676
	}
677
	return false;
678
}
679
680
function is_ldap_peer_ca($caref) {
681
	global $config;
682
	if (!is_array($config['system']['authserver'])) {
683
		return;
684
	}
685
	foreach ($config['system']['authserver'] as $authserver) {
686
		if ($authserver['ldap_caref'] == $caref) {
687
			return true;
688
		}
689
	}
690
	return false;
691
}
692
693
function ca_in_use($caref) {
694
	return (is_openvpn_server_ca($caref) ||
695
		is_openvpn_client_ca($caref) ||
696
		is_ipsec_peer_ca($caref) ||
697
		is_ldap_peer_ca($caref));
698
}
699
700 dea98903 jim-p
function is_user_cert($certref) {
701 dab2e769 jim-p
	global $config;
702 1e0b1727 Phil Davis
	if (!is_array($config['system']['user'])) {
703 dab2e769 jim-p
		return;
704 1e0b1727 Phil Davis
	}
705 dab2e769 jim-p
	foreach ($config['system']['user'] as $user) {
706 1e0b1727 Phil Davis
		if (!is_array($user['cert'])) {
707 dab2e769 jim-p
			continue;
708 1e0b1727 Phil Davis
		}
709 dab2e769 jim-p
		foreach ($user['cert'] as $cert) {
710 1e0b1727 Phil Davis
			if ($certref == $cert) {
711 dea98903 jim-p
				return true;
712 1e0b1727 Phil Davis
			}
713 dab2e769 jim-p
		}
714
	}
715 dea98903 jim-p
	return false;
716 dab2e769 jim-p
}
717
718 dea98903 jim-p
function is_openvpn_server_cert($certref) {
719 dab2e769 jim-p
	global $config;
720 1e0b1727 Phil Davis
	if (!is_array($config['openvpn']['openvpn-server'])) {
721 dea98903 jim-p
		return;
722 1e0b1727 Phil Davis
	}
723 dea98903 jim-p
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
724 1e0b1727 Phil Davis
		if ($ovpns['certref'] == $certref) {
725 dea98903 jim-p
			return true;
726 1e0b1727 Phil Davis
		}
727 dea98903 jim-p
	}
728
	return false;
729
}
730
731
function is_openvpn_client_cert($certref) {
732
	global $config;
733 1e0b1727 Phil Davis
	if (!is_array($config['openvpn']['openvpn-client'])) {
734 dea98903 jim-p
		return;
735 1e0b1727 Phil Davis
	}
736 dea98903 jim-p
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
737 1e0b1727 Phil Davis
		if ($ovpnc['certref'] == $certref) {
738 dea98903 jim-p
			return true;
739 1e0b1727 Phil Davis
		}
740 dea98903 jim-p
	}
741
	return false;
742
}
743
744
function is_ipsec_cert($certref) {
745
	global $config;
746 1e0b1727 Phil Davis
	if (!is_array($config['ipsec']['phase1'])) {
747 dea98903 jim-p
		return;
748 1e0b1727 Phil Davis
	}
749 dea98903 jim-p
	foreach ($config['ipsec']['phase1'] as $ipsec) {
750 1e0b1727 Phil Davis
		if ($ipsec['certref'] == $certref) {
751 dea98903 jim-p
			return true;
752 1e0b1727 Phil Davis
		}
753 dea98903 jim-p
	}
754
	return false;
755
}
756
757
function is_webgui_cert($certref) {
758
	global $config;
759 ae52d165 Renato Botelho
	if (($config['system']['webgui']['ssl-certref'] == $certref) &&
760
	    ($config['system']['webgui']['protocol'] != "http")) {
761 dea98903 jim-p
		return true;
762 1e0b1727 Phil Davis
	}
763 dea98903 jim-p
}
764
765 29e6a815 Renato Botelho
function is_package_cert($certref) {
766
	$pluginparams = array();
767
	$pluginparams['type'] = 'certificates';
768
	$pluginparams['event'] = 'used_certificates';
769
770
	$certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams);
771
772
	/* Check if any package is using certificate */
773
	foreach ($certificates_used_by_packages as $name => $package) {
774
		if (is_array($package['certificatelist'][$certref]) &&
775
		    isset($package['certificatelist'][$certref]) > 0) {
776
			return true;
777
		}
778
	}
779
}
780
781 36f6ed35 bcyrill
function is_captiveportal_cert($certref) {
782
	global $config;
783 1e0b1727 Phil Davis
	if (!is_array($config['captiveportal'])) {
784 36f6ed35 bcyrill
		return;
785 1e0b1727 Phil Davis
	}
786 36f6ed35 bcyrill
	foreach ($config['captiveportal'] as $portal) {
787 1e0b1727 Phil Davis
		if (isset($portal['enable']) && isset($portal['httpslogin']) && ($portal['certref'] == $certref)) {
788 36f6ed35 bcyrill
			return true;
789 1e0b1727 Phil Davis
		}
790 36f6ed35 bcyrill
	}
791
	return false;
792
}
793
794 dea98903 jim-p
function cert_in_use($certref) {
795 29e6a815 Renato Botelho
796 dea98903 jim-p
	return (is_webgui_cert($certref) ||
797
		is_user_cert($certref) ||
798
		is_openvpn_server_cert($certref) ||
799
		is_openvpn_client_cert($certref) ||
800 36f6ed35 bcyrill
		is_ipsec_cert($certref) ||
801 29e6a815 Renato Botelho
		is_captiveportal_cert($certref) ||
802
		is_package_cert($certref));
803 dab2e769 jim-p
}
804
805 3bde5cdd PiBa-NL
function cert_usedby_description($refid, $certificates_used_by_packages) {
806
	$result = "";
807
	if (is_array($certificates_used_by_packages)) {
808
		foreach ($certificates_used_by_packages as $name => $package) {
809
			if (isset($package['certificatelist'][$refid])) {
810
				$hint = "" ;
811
				if (is_array($package['certificatelist'][$refid])) {
812
					foreach ($package['certificatelist'][$refid] as $cert_used) {
813
						$hint = $hint . $cert_used['usedby']."\n";
814
					}
815
				}
816
				$count = count($package['certificatelist'][$refid]);
817
				$result .= "<div title='".htmlspecialchars($hint)."'>";
818
				$result .= htmlspecialchars($package['pkgname'])." ($count)<br />";
819
				$result .= "</div>";
820
			}
821
		}
822
	}
823
	return $result;
824
}
825
826 6c07db48 Phil Davis
function crl_create(& $crl, $caref, $name, $serial = 0, $lifetime = 9999) {
827 c5f010aa jim-p
	global $config;
828
	$ca =& lookup_ca($caref);
829 1e0b1727 Phil Davis
	if (!$ca) {
830 c5f010aa jim-p
		return false;
831 1e0b1727 Phil Davis
	}
832 f2a86ca9 jim-p
	$crl['descr'] = $name;
833 c5f010aa jim-p
	$crl['caref'] = $caref;
834
	$crl['serial'] = $serial;
835
	$crl['lifetime'] = $lifetime;
836
	$crl['cert'] = array();
837
	$crl_res = crl_update($crl);
838
	$config['crl'][] = $crl;
839
	return $crl_res;
840
}
841
842
function crl_update(& $crl) {
843
	global $config;
844
	$ca =& lookup_ca($crl['caref']);
845 1e0b1727 Phil Davis
	if (!$ca) {
846 c5f010aa jim-p
		return false;
847 1e0b1727 Phil Davis
	}
848 7b757d1b jim-p
	// If we have text but no certs, it was imported and cannot be updated.
849 1e0b1727 Phil Davis
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert']))) {
850 7b757d1b jim-p
		return false;
851 1e0b1727 Phil Davis
	}
852 c5f010aa jim-p
	$crl['serial']++;
853
	$ca_str_crt = base64_decode($ca['crt']);
854
	$ca_str_key = base64_decode($ca['prv']);
855
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
856 4bc2c676 jim-p
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
857
		foreach ($crl['cert'] as $cert) {
858
			openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
859
		}
860 c5f010aa jim-p
	}
861
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
862
	$crl['text'] = base64_encode($crl_text);
863
	return $crl_res;
864
}
865
866 6c07db48 Phil Davis
function cert_revoke($cert, & $crl, $reason = OCSP_REVOKED_STATUS_UNSPECIFIED) {
867 c5f010aa jim-p
	global $config;
868 1e0b1727 Phil Davis
	if (is_cert_revoked($cert, $crl['refid'])) {
869 c5f010aa jim-p
		return true;
870 1e0b1727 Phil Davis
	}
871 7b757d1b jim-p
	// If we have text but no certs, it was imported and cannot be updated.
872 1e0b1727 Phil Davis
	if (!is_crl_internal($crl)) {
873 7b757d1b jim-p
		return false;
874 1e0b1727 Phil Davis
	}
875 c5f010aa jim-p
	$cert["reason"] = $reason;
876
	$cert["revoke_time"] = time();
877
	$crl["cert"][] = $cert;
878
	crl_update($crl);
879 fb3f1993 jim-p
	return true;
880 c5f010aa jim-p
}
881
882
function cert_unrevoke($cert, & $crl) {
883
	global $config;
884 1e0b1727 Phil Davis
	if (!is_crl_internal($crl)) {
885 7b757d1b jim-p
		return false;
886 1e0b1727 Phil Davis
	}
887 c5f010aa jim-p
	foreach ($crl['cert'] as $id => $rcert) {
888 f2a86ca9 jim-p
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
889 c5f010aa jim-p
			unset($crl['cert'][$id]);
890 728003c8 jim-p
			if (count($crl['cert']) == 0) {
891
				// Protect against accidentally switching the type to imported, for older CRLs
892 1e0b1727 Phil Davis
				if (!isset($crl['method'])) {
893 728003c8 jim-p
					$crl['method'] = "internal";
894 1e0b1727 Phil Davis
				}
895 728003c8 jim-p
				crl_update($crl);
896 1e0b1727 Phil Davis
			} else {
897 a59831e7 jim-p
				crl_update($crl);
898 1e0b1727 Phil Davis
			}
899 c5f010aa jim-p
			return true;
900
		}
901
	}
902
	return false;
903
}
904
905 04761344 jim-p
/* Compare two certificates to see if they match. */
906
function cert_compare($cert1, $cert2) {
907
	/* Ensure two certs are identical by first checking that their issuers match, then
908
		subjects, then serial numbers, and finally the moduli. Anything less strict
909
		could accidentally count two similar, but different, certificates as
910
		being identical. */
911
	$c1 = base64_decode($cert1['crt']);
912
	$c2 = base64_decode($cert2['crt']);
913 ae52d165 Renato Botelho
	if ((cert_get_issuer($c1, false) == cert_get_issuer($c2, false)) &&
914
	    (cert_get_subject($c1, false) == cert_get_subject($c2, false)) &&
915
	    (cert_get_serial($c1, false) == cert_get_serial($c2, false)) &&
916 1746c5ce PiBa-NL
	    (cert_get_publickey($c1, false) == cert_get_publickey($c2, false))) {
917 04761344 jim-p
		return true;
918 1e0b1727 Phil Davis
	}
919 04761344 jim-p
	return false;
920
}
921
922 fb3f1993 jim-p
function is_cert_revoked($cert, $crlref = "") {
923 c5f010aa jim-p
	global $config;
924 1e0b1727 Phil Davis
	if (!is_array($config['crl'])) {
925 c5f010aa jim-p
		return false;
926 1e0b1727 Phil Davis
	}
927 c5f010aa jim-p
928 fb3f1993 jim-p
	if (!empty($crlref)) {
929 28ff7ace jim-p
		$crl = lookup_crl($crlref);
930 1e0b1727 Phil Davis
		if (!is_array($crl['cert'])) {
931 fb3f1993 jim-p
			return false;
932 1e0b1727 Phil Davis
		}
933 088ce869 jim-p
		foreach ($crl['cert'] as $rcert) {
934 1e0b1727 Phil Davis
			if (cert_compare($rcert, $cert)) {
935 c5f010aa jim-p
				return true;
936 1e0b1727 Phil Davis
			}
937 c5f010aa jim-p
		}
938 fb3f1993 jim-p
	} else {
939
		foreach ($config['crl'] as $crl) {
940 1e0b1727 Phil Davis
			if (!is_array($crl['cert'])) {
941 fb3f1993 jim-p
				continue;
942 1e0b1727 Phil Davis
			}
943 fb3f1993 jim-p
			foreach ($crl['cert'] as $rcert) {
944 1e0b1727 Phil Davis
				if (cert_compare($rcert, $cert)) {
945 fb3f1993 jim-p
					return true;
946 1e0b1727 Phil Davis
				}
947 fb3f1993 jim-p
			}
948
		}
949
	}
950
	return false;
951
}
952
953
function is_openvpn_server_crl($crlref) {
954
	global $config;
955 1e0b1727 Phil Davis
	if (!is_array($config['openvpn']['openvpn-server'])) {
956 fb3f1993 jim-p
		return;
957 1e0b1727 Phil Davis
	}
958 fb3f1993 jim-p
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
959 1e0b1727 Phil Davis
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref)) {
960 fb3f1993 jim-p
			return true;
961 1e0b1727 Phil Davis
		}
962 c5f010aa jim-p
	}
963
	return false;
964
}
965
966 fb3f1993 jim-p
// Keep this general to allow for future expansion. See cert_in_use() above.
967
function crl_in_use($crlref) {
968
	return (is_openvpn_server_crl($crlref));
969
}
970
971
function is_crl_internal($crl) {
972 728003c8 jim-p
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
973 fb3f1993 jim-p
}
974
975 b34b2b7d jim-p
function cert_get_cn($crt, $isref = false) {
976
	/* If this is a certref, not an actual cert, look up the cert first */
977
	if ($isref) {
978
		$cert = lookup_cert($crt);
979
		/* If it's not a valid cert, bail. */
980 1e0b1727 Phil Davis
		if (!(is_array($cert) && !empty($cert['crt']))) {
981 b34b2b7d jim-p
			return "";
982 1e0b1727 Phil Davis
		}
983 b34b2b7d jim-p
		$cert = $cert['crt'];
984
	} else {
985
		$cert = $crt;
986
	}
987
	$sub = cert_get_subject_array($cert);
988
	if (is_array($sub)) {
989
		foreach ($sub as $s) {
990 1e0b1727 Phil Davis
			if (strtoupper($s['a']) == "CN") {
991 b34b2b7d jim-p
				return $s['v'];
992 1e0b1727 Phil Davis
			}
993 b34b2b7d jim-p
		}
994
	}
995
	return "";
996
}
997
998 83d2b83a jim-p
function cert_escape_x509_chars($str, $reverse = false) {
999
	/* Characters which need escaped when present in x.509 fields.
1000
	 * See https://www.ietf.org/rfc/rfc4514.txt
1001
	 *
1002
	 * The backslash (\) must be listed first in these arrays!
1003
	 */
1004
	$cert_directory_string_special_chars = array('\\', '"', '#', '+', ',', ';', '<', '=', '>');
1005
	$cert_directory_string_special_chars_esc = array('\\\\', '\"', '\#', '\+', '\,', '\;', '\<', '\=', '\>');
1006
	if ($reverse) {
1007
		return str_replace($cert_directory_string_special_chars_esc, $cert_directory_string_special_chars, $str);
1008
	} else {
1009
		/* First unescape and then escape again, to prevent possible double escaping. */
1010
		return str_replace($cert_directory_string_special_chars, $cert_directory_string_special_chars_esc, cert_escape_x509_chars($str, true));
1011
	}
1012
}
1013
1014 b89c34aa Ermal
?>