Project

General

Profile

Download (14.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
/* this function works on x509 (crt), rsa key (prv), and req(csr) */
373
function cert_get_modulus($str_crt, $decode = true, $type = "crt"){
374
	if ($decode)
375
		$str_crt = base64_decode($str_crt);
376

    
377
	$modulus = "";
378
	if ( in_array($type, array("crt", "prv", "csr")) ) {
379
			$type = str_replace( array("crt","prv","csr"), array("x509","rsa","req"), $type);
380
			$modulus = exec("echo \"{$str_crt}\" | openssl {$type} -noout -modulus");
381
	}
382
	return $modulus;
383
}
384
function csr_get_modulus($str_crt, $decode = true){
385
	return cert_get_modulus($str_crt, $decode, "csr");
386
}
387
function prv_get_modulus($str_crt, $decode = true){
388
	return cert_get_modulus($str_crt, $decode, "prv");
389
}
390

    
391
function is_user_cert($certref) {
392
	global $config;
393
	if (!is_array($config['system']['user']))
394
		return;
395
	foreach ($config['system']['user'] as $user) {
396
		if (!is_array($user['cert']))
397
			continue;
398
		foreach ($user['cert'] as $cert) {
399
			if ($certref == $cert)
400
				return true;
401
		}
402
	}
403
	return false;
404
}
405

    
406
function is_openvpn_server_cert($certref) {
407
	global $config;
408
	if (!is_array($config['openvpn']['openvpn-server']))
409
		return;
410
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
411
		if ($ovpns['certref'] == $certref)
412
			return true;
413
	}
414
	return false;
415
}
416

    
417
function is_openvpn_client_cert($certref) {
418
	global $config;
419
	if (!is_array($config['openvpn']['openvpn-client']))
420
		return;
421
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
422
		if ($ovpnc['certref'] == $certref)
423
			return true;
424
	}
425
	return false;
426
}
427

    
428
function is_ipsec_cert($certref) {
429
	global $config;
430
	if (!is_array($config['ipsec']['phase1']))
431
		return;
432
	foreach ($config['ipsec']['phase1'] as $ipsec) {
433
		if ($ipsec['certref'] == $certref)
434
			return true;
435
	}
436
	return false;
437
}
438

    
439
function is_webgui_cert($certref) {
440
	global $config;
441
	if (($config['system']['webgui']['ssl-certref'] == $certref)
442
		&& ($config['system']['webgui']['protocol'] != "http"))
443
		return true;
444
}
445

    
446
function cert_in_use($certref) {
447
	return (is_webgui_cert($certref) ||
448
		is_user_cert($certref) ||
449
		is_openvpn_server_cert($certref) ||
450
		is_openvpn_client_cert($certref) ||
451
		is_ipsec_cert($certref));
452
}
453

    
454
/*
455
CRL code is a *WORK IN PROGRESS* do not try to use these functions yet.
456

    
457
OpenSSL CRL status code constants.
458
OCSP_REVOKED_STATUS_NOSTATUS
459
OCSP_REVOKED_STATUS_UNSPECIFIED
460
OCSP_REVOKED_STATUS_KEYCOMPROMISE
461
OCSP_REVOKED_STATUS_CACOMPROMISE
462
OCSP_REVOKED_STATUS_AFFILIATIONCHANGED
463
OCSP_REVOKED_STATUS_SUPERSEDED
464
OCSP_REVOKED_STATUS_CESSATIONOFOPERATION
465
OCSP_REVOKED_STATUS_CERTIFICATEHOLD
466
OCSP_REVOKED_STATUS_REMOVEFROMCRL
467
*/
468

    
469
$openssl_crl_status = array(
470
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
471
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
472
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
473
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
474
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
475
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
476
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
477
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
478
);
479

    
480
function crl_create(& $crl, $caref, $name, $serial=0, $lifetime=9999) {
481
	global $config;
482
	$ca =& lookup_ca($caref);
483
	if (!$ca)
484
		return false;
485
	$crl['descr'] = $name;
486
	$crl['caref'] = $caref;
487
	$crl['serial'] = $serial;
488
	$crl['lifetime'] = $lifetime;
489
	$crl['cert'] = array();
490
	$crl_res = crl_update($crl);
491
	$config['crl'][] = $crl;
492
	return $crl_res;
493
}
494

    
495
function crl_update(& $crl) {
496
	global $config;
497
	$ca =& lookup_ca($crl['caref']);
498
	if (!$ca)
499
		return false;
500
	// If we have text but no certs, it was imported and cannot be updated.
501
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert'])))
502
		return false;
503
	$crl['serial']++;
504
	$ca_str_crt = base64_decode($ca['crt']);
505
	$ca_str_key = base64_decode($ca['prv']);
506
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
507
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
508
		foreach ($crl['cert'] as $cert) {
509
			openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
510
		}
511
	}
512
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
513
	$crl['text'] = base64_encode($crl_text);
514
	return $crl_res;
515
}
516

    
517
function cert_revoke($cert, & $crl, $reason=OCSP_REVOKED_STATUS_UNSPECIFIED) {
518
	global $config;
519
	if (is_cert_revoked($cert, $crl['refid']))
520
		return true;
521
	// If we have text but no certs, it was imported and cannot be updated.
522
	if (!is_crl_internal($crl))
523
		return false;
524
	$cert["reason"] = $reason;
525
	$cert["revoke_time"] = time();
526
	$crl["cert"][] = $cert;
527
	crl_update($crl);
528
	return true;
529
}
530

    
531
function cert_unrevoke($cert, & $crl) {
532
	global $config;
533
	if (!is_crl_internal($crl))
534
		return false;
535
	foreach ($crl['cert'] as $id => $rcert) {
536
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
537
			unset($crl['cert'][$id]);
538
			if (count($crl['cert']) == 0) {
539
				// Protect against accidentally switching the type to imported, for older CRLs
540
				if (!isset($crl['method']))
541
					$crl['method'] = "internal";
542
				crl_update($crl);
543
			} else
544
				crl_update($crl);
545
			return true;
546
		}
547
	}
548
	return false;
549
}
550

    
551
function is_cert_revoked($cert, $crlref = "") {
552
	global $config;
553
	if (!is_array($config['crl']))
554
		return false;
555

    
556
	if (!empty($crlref)) {
557
		$crl = lookup_crl($crlref);
558
		if (!is_array($crl['cert']))
559
			return false;
560
		foreach ($crl['cert'] as $rcert) {
561
			if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
562
				return true;
563
		}
564
	} else {
565
		foreach ($config['crl'] as $crl) {
566
			if (!is_array($crl['cert']))
567
				continue;
568
			foreach ($crl['cert'] as $rcert) {
569
				if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
570
					return true;
571
			}
572
		}
573
	}
574
	return false;
575
}
576

    
577
function is_openvpn_server_crl($crlref) {
578
	global $config;
579
	if (!is_array($config['openvpn']['openvpn-server']))
580
		return;
581
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
582
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref))
583
			return true;
584
	}
585
	return false;
586
}
587

    
588
// Keep this general to allow for future expansion. See cert_in_use() above.
589
function crl_in_use($crlref) {
590
	return (is_openvpn_server_crl($crlref));
591
}
592

    
593
function is_crl_internal($crl) {
594
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
595
}
596

    
597
?>
(8-8/61)