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
				$asubject = "{$a}={$w}";
314
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
315
			}
316
		else {
317
			$asubject = "{$a}={$v}";
318
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
319
		}
320
	}
321

    
322
	return $subject;
323
}
324

    
325
function cert_get_subject_array($crt) {
326
	$str_crt = base64_decode($crt);
327
	$inf_crt = openssl_x509_parse($str_crt);
328
	$components = $inf_crt['subject'];
329

    
330
	if (!is_array($components))
331
		return;
332

    
333
	$subject_array = array();
334

    
335
	foreach($components as $a => $v)
336
		$subject_array[] = array('a' => $a, 'v' => $v);
337

    
338
	return $subject_array;
339
}
340

    
341
function cert_get_subject_hash($crt) {
342
	$str_crt = base64_decode($crt);
343
	$inf_crt = openssl_x509_parse($str_crt);
344
	return $inf_crt['subject'];
345
}
346

    
347
function cert_get_issuer($str_crt, $decode = true) {
348

    
349
	if ($decode)
350
		$str_crt = base64_decode($str_crt);
351

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

    
364
	return $issuer;
365
}
366

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

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

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

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

    
415
function is_webgui_cert($certref) {
416
	global $config;
417
	if ($config['system']['webgui']['ssl-certref'] == $certref)
418
		return true;
419
}
420

    
421
function cert_in_use($certref) {
422
	return (is_webgui_cert($certref) ||
423
		is_user_cert($certref) ||
424
		is_openvpn_server_cert($certref) ||
425
		is_openvpn_client_cert($certref) ||
426
		is_ipsec_cert($certref));
427
}
428

    
429
/*
430
CRL code is a *WORK IN PROGRESS* do not try to use these functions yet.
431

    
432
OpenSSL CRL status code constants.
433
OCSP_REVOKED_STATUS_NOSTATUS
434
OCSP_REVOKED_STATUS_UNSPECIFIED
435
OCSP_REVOKED_STATUS_KEYCOMPROMISE
436
OCSP_REVOKED_STATUS_CACOMPROMISE
437
OCSP_REVOKED_STATUS_AFFILIATIONCHANGED
438
OCSP_REVOKED_STATUS_SUPERSEDED
439
OCSP_REVOKED_STATUS_CESSATIONOFOPERATION
440
OCSP_REVOKED_STATUS_CERTIFICATEHOLD
441
OCSP_REVOKED_STATUS_REMOVEFROMCRL
442
*/
443

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

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

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

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

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

    
518
function is_cert_revoked($cert, $crlref = "") {
519
	global $config;
520
	if (!is_array($config['crl']))
521
		return false;
522

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

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

    
555
// Keep this general to allow for future expansion. See cert_in_use() above.
556
function crl_in_use($crlref) {
557
	return (is_openvpn_server_crl($crlref));
558
}
559

    
560
function is_crl_internal($crl) {
561
	return !(!empty($crl['text']) && empty($crl['cert']));
562
}
563

    
564
?>
(8-8/61)