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="") {
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
	ksort($components);
290
	foreach ($components as $a => $v) {
291
		if (!strlen($subject))
292
			$subject = "{$a}={$v}";
293
		else
294
			$subject = "{$a}={$v}, {$subject}";
295
	}
296

    
297
	return $subject;
298
}
299

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

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

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

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

    
311
	ksort($components);
312
	foreach ($components as $a => $v) {
313
		if (is_array($v)) {
314
			ksort($v);
315
			foreach ($v as $w) {
316
				$asubject = "{$a}={$w}";
317
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
318
			}
319
		} else {
320
			$asubject = "{$a}={$v}";
321
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
322
		}
323
	}
324

    
325
	return $subject;
326
}
327

    
328
function cert_get_subject_array($crt) {
329
	$str_crt = base64_decode($crt);
330
	$inf_crt = openssl_x509_parse($str_crt);
331
	$components = $inf_crt['subject'];
332

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

    
336
	$subject_array = array();
337

    
338
	foreach($components as $a => $v)
339
		$subject_array[] = array('a' => $a, 'v' => $v);
340

    
341
	return $subject_array;
342
}
343

    
344
function cert_get_subject_hash($crt) {
345
	$str_crt = base64_decode($crt);
346
	$inf_crt = openssl_x509_parse($str_crt);
347
	return $inf_crt['subject'];
348
}
349

    
350
function cert_get_issuer($str_crt, $decode = true) {
351

    
352
	if ($decode)
353
		$str_crt = base64_decode($str_crt);
354

    
355
	$inf_crt = openssl_x509_parse($str_crt);
356
	$components = $inf_crt['issuer'];
357
	
358
	if (!is_array($components))
359
		return "unknown";
360
	foreach ($components as $a => $v) {
361
		if (!strlen($issuer))
362
			$issuer = "{$a}={$v}";
363
		else
364
			$issuer = "{$a}={$v}, {$issuer}";
365
	}
366

    
367
	return $issuer;
368
}
369

    
370
function is_user_cert($certref) {
371
	global $config;
372
	if (!is_array($config['system']['user']))
373
		return;
374
	foreach ($config['system']['user'] as $user) {
375
		if (!is_array($user['cert']))
376
			continue;
377
		foreach ($user['cert'] as $cert) {
378
			if ($certref == $cert)
379
				return true;
380
		}
381
	}
382
	return false;
383
}
384

    
385
function is_openvpn_server_cert($certref) {
386
	global $config;
387
	if (!is_array($config['openvpn']['openvpn-server']))
388
		return;
389
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
390
		if ($ovpns['certref'] == $certref)
391
			return true;
392
	}
393
	return false;
394
}
395

    
396
function is_openvpn_client_cert($certref) {
397
	global $config;
398
	if (!is_array($config['openvpn']['openvpn-client']))
399
		return;
400
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
401
		if ($ovpnc['certref'] == $certref)
402
			return true;
403
	}
404
	return false;
405
}
406

    
407
function is_ipsec_cert($certref) {
408
	global $config;
409
	if (!is_array($config['ipsec']['phase1']))
410
		return;
411
	foreach ($config['ipsec']['phase1'] as $ipsec) {
412
		if ($ipsec['certref'] == $certref)
413
			return true;
414
	}
415
	return false;
416
}
417

    
418
function is_webgui_cert($certref) {
419
	global $config;
420
	if (($config['system']['webgui']['ssl-certref'] == $certref)
421
		&& ($config['system']['webgui']['protocol'] != "http"))
422
		return true;
423
}
424

    
425
function cert_in_use($certref) {
426
	return (is_webgui_cert($certref) ||
427
		is_user_cert($certref) ||
428
		is_openvpn_server_cert($certref) ||
429
		is_openvpn_client_cert($certref) ||
430
		is_ipsec_cert($certref));
431
}
432

    
433
/*
434
CRL code is a *WORK IN PROGRESS* do not try to use these functions yet.
435

    
436
OpenSSL CRL status code constants.
437
OCSP_REVOKED_STATUS_NOSTATUS
438
OCSP_REVOKED_STATUS_UNSPECIFIED
439
OCSP_REVOKED_STATUS_KEYCOMPROMISE
440
OCSP_REVOKED_STATUS_CACOMPROMISE
441
OCSP_REVOKED_STATUS_AFFILIATIONCHANGED
442
OCSP_REVOKED_STATUS_SUPERSEDED
443
OCSP_REVOKED_STATUS_CESSATIONOFOPERATION
444
OCSP_REVOKED_STATUS_CERTIFICATEHOLD
445
OCSP_REVOKED_STATUS_REMOVEFROMCRL
446
*/
447

    
448
$openssl_crl_status = array(
449
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
450
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
451
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
452
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
453
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
454
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
455
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
456
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
457
);
458

    
459
function crl_create(& $crl, $caref, $name, $serial=0, $lifetime=9999) {
460
	global $config;
461
	$ca =& lookup_ca($caref);
462
	if (!$ca)
463
		return false;
464
	$crl['descr'] = $name;
465
	$crl['caref'] = $caref;
466
	$crl['serial'] = $serial;
467
	$crl['lifetime'] = $lifetime;
468
	$crl['cert'] = array();
469
	$crl_res = crl_update($crl);
470
	$config['crl'][] = $crl;
471
	return $crl_res;
472
}
473

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

    
494
function cert_revoke($cert, & $crl, $reason=OCSP_REVOKED_STATUS_UNSPECIFIED) {
495
	global $config;
496
	if (is_cert_revoked($cert, $crl['refid']))
497
		return true;
498
	// If we have text but no certs, it was imported and cannot be updated.
499
	if (!is_crl_internal($crl))
500
		return false;
501
	$cert["reason"] = $reason;
502
	$cert["revoke_time"] = time();
503
	$crl["cert"][] = $cert;
504
	crl_update($crl);
505
	return true;
506
}
507

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

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

    
527
	if (!empty($crlref)) {
528
		$crl = lookup_crl($crlref);
529
		if (!is_array($crl['cert']))
530
			return false;
531
		foreach ($crl['cert'] as $rcert) {
532
			if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
533
				return true;
534
		}
535
	} else {
536
		foreach ($config['crl'] as $crl) {
537
			if (!is_array($crl['cert']))
538
				continue;
539
			foreach ($crl['cert'] as $rcert) {
540
				if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
541
					return true;
542
			}
543
		}
544
	}
545
	return false;
546
}
547

    
548
function is_openvpn_server_crl($crlref) {
549
	global $config;
550
	if (!is_array($config['openvpn']['openvpn-server']))
551
		return;
552
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
553
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref))
554
			return true;
555
	}
556
	return false;
557
}
558

    
559
// Keep this general to allow for future expansion. See cert_in_use() above.
560
function crl_in_use($crlref) {
561
	return (is_openvpn_server_crl($crlref));
562
}
563

    
564
function is_crl_internal($crl) {
565
	return !(!empty($crl['text']) && empty($crl['cert']));
566
}
567

    
568
?>
(8-8/61)