Project

General

Profile

Download (13.9 KB) Statistics
| Branch: | Tag: | Revision:
1 d43ad788 Scott Ullrich
<?php
2
/* $Id$ */
3
/*
4 c5f010aa jim-p
	Copyright (C) 2008 Shrew Soft Inc
5
	Copyright (C) 2010 Jim Pingle <jimp@pfsense.org>
6
	All rights reserved.
7 d43ad788 Scott Ullrich
8
        Redistribution and use in source and binary forms, with or without
9
        modification, are permitted provided that the following conditions are met:
10
11
        1. Redistributions of source code must retain the above copyright notice,
12
           this list of conditions and the following disclaimer.
13
14
        2. Redistributions in binary form must reproduce the above copyright
15
           notice, this list of conditions and the following disclaimer in the
16
           documentation and/or other materials provided with the distribution.
17
18
        THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19
        INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20
        AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21
        AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
        OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
        SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
        INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
        CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
        ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
        POSSIBILITY OF SUCH DAMAGE.
28
29
		DISABLE_PHP_LINT_CHECKING
30 523855b0 Scott Ullrich
		pfSense_MODULE:	certificate_managaer
31 d43ad788 Scott Ullrich
*/
32
33
require_once("functions.inc");
34
35
function & lookup_ca($refid) {
36
	global $config;
37
38 1e332e98 jim-p
	if (is_array($config['ca']))
39
		foreach ($config['ca'] as & $ca)
40 d43ad788 Scott Ullrich
			if ($ca['refid'] == $refid)
41
				return $ca;
42
43
	return false;
44
}
45
46
function & lookup_ca_by_subject($subject) {
47
	global $config;
48
49 1e332e98 jim-p
	if (is_array($config['ca']))
50
		foreach ($config['ca'] as & $ca)
51 d43ad788 Scott Ullrich
		{
52
			$ca_subject = cert_get_subject($ca['crt']);
53
			if ($ca_subject == $subject)
54
				return $ca;
55
		}
56
57
	return false;
58
}
59
60
function & lookup_cert($refid) {
61
	global $config;
62
63 1e332e98 jim-p
	if (is_array($config['cert']))
64
		foreach ($config['cert'] as & $cert)
65 d43ad788 Scott Ullrich
			if ($cert['refid'] == $refid)
66
				return $cert;
67
68
	return false;
69
}
70
71 c5f010aa jim-p
function & lookup_cert_by_name($name) {
72
	global $config;
73
	if (is_array($config['cert']))
74
		foreach ($config['cert'] as & $cert)
75 f2a86ca9 jim-p
			if ($cert['descr'] == $name)
76 c5f010aa jim-p
				return $cert;
77
}
78
79
function & lookup_crl($refid) {
80
	global $config;
81
82
	if (is_array($config['crl']))
83
		foreach ($config['crl'] as & $crl)
84
			if ($crl['refid'] == $refid)
85
				return $crl;
86
87
	return false;
88
}
89
90 d43ad788 Scott Ullrich
function ca_chain_array(& $cert) {
91
	if($cert['caref']) {
92
		$chain = array();
93 5289dc57 jim-p
		$crt = lookup_ca($cert['caref']);
94 d43ad788 Scott Ullrich
		$chain[] = $crt;
95
		while ($crt) {
96
			$caref = $crt['caref'];
97
			if($caref)
98 5289dc57 jim-p
				$crt = lookup_ca($caref);
99 d43ad788 Scott Ullrich
			else
100
				$crt = false;
101
			if($crt)
102
				$chain[] = $crt;
103
		}
104
		return $chain;
105
	}
106
	return false;
107
}
108
109
function ca_chain(& $cert) {
110
	if($cert['caref']) {
111
		$ca = "";
112
		$cas = ca_chain_array($cert);
113
		if (is_array($cas))
114
			foreach ($cas as & $ca_cert)
115
			{
116
				$ca .= base64_decode($ca_cert['crt']);
117
				$ca .= "\n";
118
			}
119
		return $ca;
120
	}
121
	return "";
122
}
123
124 bfa992bc jim-p
function ca_import(& $ca, $str, $key="", $serial=0) {
125 d43ad788 Scott Ullrich
	global $config;
126
127
	$ca['crt'] = base64_encode($str);
128 ecefc738 jim-p
	if (!empty($key))
129
		$ca['prv'] = base64_encode($key);
130 bfa992bc jim-p
	if (!empty($serial))
131
		$ca['serial'] = $serial;
132 d43ad788 Scott Ullrich
	$subject = cert_get_subject($str, false);
133
	$issuer = cert_get_issuer($str, false);
134
	
135
	// Find my issuer unless self-signed
136
	if($issuer <> $subject) {
137
		$issuer_crt =& lookup_ca_by_subject($issuer);
138
		if($issuer_crt)
139
			$ca['caref'] = $issuer_crt['refid'];
140
	}
141
142
	/* Correct if child certificate was loaded first */
143 1e332e98 jim-p
	if (is_array($config['ca']))
144
		foreach ($config['ca'] as & $oca)
145 d43ad788 Scott Ullrich
		{
146
			$issuer = cert_get_issuer($oca['crt']);
147
			if($ca['refid']<>$oca['refid'] && $issuer==$subject)
148
				$oca['caref'] = $ca['refid'];
149
		}
150 1e332e98 jim-p
	if (is_array($config['cert']))
151
		foreach ($config['cert'] as & $cert)
152 d43ad788 Scott Ullrich
		{
153
			$issuer = cert_get_issuer($cert['crt']);
154
			if($issuer==$subject)
155
				$cert['caref'] = $ca['refid'];
156
		}
157
	return true;
158
}
159
160
function ca_create(& $ca, $keylen, $lifetime, $dn) {
161
162
	$args = array(
163
		"digest_alg" => "sha1",
164 51dbdcde Ermal Lu?i
		"private_key_bits" => (int)$keylen,
165 d43ad788 Scott Ullrich
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
166
		"encrypt_key" => false);
167
168
	// generate a new key pair
169 838e27bf jim-p
	$res_key = openssl_pkey_new($args);
170 d43ad788 Scott Ullrich
171
	// generate a certificate signing request
172
	$res_csr = openssl_csr_new($dn, $res_key, $args);
173
174
	// self sign the certificate
175
	$res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args);
176
177
	// export our certificate data
178
	openssl_pkey_export($res_key, $str_key);
179
	openssl_x509_export($res_crt, $str_crt);
180
181
	// return our ca information
182
	$ca['crt'] = base64_encode($str_crt);
183
	$ca['prv'] = base64_encode($str_key);
184
	$ca['serial'] = 0;
185
186
	return true;
187
}
188
189
function cert_import(& $cert, $crt_str, $key_str) {
190
191
	$cert['crt'] = base64_encode($crt_str);
192
	$cert['prv'] = base64_encode($key_str);
193
194
	$subject = cert_get_subject($crt_str, false);
195
	$issuer = cert_get_issuer($crt_str, false);
196
	
197
	// Find my issuer unless self-signed
198
	if($issuer <> $subject) {
199
		$issuer_crt =& lookup_ca_by_subject($issuer);
200
		if($issuer_crt)
201
			$cert['caref'] = $issuer_crt['refid'];
202
	}
203
	return true;
204
}
205
206
function cert_create(& $cert, $caref, $keylen, $lifetime, $dn) {
207
208
	$ca =& lookup_ca($caref);
209
	if (!$ca)
210
		return false;
211
212
	$ca_str_crt = base64_decode($ca['crt']);
213
	$ca_str_key = base64_decode($ca['prv']);
214
	$ca_res_crt = openssl_x509_read($ca_str_crt);
215 ae4dbded Ermal
	$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
216
	$ca_serial = ++$ca['serial'];
217 d43ad788 Scott Ullrich
218
	$args = array(
219
		"digest_alg" => "sha1",
220 51dbdcde Ermal Lu?i
		"private_key_bits" => (int)$keylen,
221 d43ad788 Scott Ullrich
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
222
		"encrypt_key" => false);
223
224
	// generate a new key pair
225 838e27bf jim-p
	$res_key = openssl_pkey_new($args);
226 d43ad788 Scott Ullrich
227
	// generate a certificate signing request
228
	$res_csr = openssl_csr_new($dn, $res_key, $args);
229
230
	// self sign the certificate
231
	$res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime,
232
				 $args, $ca_serial);
233
234
	// export our certificate data
235
	openssl_pkey_export($res_key, $str_key);
236
	openssl_x509_export($res_crt, $str_crt);
237
238
	// return our certificate information
239
	$cert['caref'] = $caref;
240
	$cert['crt'] = base64_encode($str_crt);
241
	$cert['prv'] = base64_encode($str_key);
242
243
	return true;
244
}
245
246
function csr_generate(& $cert, $keylen, $dn) {
247
248
	$args = array(
249
		"digest_alg" => "sha1",
250 3198b8d3 Ermal Lu?i
		"private_key_bits" => (int)$keylen,
251 d43ad788 Scott Ullrich
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
252
		"encrypt_key" => false);
253
254
	// generate a new key pair
255 838e27bf jim-p
	$res_key = openssl_pkey_new($args);
256 d43ad788 Scott Ullrich
257
	// generate a certificate signing request
258
	$res_csr = openssl_csr_new($dn, $res_key, $args);
259
260
	// export our request data
261
	openssl_pkey_export($res_key, $str_key);
262
	openssl_csr_export($res_csr, $str_csr);
263
264
	// return our request information
265
	$cert['csr'] = base64_encode($str_csr);
266
	$cert['prv'] = base64_encode($str_key);
267
268
	return true;
269
}
270
271
function csr_complete(& $cert, $str_crt) {
272
273
	// return our request information
274
	$cert['crt'] = base64_encode($str_crt);
275
	unset($cert['csr']);
276
277
	return true;
278
}
279
280
function csr_get_subject($str_crt, $decode = true) {
281
282
	if ($decode)
283
		$str_crt = base64_decode($str_crt);
284
285
	$components = openssl_csr_get_subject($str_crt);
286
287
	if (!is_array($components))
288
		return "unknown";
289
290 311f93cd Ermal
	ksort($components);
291 d43ad788 Scott Ullrich
	foreach ($components as $a => $v) {
292
		if (!strlen($subject))
293
			$subject = "{$a}={$v}";
294
		else
295
			$subject = "{$a}={$v}, {$subject}";
296
	}
297
298
	return $subject;
299
}
300
301
function cert_get_subject($str_crt, $decode = true) {
302
303
	if ($decode)
304
		$str_crt = base64_decode($str_crt);
305
306
	$inf_crt = openssl_x509_parse($str_crt);
307
	$components = $inf_crt['subject'];
308
309
	if (!is_array($components))
310
		return "unknown";
311
312 b89c34aa Ermal
	ksort($components);
313 d43ad788 Scott Ullrich
	foreach ($components as $a => $v) {
314 b89c34aa Ermal
		if (is_array($v)) {
315
			ksort($v);
316 5479df47 jim-p
			foreach ($v as $w) {
317
				$asubject = "{$a}={$w}";
318
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
319
			}
320 b89c34aa Ermal
		} else {
321 5479df47 jim-p
			$asubject = "{$a}={$v}";
322
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
323
		}
324 d43ad788 Scott Ullrich
	}
325
326
	return $subject;
327
}
328
329
function cert_get_subject_array($crt) {
330
	$str_crt = base64_decode($crt);
331
	$inf_crt = openssl_x509_parse($str_crt);
332
	$components = $inf_crt['subject'];
333 e4d7a064 jim-p
334
	if (!is_array($components))
335
		return;
336
337 d43ad788 Scott Ullrich
	$subject_array = array();
338
339
	foreach($components as $a => $v)
340
		$subject_array[] = array('a' => $a, 'v' => $v);
341
342
	return $subject_array;
343
}
344
345 a84eb838 jim-p
function cert_get_subject_hash($crt) {
346
	$str_crt = base64_decode($crt);
347
	$inf_crt = openssl_x509_parse($str_crt);
348
	return $inf_crt['subject'];
349
}
350
351 d43ad788 Scott Ullrich
function cert_get_issuer($str_crt, $decode = true) {
352
353
	if ($decode)
354
		$str_crt = base64_decode($str_crt);
355
356
	$inf_crt = openssl_x509_parse($str_crt);
357
	$components = $inf_crt['issuer'];
358
	
359 bfa992bc jim-p
	ksort($components);
360 d43ad788 Scott Ullrich
	if (!is_array($components))
361
		return "unknown";
362
	foreach ($components as $a => $v) {
363
		if (!strlen($issuer))
364
			$issuer = "{$a}={$v}";
365
		else
366
			$issuer = "{$a}={$v}, {$issuer}";
367
	}
368
369
	return $issuer;
370
}
371
372 dea98903 jim-p
function is_user_cert($certref) {
373 dab2e769 jim-p
	global $config;
374
	if (!is_array($config['system']['user']))
375
		return;
376
	foreach ($config['system']['user'] as $user) {
377
		if (!is_array($user['cert']))
378
			continue;
379
		foreach ($user['cert'] as $cert) {
380 dea98903 jim-p
			if ($certref == $cert)
381
				return true;
382 dab2e769 jim-p
		}
383
	}
384 dea98903 jim-p
	return false;
385 dab2e769 jim-p
}
386
387 dea98903 jim-p
function is_openvpn_server_cert($certref) {
388 dab2e769 jim-p
	global $config;
389 dea98903 jim-p
	if (!is_array($config['openvpn']['openvpn-server']))
390
		return;
391
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
392
		if ($ovpns['certref'] == $certref)
393
			return true;
394
	}
395
	return false;
396
}
397
398
function is_openvpn_client_cert($certref) {
399
	global $config;
400
	if (!is_array($config['openvpn']['openvpn-client']))
401
		return;
402
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
403
		if ($ovpnc['certref'] == $certref)
404
			return true;
405
	}
406
	return false;
407
}
408
409
function is_ipsec_cert($certref) {
410
	global $config;
411
	if (!is_array($config['ipsec']['phase1']))
412
		return;
413
	foreach ($config['ipsec']['phase1'] as $ipsec) {
414
		if ($ipsec['certref'] == $certref)
415
			return true;
416
	}
417
	return false;
418
}
419
420
function is_webgui_cert($certref) {
421
	global $config;
422 8364184a jim-p
	if (($config['system']['webgui']['ssl-certref'] == $certref)
423
		&& ($config['system']['webgui']['protocol'] != "http"))
424 dea98903 jim-p
		return true;
425
}
426
427
function cert_in_use($certref) {
428
	return (is_webgui_cert($certref) ||
429
		is_user_cert($certref) ||
430
		is_openvpn_server_cert($certref) ||
431
		is_openvpn_client_cert($certref) ||
432
		is_ipsec_cert($certref));
433 dab2e769 jim-p
}
434
435 c5f010aa jim-p
/*
436
CRL code is a *WORK IN PROGRESS* do not try to use these functions yet.
437
438
OpenSSL CRL status code constants.
439
OCSP_REVOKED_STATUS_NOSTATUS
440
OCSP_REVOKED_STATUS_UNSPECIFIED
441
OCSP_REVOKED_STATUS_KEYCOMPROMISE
442
OCSP_REVOKED_STATUS_CACOMPROMISE
443
OCSP_REVOKED_STATUS_AFFILIATIONCHANGED
444
OCSP_REVOKED_STATUS_SUPERSEDED
445
OCSP_REVOKED_STATUS_CESSATIONOFOPERATION
446
OCSP_REVOKED_STATUS_CERTIFICATEHOLD
447
OCSP_REVOKED_STATUS_REMOVEFROMCRL
448
*/
449
450
$openssl_crl_status = array(
451
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
452
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
453
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
454
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
455
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
456
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
457
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
458 fc54f29b jim-p
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
459 c5f010aa jim-p
);
460
461
function crl_create(& $crl, $caref, $name, $serial=0, $lifetime=9999) {
462
	global $config;
463
	$ca =& lookup_ca($caref);
464
	if (!$ca)
465
		return false;
466 f2a86ca9 jim-p
	$crl['descr'] = $name;
467 c5f010aa jim-p
	$crl['caref'] = $caref;
468
	$crl['serial'] = $serial;
469
	$crl['lifetime'] = $lifetime;
470
	$crl['cert'] = array();
471
	$crl_res = crl_update($crl);
472
	$config['crl'][] = $crl;
473
	return $crl_res;
474
}
475
476
function crl_update(& $crl) {
477
	global $config;
478
	$ca =& lookup_ca($crl['caref']);
479
	if (!$ca)
480
		return false;
481 7b757d1b jim-p
	// If we have text but no certs, it was imported and cannot be updated.
482
	if (!empty($crl['text']) && empty($crl['cert']))
483
		return false;
484 c5f010aa jim-p
	$crl['serial']++;
485
	$ca_str_crt = base64_decode($ca['crt']);
486
	$ca_str_key = base64_decode($ca['prv']);
487
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
488
	foreach ($crl['cert'] as $cert) {
489
		openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
490
	}
491
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
492
	$crl['text'] = base64_encode($crl_text);
493
	return $crl_res;
494
}
495
496
function cert_revoke($cert, & $crl, $reason=OCSP_REVOKED_STATUS_UNSPECIFIED) {
497
	global $config;
498 fb3f1993 jim-p
	if (is_cert_revoked($cert, $crl['refid']))
499 c5f010aa jim-p
		return true;
500 7b757d1b jim-p
	// If we have text but no certs, it was imported and cannot be updated.
501 fb3f1993 jim-p
	if (!is_crl_internal($crl))
502 7b757d1b jim-p
		return false;
503 c5f010aa jim-p
	$cert["reason"] = $reason;
504
	$cert["revoke_time"] = time();
505
	$crl["cert"][] = $cert;
506
	crl_update($crl);
507 fb3f1993 jim-p
	return true;
508 c5f010aa jim-p
}
509
510
function cert_unrevoke($cert, & $crl) {
511
	global $config;
512 fb3f1993 jim-p
	if (!is_crl_internal($crl))
513 7b757d1b jim-p
		return false;
514 c5f010aa jim-p
	foreach ($crl['cert'] as $id => $rcert) {
515 f2a86ca9 jim-p
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
516 c5f010aa jim-p
			unset($crl['cert'][$id]);
517
			crl_update($crl);
518
			return true;
519
		}
520
	}
521
	return false;
522
}
523
524 fb3f1993 jim-p
function is_cert_revoked($cert, $crlref = "") {
525 c5f010aa jim-p
	global $config;
526 088ce869 jim-p
	if (!is_array($config['crl']))
527 c5f010aa jim-p
		return false;
528
529 fb3f1993 jim-p
	if (!empty($crlref)) {
530 28ff7ace jim-p
		$crl = lookup_crl($crlref);
531 088ce869 jim-p
		if (!is_array($crl['cert']))
532 fb3f1993 jim-p
			return false;
533 088ce869 jim-p
		foreach ($crl['cert'] as $rcert) {
534 f2a86ca9 jim-p
			if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
535 c5f010aa jim-p
				return true;
536
		}
537 fb3f1993 jim-p
	} else {
538
		foreach ($config['crl'] as $crl) {
539
			if (!is_array($crl['cert']))
540
				continue;
541
			foreach ($crl['cert'] as $rcert) {
542 f2a86ca9 jim-p
				if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
543 fb3f1993 jim-p
					return true;
544
			}
545
		}
546
	}
547
	return false;
548
}
549
550
function is_openvpn_server_crl($crlref) {
551
	global $config;
552
	if (!is_array($config['openvpn']['openvpn-server']))
553
		return;
554
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
555 cd9f13e0 jim-p
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref))
556 fb3f1993 jim-p
			return true;
557 c5f010aa jim-p
	}
558
	return false;
559
}
560
561 fb3f1993 jim-p
// Keep this general to allow for future expansion. See cert_in_use() above.
562
function crl_in_use($crlref) {
563
	return (is_openvpn_server_crl($crlref));
564
}
565
566
function is_crl_internal($crl) {
567
	return !(!empty($crl['text']) && empty($crl['cert']));
568
}
569
570 b89c34aa Ermal
?>