Project

General

Profile

Download (13.9 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/* $Id$ */
3
/*
4
	Copyright (C) 2008 Shrew Soft Inc
5
	Copyright (C) 2010 Jim Pingle <jimp@pfsense.org>
6
	All rights reserved.
7

    
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
		pfSense_MODULE:	certificate_managaer
31
*/
32

    
33
require_once("functions.inc");
34

    
35
function & lookup_ca($refid) {
36
	global $config;
37

    
38
	if (is_array($config['ca']))
39
		foreach ($config['ca'] as & $ca)
40
			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
	if (is_array($config['ca']))
50
		foreach ($config['ca'] as & $ca)
51
		{
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
	if (is_array($config['cert']))
64
		foreach ($config['cert'] as & $cert)
65
			if ($cert['refid'] == $refid)
66
				return $cert;
67

    
68
	return false;
69
}
70

    
71
function & lookup_cert_by_name($name) {
72
	global $config;
73
	if (is_array($config['cert']))
74
		foreach ($config['cert'] as & $cert)
75
			if ($cert['descr'] == $name)
76
				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
function ca_chain_array(& $cert) {
91
	if($cert['caref']) {
92
		$chain = array();
93
		$crt = lookup_ca($cert['caref']);
94
		$chain[] = $crt;
95
		while ($crt) {
96
			$caref = $crt['caref'];
97
			if($caref)
98
				$crt = lookup_ca($caref);
99
			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
function ca_import(& $ca, $str, $key="", $serial=0) {
125
	global $config;
126

    
127
	$ca['crt'] = base64_encode($str);
128
	if (!empty($key))
129
		$ca['prv'] = base64_encode($key);
130
	if (!empty($serial))
131
		$ca['serial'] = $serial;
132
	$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
	if (is_array($config['ca']))
144
		foreach ($config['ca'] as & $oca)
145
		{
146
			$issuer = cert_get_issuer($oca['crt']);
147
			if($ca['refid']<>$oca['refid'] && $issuer==$subject)
148
				$oca['caref'] = $ca['refid'];
149
		}
150
	if (is_array($config['cert']))
151
		foreach ($config['cert'] as & $cert)
152
		{
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
		"private_key_bits" => (int)$keylen,
165
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
166
		"encrypt_key" => false);
167

    
168
	// generate a new key pair
169
	$res_key = openssl_pkey_new($args);
170

    
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
	$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
216
	$ca_serial = ++$ca['serial'];
217

    
218
	$args = array(
219
		"digest_alg" => "sha1",
220
		"private_key_bits" => (int)$keylen,
221
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
222
		"encrypt_key" => false);
223

    
224
	// generate a new key pair
225
	$res_key = openssl_pkey_new($args);
226

    
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
		"private_key_bits" => (int)$keylen,
251
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
252
		"encrypt_key" => false);
253

    
254
	// generate a new key pair
255
	$res_key = openssl_pkey_new($args);
256

    
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
	ksort($components);
291
	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
	ksort($components);
313
	foreach ($components as $a => $v) {
314
		if (is_array($v)) {
315
			ksort($v);
316
			foreach ($v as $w) {
317
				$asubject = "{$a}={$w}";
318
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
319
			}
320
		} else {
321
			$asubject = "{$a}={$v}";
322
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
323
		}
324
	}
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

    
334
	if (!is_array($components))
335
		return;
336

    
337
	$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
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
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
	ksort($components);
360
	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
function is_user_cert($certref) {
373
	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
			if ($certref == $cert)
381
				return true;
382
		}
383
	}
384
	return false;
385
}
386

    
387
function is_openvpn_server_cert($certref) {
388
	global $config;
389
	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
	if (($config['system']['webgui']['ssl-certref'] == $certref)
423
		&& ($config['system']['webgui']['protocol'] != "http"))
424
		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
}
434

    
435
/*
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
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
459
);
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
	$crl['descr'] = $name;
467
	$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
	// 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
	$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
	if (is_cert_revoked($cert, $crl['refid']))
499
		return true;
500
	// If we have text but no certs, it was imported and cannot be updated.
501
	if (!is_crl_internal($crl))
502
		return false;
503
	$cert["reason"] = $reason;
504
	$cert["revoke_time"] = time();
505
	$crl["cert"][] = $cert;
506
	crl_update($crl);
507
	return true;
508
}
509

    
510
function cert_unrevoke($cert, & $crl) {
511
	global $config;
512
	if (!is_crl_internal($crl))
513
		return false;
514
	foreach ($crl['cert'] as $id => $rcert) {
515
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
516
			unset($crl['cert'][$id]);
517
			crl_update($crl);
518
			return true;
519
		}
520
	}
521
	return false;
522
}
523

    
524
function is_cert_revoked($cert, $crlref = "") {
525
	global $config;
526
	if (!is_array($config['crl']))
527
		return false;
528

    
529
	if (!empty($crlref)) {
530
		$crl = lookup_crl($crlref);
531
		if (!is_array($crl['cert']))
532
			return false;
533
		foreach ($crl['cert'] as $rcert) {
534
			if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
535
				return true;
536
		}
537
	} else {
538
		foreach ($config['crl'] as $crl) {
539
			if (!is_array($crl['cert']))
540
				continue;
541
			foreach ($crl['cert'] as $rcert) {
542
				if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
543
					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
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref))
556
			return true;
557
	}
558
	return false;
559
}
560

    
561
// 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
?>
(8-8/61)