Project

General

Profile

Download (13.7 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="") {
125
	global $config;
126

    
127
	$ca['crt'] = base64_encode($str);
128
	if (!empty($key))
129
		$ca['prv'] = base64_encode($key);
130

    
131
	$subject = cert_get_subject($str, false);
132
	$issuer = cert_get_issuer($str, false);
133
	
134
	// Find my issuer unless self-signed
135
	if($issuer <> $subject) {
136
		$issuer_crt =& lookup_ca_by_subject($issuer);
137
		if($issuer_crt)
138
			$ca['caref'] = $issuer_crt['refid'];
139
	}
140

    
141
	/* Correct if child certificate was loaded first */
142
	if (is_array($config['ca']))
143
		foreach ($config['ca'] as & $oca)
144
		{
145
			$issuer = cert_get_issuer($oca['crt']);
146
			if($ca['refid']<>$oca['refid'] && $issuer==$subject)
147
				$oca['caref'] = $ca['refid'];
148
		}
149
	if (is_array($config['cert']))
150
		foreach ($config['cert'] as & $cert)
151
		{
152
			$issuer = cert_get_issuer($cert['crt']);
153
			if($issuer==$subject)
154
				$cert['caref'] = $ca['refid'];
155
		}
156
	return true;
157
}
158

    
159
function ca_create(& $ca, $keylen, $lifetime, $dn) {
160

    
161
	$args = array(
162
		"digest_alg" => "sha1",
163
		"private_key_bits" => (int)$keylen,
164
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
165
		"encrypt_key" => false);
166

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

    
170
	// generate a certificate signing request
171
	$res_csr = openssl_csr_new($dn, $res_key, $args);
172

    
173
	// self sign the certificate
174
	$res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args);
175

    
176
	// export our certificate data
177
	openssl_pkey_export($res_key, $str_key);
178
	openssl_x509_export($res_crt, $str_crt);
179

    
180
	// return our ca information
181
	$ca['crt'] = base64_encode($str_crt);
182
	$ca['prv'] = base64_encode($str_key);
183
	$ca['serial'] = 0;
184

    
185
	return true;
186
}
187

    
188
function cert_import(& $cert, $crt_str, $key_str) {
189

    
190
	$cert['crt'] = base64_encode($crt_str);
191
	$cert['prv'] = base64_encode($key_str);
192

    
193
	$subject = cert_get_subject($crt_str, false);
194
	$issuer = cert_get_issuer($crt_str, false);
195
	
196
	// Find my issuer unless self-signed
197
	if($issuer <> $subject) {
198
		$issuer_crt =& lookup_ca_by_subject($issuer);
199
		if($issuer_crt)
200
			$cert['caref'] = $issuer_crt['refid'];
201
	}
202
	return true;
203
}
204

    
205
function cert_create(& $cert, $caref, $keylen, $lifetime, $dn) {
206

    
207
	$ca =& lookup_ca($caref);
208
	if (!$ca)
209
		return false;
210

    
211
	$ca_str_crt = base64_decode($ca['crt']);
212
	$ca_str_key = base64_decode($ca['prv']);
213
	$ca_res_crt = openssl_x509_read($ca_str_crt);
214
	$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
215
	$ca_serial = ++$ca['serial'];
216

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

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

    
226
	// generate a certificate signing request
227
	$res_csr = openssl_csr_new($dn, $res_key, $args);
228

    
229
	// self sign the certificate
230
	$res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime,
231
				 $args, $ca_serial);
232

    
233
	// export our certificate data
234
	openssl_pkey_export($res_key, $str_key);
235
	openssl_x509_export($res_crt, $str_crt);
236

    
237
	// return our certificate information
238
	$cert['caref'] = $caref;
239
	$cert['crt'] = base64_encode($str_crt);
240
	$cert['prv'] = base64_encode($str_key);
241

    
242
	return true;
243
}
244

    
245
function csr_generate(& $cert, $keylen, $dn) {
246

    
247
	$args = array(
248
		"digest_alg" => "sha1",
249
		"private_key_bits" => (int)$keylen,
250
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
251
		"encrypt_key" => false);
252

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

    
256
	// generate a certificate signing request
257
	$res_csr = openssl_csr_new($dn, $res_key, $args);
258

    
259
	// export our request data
260
	openssl_pkey_export($res_key, $str_key);
261
	openssl_csr_export($res_csr, $str_csr);
262

    
263
	// return our request information
264
	$cert['csr'] = base64_encode($str_csr);
265
	$cert['prv'] = base64_encode($str_key);
266

    
267
	return true;
268
}
269

    
270
function csr_complete(& $cert, $str_crt) {
271

    
272
	// return our request information
273
	$cert['crt'] = base64_encode($str_crt);
274
	unset($cert['csr']);
275

    
276
	return true;
277
}
278

    
279
function csr_get_subject($str_crt, $decode = true) {
280

    
281
	if ($decode)
282
		$str_crt = base64_decode($str_crt);
283

    
284
	$components = openssl_csr_get_subject($str_crt);
285

    
286
	if (!is_array($components))
287
		return "unknown";
288

    
289
	foreach ($components as $a => $v) {
290
		if (!strlen($subject))
291
			$subject = "{$a}={$v}";
292
		else
293
			$subject = "{$a}={$v}, {$subject}";
294
	}
295

    
296
	return $subject;
297
}
298

    
299
function cert_get_subject($str_crt, $decode = true) {
300

    
301
	if ($decode)
302
		$str_crt = base64_decode($str_crt);
303

    
304
	$inf_crt = openssl_x509_parse($str_crt);
305
	$components = $inf_crt['subject'];
306

    
307
	if (!is_array($components))
308
		return "unknown";
309

    
310
	foreach ($components as $a => $v) {
311
		if (is_array($v))
312
			foreach ($v as $w)
313
				$subject = "{$a}={$w}" . (strlen($subject)) ? ", {$subject}" : "" ;
314
		else
315
			$subject = "{$a}={$v}" . (strlen($subject)) ? ", {$subject}" : "" ;
316
	}
317

    
318
	return $subject;
319
}
320

    
321
function cert_get_subject_array($crt) {
322
	$str_crt = base64_decode($crt);
323
	$inf_crt = openssl_x509_parse($str_crt);
324
	$components = $inf_crt['subject'];
325

    
326
	if (!is_array($components))
327
		return;
328

    
329
	$subject_array = array();
330

    
331
	foreach($components as $a => $v)
332
		$subject_array[] = array('a' => $a, 'v' => $v);
333

    
334
	return $subject_array;
335
}
336

    
337
function cert_get_subject_hash($crt) {
338
	$str_crt = base64_decode($crt);
339
	$inf_crt = openssl_x509_parse($str_crt);
340
	return $inf_crt['subject'];
341
}
342

    
343
function cert_get_issuer($str_crt, $decode = true) {
344

    
345
	if ($decode)
346
		$str_crt = base64_decode($str_crt);
347

    
348
	$inf_crt = openssl_x509_parse($str_crt);
349
	$components = $inf_crt['issuer'];
350
	
351
	if (!is_array($components))
352
		return "unknown";
353
	foreach ($components as $a => $v) {
354
		if (!strlen($issuer))
355
			$issuer = "{$a}={$v}";
356
		else
357
			$issuer = "{$a}={$v}, {$issuer}";
358
	}
359

    
360
	return $issuer;
361
}
362

    
363
function is_user_cert($certref) {
364
	global $config;
365
	if (!is_array($config['system']['user']))
366
		return;
367
	foreach ($config['system']['user'] as $user) {
368
		if (!is_array($user['cert']))
369
			continue;
370
		foreach ($user['cert'] as $cert) {
371
			if ($certref == $cert)
372
				return true;
373
		}
374
	}
375
	return false;
376
}
377

    
378
function is_openvpn_server_cert($certref) {
379
	global $config;
380
	if (!is_array($config['openvpn']['openvpn-server']))
381
		return;
382
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
383
		if ($ovpns['certref'] == $certref)
384
			return true;
385
	}
386
	return false;
387
}
388

    
389
function is_openvpn_client_cert($certref) {
390
	global $config;
391
	if (!is_array($config['openvpn']['openvpn-client']))
392
		return;
393
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
394
		if ($ovpnc['certref'] == $certref)
395
			return true;
396
	}
397
	return false;
398
}
399

    
400
function is_ipsec_cert($certref) {
401
	global $config;
402
	if (!is_array($config['ipsec']['phase1']))
403
		return;
404
	foreach ($config['ipsec']['phase1'] as $ipsec) {
405
		if ($ipsec['certref'] == $certref)
406
			return true;
407
	}
408
	return false;
409
}
410

    
411
function is_webgui_cert($certref) {
412
	global $config;
413
	if ($config['system']['webgui']['ssl-certref'] == $certref)
414
		return true;
415
}
416

    
417
function cert_in_use($certref) {
418
	return (is_webgui_cert($certref) ||
419
		is_user_cert($certref) ||
420
		is_openvpn_server_cert($certref) ||
421
		is_openvpn_client_cert($certref) ||
422
		is_ipsec_cert($certref));
423
}
424

    
425
/*
426
CRL code is a *WORK IN PROGRESS* do not try to use these functions yet.
427

    
428
OpenSSL CRL status code constants.
429
OCSP_REVOKED_STATUS_NOSTATUS
430
OCSP_REVOKED_STATUS_UNSPECIFIED
431
OCSP_REVOKED_STATUS_KEYCOMPROMISE
432
OCSP_REVOKED_STATUS_CACOMPROMISE
433
OCSP_REVOKED_STATUS_AFFILIATIONCHANGED
434
OCSP_REVOKED_STATUS_SUPERSEDED
435
OCSP_REVOKED_STATUS_CESSATIONOFOPERATION
436
OCSP_REVOKED_STATUS_CERTIFICATEHOLD
437
OCSP_REVOKED_STATUS_REMOVEFROMCRL
438
*/
439

    
440
$openssl_crl_status = array(
441
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
442
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
443
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
444
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
445
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
446
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
447
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
448
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
449
);
450

    
451
function crl_create(& $crl, $caref, $name, $serial=0, $lifetime=9999) {
452
	global $config;
453
	$ca =& lookup_ca($caref);
454
	if (!$ca)
455
		return false;
456
	$crl['descr'] = $name;
457
	$crl['caref'] = $caref;
458
	$crl['serial'] = $serial;
459
	$crl['lifetime'] = $lifetime;
460
	$crl['cert'] = array();
461
	$crl_res = crl_update($crl);
462
	$config['crl'][] = $crl;
463
	return $crl_res;
464
}
465

    
466
function crl_update(& $crl) {
467
	global $config;
468
	$ca =& lookup_ca($crl['caref']);
469
	if (!$ca)
470
		return false;
471
	// If we have text but no certs, it was imported and cannot be updated.
472
	if (!empty($crl['text']) && empty($crl['cert']))
473
		return false;
474
	$crl['serial']++;
475
	$ca_str_crt = base64_decode($ca['crt']);
476
	$ca_str_key = base64_decode($ca['prv']);
477
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
478
	foreach ($crl['cert'] as $cert) {
479
		openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
480
	}
481
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
482
	$crl['text'] = base64_encode($crl_text);
483
	return $crl_res;
484
}
485

    
486
function cert_revoke($cert, & $crl, $reason=OCSP_REVOKED_STATUS_UNSPECIFIED) {
487
	global $config;
488
	if (is_cert_revoked($cert, $crl['refid']))
489
		return true;
490
	// If we have text but no certs, it was imported and cannot be updated.
491
	if (!is_crl_internal($crl))
492
		return false;
493
	$cert["reason"] = $reason;
494
	$cert["revoke_time"] = time();
495
	$crl["cert"][] = $cert;
496
	crl_update($crl);
497
	return true;
498
}
499

    
500
function cert_unrevoke($cert, & $crl) {
501
	global $config;
502
	if (!is_crl_internal($crl))
503
		return false;
504
	foreach ($crl['cert'] as $id => $rcert) {
505
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
506
			unset($crl['cert'][$id]);
507
			crl_update($crl);
508
			return true;
509
		}
510
	}
511
	return false;
512
}
513

    
514
function is_cert_revoked($cert, $crlref = "") {
515
	global $config;
516
	if (!is_array($config['crl']))
517
		return false;
518

    
519
	if (!empty($crlref)) {
520
		$crl = lookup_crl($crlref);
521
		if (!is_array($crl['cert']))
522
			return false;
523
		foreach ($crl['cert'] as $rcert) {
524
			if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
525
				return true;
526
		}
527
	} else {
528
		foreach ($config['crl'] as $crl) {
529
			if (!is_array($crl['cert']))
530
				continue;
531
			foreach ($crl['cert'] as $rcert) {
532
				if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
533
					return true;
534
			}
535
		}
536
	}
537
	return false;
538
}
539

    
540
function is_openvpn_server_crl($crlref) {
541
	global $config;
542
	if (!is_array($config['openvpn']['openvpn-server']))
543
		return;
544
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
545
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref))
546
			return true;
547
	}
548
	return false;
549
}
550

    
551
// Keep this general to allow for future expansion. See cert_in_use() above.
552
function crl_in_use($crlref) {
553
	return (is_openvpn_server_crl($crlref));
554
}
555

    
556
function is_crl_internal($crl) {
557
	return !(!empty($crl['text']) && empty($crl['cert']));
558
}
559

    
560
?>
(7-7/54)