Project

General

Profile

Download (13.6 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['name'] == $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();
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();
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();
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 (!strlen($subject))
312
			$subject = "{$a}={$v}";
313
		else
314
			$subject = "{$a}={$v}, {$subject}";
315
	}
316

    
317
	return $subject;
318
}
319

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

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

    
328
	$subject_array = array();
329

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

    
333
	return $subject_array;
334
}
335

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

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

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

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

    
359
	return $issuer;
360
}
361

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

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

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

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

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

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

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

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

    
439
$openssl_crl_status = array(
440
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
441
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
442
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
443
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
444
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
445
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
446
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
447
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold",
448
	OCSP_REVOKED_STATUS_REMOVEFROMCRL         => "Remove from CRL"
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['name'] = $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['name'] == $cert['name'])) {
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 = crl_lookup($crlref);
521
		if (!is_array($crl['cert']))
522
			return false;
523
		foreach ($crl['cert'] as $rcert) {
524
			if (($rcert['refid'] == $cert['refid']) || ($rcert['name'] == $cert['name']))
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['name'] == $cert['name']))
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)