Project

General

Profile

Download (17.1 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
	if (!$res_key) return false;
171

    
172
	// generate a certificate signing request
173
	$res_csr = openssl_csr_new($dn, $res_key, $args);
174
	if (!$res_csr) return false;
175

    
176
	// self sign the certificate
177
	$res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args);
178
	if (!$res_crt) return false;
179

    
180
	// export our certificate data
181
	if (!openssl_pkey_export($res_key, $str_key) ||
182
	    !openssl_x509_export($res_crt, $str_crt))
183
		return false;
184

    
185
	// return our ca information
186
	$ca['crt'] = base64_encode($str_crt);
187
	$ca['prv'] = base64_encode($str_key);
188
	$ca['serial'] = 0;
189

    
190
	return true;
191
}
192

    
193
function ca_inter_create(& $ca, $keylen, $lifetime, $dn, $caref) {
194
	// Create Intermediate Certificate Authority
195
	$signing_ca =& lookup_ca($caref);
196
	if (!$signing_ca)
197
		return false;
198

    
199
	$signing_ca_res_crt = openssl_x509_read(base64_decode($signing_ca['crt']));
200
	$signing_ca_res_key = openssl_pkey_get_private(array(0 => base64_decode($signing_ca['prv']) , 1 => ""));
201
	if (!$signing_ca_res_crt || !$signing_ca_res_key) return false;
202
	$signing_ca_serial = ++$signing_ca['serial'];
203

    
204
	$args = array(
205
		"digest_alg" => "sha1",
206
		"private_key_bits" => (int)$keylen,
207
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
208
		"encrypt_key" => false);
209

    
210
	// generate a new key pair
211
	$res_key = openssl_pkey_new($args);
212
	if (!$res_key) return false;
213

    
214
	// generate a certificate signing request
215
	$res_csr = openssl_csr_new($dn, $res_key, $args);
216
	if (!$res_csr) return false;
217

    
218
	// Sign the certificate
219
	$res_crt = openssl_csr_sign($res_csr, $signing_ca_res_crt, $signing_ca_res_key, $lifetime, $args, $signing_ca_serial);
220
	if (!$res_crt) return false;
221

    
222
	// export our certificate data
223
	if (!openssl_pkey_export($res_key, $str_key) ||
224
	    !openssl_x509_export($res_crt, $str_crt))
225
		return false;
226

    
227
	// return our ca information
228
	$ca['crt'] = base64_encode($str_crt);
229
	$ca['prv'] = base64_encode($str_key);
230
	$ca['serial'] = 0;
231

    
232
	return true;
233
}
234

    
235
function cert_import(& $cert, $crt_str, $key_str) {
236

    
237
	$cert['crt'] = base64_encode($crt_str);
238
	$cert['prv'] = base64_encode($key_str);
239

    
240
	$subject = cert_get_subject($crt_str, false);
241
	$issuer = cert_get_issuer($crt_str, false);
242
	
243
	// Find my issuer unless self-signed
244
	if($issuer <> $subject) {
245
		$issuer_crt =& lookup_ca_by_subject($issuer);
246
		if($issuer_crt)
247
			$cert['caref'] = $issuer_crt['refid'];
248
	}
249
	return true;
250
}
251

    
252
function cert_create(& $cert, $caref, $keylen, $lifetime, $dn) {
253

    
254
	$ca =& lookup_ca($caref);
255
	if (!$ca)
256
		return false;
257

    
258
	$ca_str_crt = base64_decode($ca['crt']);
259
	$ca_str_key = base64_decode($ca['prv']);
260
	$ca_res_crt = openssl_x509_read($ca_str_crt);
261
	$ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => ""));
262
	if(!$ca_res_key) return false;
263
	$ca_serial = ++$ca['serial'];
264

    
265
	$args = array(
266
		"digest_alg" => "sha1",
267
		"private_key_bits" => (int)$keylen,
268
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
269
		"encrypt_key" => false);
270

    
271
	// generate a new key pair
272
	$res_key = openssl_pkey_new($args);
273
	if(!$res_key) return false;
274

    
275
	// generate a certificate signing request
276
	$res_csr = openssl_csr_new($dn, $res_key, $args);
277
	if(!$res_csr) return false;
278

    
279
	// self sign the certificate
280
	$res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime,
281
				 $args, $ca_serial);
282
	if(!$res_crt) return false;
283

    
284
	// export our certificate data
285
	if (!openssl_pkey_export($res_key, $str_key) ||
286
	    !openssl_x509_export($res_crt, $str_crt))
287
		return false;
288

    
289
	// return our certificate information
290
	$cert['caref'] = $caref;
291
	$cert['crt'] = base64_encode($str_crt);
292
	$cert['prv'] = base64_encode($str_key);
293

    
294
	return true;
295
}
296

    
297
function csr_generate(& $cert, $keylen, $dn) {
298

    
299
	$args = array(
300
		"digest_alg" => "sha1",
301
		"private_key_bits" => (int)$keylen,
302
		"private_key_type" => OPENSSL_KEYTYPE_RSA,
303
		"encrypt_key" => false);
304

    
305
	// generate a new key pair
306
	$res_key = openssl_pkey_new($args);
307
	if(!$res_key) return false;
308

    
309
	// generate a certificate signing request
310
	$res_csr = openssl_csr_new($dn, $res_key, $args);
311
	if(!$res_csr) return false;
312

    
313
	// export our request data
314
	if (!openssl_pkey_export($res_key, $str_key) ||
315
	    !openssl_csr_export($res_csr, $str_csr))
316
		return false;
317

    
318
	// return our request information
319
	$cert['csr'] = base64_encode($str_csr);
320
	$cert['prv'] = base64_encode($str_key);
321

    
322
	return true;
323
}
324

    
325
function csr_complete(& $cert, $str_crt) {
326

    
327
	// return our request information
328
	$cert['crt'] = base64_encode($str_crt);
329
	unset($cert['csr']);
330

    
331
	return true;
332
}
333

    
334
function csr_get_subject($str_crt, $decode = true) {
335

    
336
	if ($decode)
337
		$str_crt = base64_decode($str_crt);
338

    
339
	$components = openssl_csr_get_subject($str_crt);
340

    
341
	if (empty($components) || !is_array($components))
342
		return "unknown";
343

    
344
	ksort($components);
345
	foreach ($components as $a => $v) {
346
		if (!strlen($subject))
347
			$subject = "{$a}={$v}";
348
		else
349
			$subject = "{$a}={$v}, {$subject}";
350
	}
351

    
352
	return $subject;
353
}
354

    
355
function cert_get_subject($str_crt, $decode = true) {
356

    
357
	if ($decode)
358
		$str_crt = base64_decode($str_crt);
359

    
360
	$inf_crt = openssl_x509_parse($str_crt);
361
	$components = $inf_crt['subject'];
362

    
363
	if (empty($components) || !is_array($components))
364
		return "unknown";
365

    
366
	ksort($components);
367
	foreach ($components as $a => $v) {
368
		if (is_array($v)) {
369
			ksort($v);
370
			foreach ($v as $w) {
371
				$asubject = "{$a}={$w}";
372
				$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
373
			}
374
		} else {
375
			$asubject = "{$a}={$v}";
376
			$subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject;
377
		}
378
	}
379

    
380
	return $subject;
381
}
382

    
383
function cert_get_subject_array($crt) {
384
	$str_crt = base64_decode($crt);
385
	$inf_crt = openssl_x509_parse($str_crt);
386
	$components = $inf_crt['subject'];
387

    
388
	if (!is_array($components))
389
		return;
390

    
391
	$subject_array = array();
392

    
393
	foreach($components as $a => $v)
394
		$subject_array[] = array('a' => $a, 'v' => $v);
395

    
396
	return $subject_array;
397
}
398

    
399
function cert_get_subject_hash($crt) {
400
	$str_crt = base64_decode($crt);
401
	$inf_crt = openssl_x509_parse($str_crt);
402
	return $inf_crt['subject'];
403
}
404

    
405
function cert_get_issuer($str_crt, $decode = true) {
406

    
407
	if ($decode)
408
		$str_crt = base64_decode($str_crt);
409

    
410
	$inf_crt = openssl_x509_parse($str_crt);
411
	$components = $inf_crt['issuer'];
412
	
413
	if (empty($components) || !is_array($components))
414
		return "unknown";
415

    
416
	ksort($components);
417
	foreach ($components as $a => $v) {
418
		if (!strlen($issuer))
419
			$issuer = "{$a}={$v}";
420
		else
421
			$issuer = "{$a}={$v}, {$issuer}";
422
	}
423

    
424
	return $issuer;
425
}
426

    
427
/* this function works on x509 (crt), rsa key (prv), and req(csr) */
428
function cert_get_modulus($str_crt, $decode = true, $type = "crt"){
429
	if ($decode)
430
		$str_crt = base64_decode($str_crt);
431

    
432
	$modulus = "";
433
	if ( in_array($type, array("crt", "prv", "csr")) ) {
434
			$type = str_replace( array("crt","prv","csr"), array("x509","rsa","req"), $type);
435
			$modulus = exec("echo \"{$str_crt}\" | openssl {$type} -noout -modulus");
436
	}
437
	return $modulus;
438
}
439
function csr_get_modulus($str_crt, $decode = true){
440
	return cert_get_modulus($str_crt, $decode, "csr");
441
}
442
function prv_get_modulus($str_crt, $decode = true){
443
	return cert_get_modulus($str_crt, $decode, "prv");
444
}
445

    
446
function is_user_cert($certref) {
447
	global $config;
448
	if (!is_array($config['system']['user']))
449
		return;
450
	foreach ($config['system']['user'] as $user) {
451
		if (!is_array($user['cert']))
452
			continue;
453
		foreach ($user['cert'] as $cert) {
454
			if ($certref == $cert)
455
				return true;
456
		}
457
	}
458
	return false;
459
}
460

    
461
function is_openvpn_server_cert($certref) {
462
	global $config;
463
	if (!is_array($config['openvpn']['openvpn-server']))
464
		return;
465
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
466
		if ($ovpns['certref'] == $certref)
467
			return true;
468
	}
469
	return false;
470
}
471

    
472
function is_openvpn_client_cert($certref) {
473
	global $config;
474
	if (!is_array($config['openvpn']['openvpn-client']))
475
		return;
476
	foreach ($config['openvpn']['openvpn-client'] as $ovpnc) {
477
		if ($ovpnc['certref'] == $certref)
478
			return true;
479
	}
480
	return false;
481
}
482

    
483
function is_ipsec_cert($certref) {
484
	global $config;
485
	if (!is_array($config['ipsec']['phase1']))
486
		return;
487
	foreach ($config['ipsec']['phase1'] as $ipsec) {
488
		if ($ipsec['certref'] == $certref)
489
			return true;
490
	}
491
	return false;
492
}
493

    
494
function is_webgui_cert($certref) {
495
	global $config;
496
	if (($config['system']['webgui']['ssl-certref'] == $certref)
497
		&& ($config['system']['webgui']['protocol'] != "http"))
498
		return true;
499
}
500

    
501
function cert_in_use($certref) {
502
	return (is_webgui_cert($certref) ||
503
		is_user_cert($certref) ||
504
		is_openvpn_server_cert($certref) ||
505
		is_openvpn_client_cert($certref) ||
506
		is_ipsec_cert($certref));
507
}
508

    
509
/*
510
CRL code is a *WORK IN PROGRESS* do not try to use these functions yet.
511

    
512
OpenSSL CRL status code constants.
513
OCSP_REVOKED_STATUS_NOSTATUS
514
OCSP_REVOKED_STATUS_UNSPECIFIED
515
OCSP_REVOKED_STATUS_KEYCOMPROMISE
516
OCSP_REVOKED_STATUS_CACOMPROMISE
517
OCSP_REVOKED_STATUS_AFFILIATIONCHANGED
518
OCSP_REVOKED_STATUS_SUPERSEDED
519
OCSP_REVOKED_STATUS_CESSATIONOFOPERATION
520
OCSP_REVOKED_STATUS_CERTIFICATEHOLD
521
OCSP_REVOKED_STATUS_REMOVEFROMCRL
522
*/
523

    
524
$openssl_crl_status = array(
525
	OCSP_REVOKED_STATUS_NOSTATUS              => "No Status (default)",
526
	OCSP_REVOKED_STATUS_UNSPECIFIED           => "Unspecified",
527
	OCSP_REVOKED_STATUS_KEYCOMPROMISE         => "Key Compromise",
528
	OCSP_REVOKED_STATUS_CACOMPROMISE          => "CA Compromise",
529
	OCSP_REVOKED_STATUS_AFFILIATIONCHANGED    => "Affiliation Changed",
530
	OCSP_REVOKED_STATUS_SUPERSEDED            => "Superseded",
531
	OCSP_REVOKED_STATUS_CESSATIONOFOPERATION  => "Cessation of Operation",
532
	OCSP_REVOKED_STATUS_CERTIFICATEHOLD       => "Certificate Hold"
533
);
534

    
535
function crl_create(& $crl, $caref, $name, $serial=0, $lifetime=9999) {
536
	global $config;
537
	$ca =& lookup_ca($caref);
538
	if (!$ca)
539
		return false;
540
	$crl['descr'] = $name;
541
	$crl['caref'] = $caref;
542
	$crl['serial'] = $serial;
543
	$crl['lifetime'] = $lifetime;
544
	$crl['cert'] = array();
545
	$crl_res = crl_update($crl);
546
	$config['crl'][] = $crl;
547
	return $crl_res;
548
}
549

    
550
function crl_update(& $crl) {
551
	global $config;
552
	$ca =& lookup_ca($crl['caref']);
553
	if (!$ca)
554
		return false;
555
	// If we have text but no certs, it was imported and cannot be updated.
556
	if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert'])))
557
		return false;
558
	$crl['serial']++;
559
	$ca_str_crt = base64_decode($ca['crt']);
560
	$ca_str_key = base64_decode($ca['prv']);
561
	$crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']);
562
	if (is_array($crl['cert']) && (count($crl['cert']) > 0)) {
563
		foreach ($crl['cert'] as $cert) {
564
			openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]);
565
		}
566
	}
567
	openssl_crl_export($crl_res, $crl_text, $ca_str_key);
568
	$crl['text'] = base64_encode($crl_text);
569
	return $crl_res;
570
}
571

    
572
function cert_revoke($cert, & $crl, $reason=OCSP_REVOKED_STATUS_UNSPECIFIED) {
573
	global $config;
574
	if (is_cert_revoked($cert, $crl['refid']))
575
		return true;
576
	// If we have text but no certs, it was imported and cannot be updated.
577
	if (!is_crl_internal($crl))
578
		return false;
579
	$cert["reason"] = $reason;
580
	$cert["revoke_time"] = time();
581
	$crl["cert"][] = $cert;
582
	crl_update($crl);
583
	return true;
584
}
585

    
586
function cert_unrevoke($cert, & $crl) {
587
	global $config;
588
	if (!is_crl_internal($crl))
589
		return false;
590
	foreach ($crl['cert'] as $id => $rcert) {
591
		if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) {
592
			unset($crl['cert'][$id]);
593
			if (count($crl['cert']) == 0) {
594
				// Protect against accidentally switching the type to imported, for older CRLs
595
				if (!isset($crl['method']))
596
					$crl['method'] = "internal";
597
				crl_update($crl);
598
			} else
599
				crl_update($crl);
600
			return true;
601
		}
602
	}
603
	return false;
604
}
605

    
606
function is_cert_revoked($cert, $crlref = "") {
607
	global $config;
608
	if (!is_array($config['crl']))
609
		return false;
610

    
611
	if (!empty($crlref)) {
612
		$crl = lookup_crl($crlref);
613
		if (!is_array($crl['cert']))
614
			return false;
615
		foreach ($crl['cert'] as $rcert) {
616
			if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
617
				return true;
618
		}
619
	} else {
620
		foreach ($config['crl'] as $crl) {
621
			if (!is_array($crl['cert']))
622
				continue;
623
			foreach ($crl['cert'] as $rcert) {
624
				if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr']))
625
					return true;
626
			}
627
		}
628
	}
629
	return false;
630
}
631

    
632
function is_openvpn_server_crl($crlref) {
633
	global $config;
634
	if (!is_array($config['openvpn']['openvpn-server']))
635
		return;
636
	foreach ($config['openvpn']['openvpn-server'] as $ovpns) {
637
		if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref))
638
			return true;
639
	}
640
	return false;
641
}
642

    
643
// Keep this general to allow for future expansion. See cert_in_use() above.
644
function crl_in_use($crlref) {
645
	return (is_openvpn_server_crl($crlref));
646
}
647

    
648
function is_crl_internal($crl) {
649
	return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal"));
650
}
651

    
652
function cert_get_cn($crt, $isref = false) {
653
	/* If this is a certref, not an actual cert, look up the cert first */
654
	if ($isref) {
655
		$cert = lookup_cert($crt);
656
		/* If it's not a valid cert, bail. */
657
		if (!(is_array($cert) && !empty($cert['crt'])))
658
			return "";
659
		$cert = $cert['crt'];
660
	} else {
661
		$cert = $crt;
662
	}
663
	$sub = cert_get_subject_array($cert);
664
	if (is_array($sub)) {
665
		foreach ($sub as $s) {
666
			if (strtoupper($s['a']) == "CN")
667
				return $s['v'];
668
		}
669
	}
670
	return "";
671
}
672

    
673
?>
(8-8/61)