2 * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL
);
29 #include <ipxe/refcnt.h>
30 #include <ipxe/malloc.h>
31 #include <ipxe/interface.h>
32 #include <ipxe/xfer.h>
33 #include <ipxe/open.h>
34 #include <ipxe/iobuf.h>
35 #include <ipxe/xferbuf.h>
36 #include <ipxe/process.h>
37 #include <ipxe/x509.h>
38 #include <ipxe/settings.h>
39 #include <ipxe/dhcp.h>
40 #include <ipxe/base64.h>
41 #include <ipxe/crc32.h>
42 #include <ipxe/ocsp.h>
43 #include <ipxe/validator.h>
47 * Certificate validator
51 /** A certificate validator */
53 /** Reference count */
55 /** Job control interface */
57 /** Data transfer interface */
58 struct interface xfer
;
61 struct process process
;
63 /** X.509 certificate chain */
64 struct x509_chain
*chain
;
66 struct ocsp_check
*ocsp
;
68 struct xfer_buffer buffer
;
69 /** Action to take upon completed transfer */
70 int ( * done
) ( struct validator
*validator
, const void *data
,
75 * Free certificate validator
77 * @v refcnt Reference count
79 static void validator_free ( struct refcnt
*refcnt
) {
80 struct validator
*validator
=
81 container_of ( refcnt
, struct validator
, refcnt
);
83 DBGC2 ( validator
, "VALIDATOR %p freed\n", validator
);
84 x509_chain_put ( validator
->chain
);
85 ocsp_put ( validator
->ocsp
);
86 xferbuf_free ( &validator
->buffer
);
91 * Mark certificate validation as finished
93 * @v validator Certificate validator
94 * @v rc Reason for finishing
96 static void validator_finished ( struct validator
*validator
, int rc
) {
99 process_del ( &validator
->process
);
101 /* Close all interfaces */
102 intf_shutdown ( &validator
->xfer
, rc
);
103 intf_shutdown ( &validator
->job
, rc
);
106 /****************************************************************************
108 * Job control interface
112 /** Certificate validator job control interface operations */
113 static struct interface_operation validator_job_operations
[] = {
114 INTF_OP ( intf_close
, struct validator
*, validator_finished
),
117 /** Certificate validator job control interface descriptor */
118 static struct interface_descriptor validator_job_desc
=
119 INTF_DESC ( struct validator
, job
, validator_job_operations
);
121 /****************************************************************************
123 * Cross-signing certificates
127 /** Cross-signed certificate source setting */
128 const struct setting crosscert_setting
__setting ( SETTING_CRYPTO
, crosscert
)={
130 .description
= "Cross-signed certificate source",
131 .tag
= DHCP_EB_CROSS_CERT
,
132 .type
= &setting_type_string
,
135 /** Default cross-signed certificate source */
136 static const char crosscert_default
[] = "http://ca.ipxe.org/auto";
139 * Append cross-signing certificates to certificate chain
141 * @v validator Certificate validator
142 * @v data Raw cross-signing certificate data
143 * @v len Length of raw data
144 * @ret rc Return status code
146 static int validator_append ( struct validator
*validator
,
147 const void *data
, size_t len
) {
148 struct asn1_cursor cursor
;
149 struct x509_chain
*certs
;
150 struct x509_certificate
*cert
;
151 struct x509_certificate
*last
;
154 /* Allocate certificate list */
155 certs
= x509_alloc_chain();
158 goto err_alloc_certs
;
161 /* Initialise cursor */
165 /* Enter certificateSet */
166 if ( ( rc
= asn1_enter ( &cursor
, ASN1_SET
) ) != 0 ) {
167 DBGC ( validator
, "VALIDATOR %p could not enter "
168 "certificateSet: %s\n", validator
, strerror ( rc
) );
169 goto err_certificateset
;
172 /* Add each certificate to list */
173 while ( cursor
.len
) {
175 /* Add certificate to chain */
176 if ( ( rc
= x509_append_raw ( certs
, cursor
.data
,
177 cursor
.len
) ) != 0 ) {
178 DBGC ( validator
, "VALIDATOR %p could not append "
180 validator
, strerror ( rc
) );
181 DBGC_HDA ( validator
, 0, cursor
.data
, cursor
.len
);
184 cert
= x509_last ( certs
);
185 DBGC ( validator
, "VALIDATOR %p found certificate %s\n",
186 validator
, x509_name ( cert
) );
188 /* Move to next certificate */
189 asn1_skip_any ( &cursor
);
192 /* Append certificates to chain */
193 last
= x509_last ( validator
->chain
);
194 if ( ( rc
= x509_auto_append ( validator
->chain
, certs
) ) != 0 ) {
195 DBGC ( validator
, "VALIDATOR %p could not append "
196 "certificates: %s\n", validator
, strerror ( rc
) );
197 goto err_auto_append
;
200 /* Check that at least one certificate has been added */
201 if ( last
== x509_last ( validator
->chain
) ) {
202 DBGC ( validator
, "VALIDATOR %p failed to append any "
203 "applicable certificates\n", validator
);
205 goto err_no_progress
;
208 /* Drop reference to certificate list */
209 x509_chain_put ( certs
);
216 x509_chain_put ( certs
);
222 * Start download of cross-signing certificate
224 * @v validator Certificate validator
225 * @v issuer Required issuer
226 * @ret rc Return status code
228 static int validator_start_download ( struct validator
*validator
,
229 const struct asn1_cursor
*issuer
) {
230 const char *crosscert
;
231 char *crosscert_copy
;
233 size_t uri_string_len
;
238 /* Determine cross-signed certificate source */
239 fetch_string_setting_copy ( NULL
, &crosscert_setting
, &crosscert_copy
);
240 crosscert
= ( crosscert_copy ? crosscert_copy
: crosscert_default
);
242 /* Allocate URI string */
243 uri_string_len
= ( strlen ( crosscert
) + 22 /* "/%08x.der?subject=" */
244 + base64_encoded_len ( issuer
->len
) + 1 /* NUL */ );
245 uri_string
= zalloc ( uri_string_len
);
246 if ( ! uri_string
) {
248 goto err_alloc_uri_string
;
252 crc
= crc32_le ( 0xffffffffUL
, issuer
->data
, issuer
->len
);
254 /* Generate URI string */
255 len
= snprintf ( uri_string
, uri_string_len
, "%s/%08x.der?subject=",
257 base64_encode ( issuer
->data
, issuer
->len
, ( uri_string
+ len
),
258 ( uri_string_len
- len
) );
259 DBGC ( validator
, "VALIDATOR %p downloading cross-signed certificate "
260 "from %s\n", validator
, uri_string
);
262 /* Set completion handler */
263 validator
->done
= validator_append
;
266 if ( ( rc
= xfer_open_uri_string ( &validator
->xfer
,
267 uri_string
) ) != 0 ) {
268 DBGC ( validator
, "VALIDATOR %p could not open %s: %s\n",
269 validator
, uri_string
, strerror ( rc
) );
270 goto err_open_uri_string
;
278 err_alloc_uri_string
:
279 free ( crosscert_copy
);
283 /****************************************************************************
290 * Validate OCSP response
292 * @v validator Certificate validator
293 * @v data Raw OCSP response
294 * @v len Length of raw data
295 * @ret rc Return status code
297 static int validator_ocsp_validate ( struct validator
*validator
,
298 const void *data
, size_t len
) {
302 /* Record OCSP response */
303 if ( ( rc
= ocsp_response ( validator
->ocsp
, data
, len
) ) != 0 ) {
304 DBGC ( validator
, "VALIDATOR %p could not record OCSP "
305 "response: %s\n", validator
, strerror ( rc
) );
309 /* Validate OCSP response */
311 if ( ( rc
= ocsp_validate ( validator
->ocsp
, now
) ) != 0 ) {
312 DBGC ( validator
, "VALIDATOR %p could not validate OCSP "
313 "response: %s\n", validator
, strerror ( rc
) );
317 /* Drop reference to OCSP check */
318 ocsp_put ( validator
->ocsp
);
319 validator
->ocsp
= NULL
;
327 * @v validator Certificate validator
328 * @v cert Certificate to check
329 * @v issuer Issuing certificate
330 * @ret rc Return status code
332 static int validator_start_ocsp ( struct validator
*validator
,
333 struct x509_certificate
*cert
,
334 struct x509_certificate
*issuer
) {
335 const char *uri_string
;
338 /* Create OCSP check */
339 assert ( validator
->ocsp
== NULL
);
340 if ( ( rc
= ocsp_check ( cert
, issuer
, &validator
->ocsp
) ) != 0 ) {
341 DBGC ( validator
, "VALIDATOR %p could not create OCSP check: "
342 "%s\n", validator
, strerror ( rc
) );
346 /* Set completion handler */
347 validator
->done
= validator_ocsp_validate
;
350 uri_string
= validator
->ocsp
->uri_string
;
351 DBGC ( validator
, "VALIDATOR %p performing OCSP check at %s\n",
352 validator
, uri_string
);
353 if ( ( rc
= xfer_open_uri_string ( &validator
->xfer
,
354 uri_string
) ) != 0 ) {
355 DBGC ( validator
, "VALIDATOR %p could not open %s: %s\n",
356 validator
, uri_string
, strerror ( rc
) );
363 /****************************************************************************
365 * Data transfer interface
370 * Close data transfer interface
372 * @v validator Certificate validator
373 * @v rc Reason for close
375 static void validator_xfer_close ( struct validator
*validator
, int rc
) {
377 /* Close data transfer interface */
378 intf_restart ( &validator
->xfer
, rc
);
380 /* Check for errors */
382 DBGC ( validator
, "VALIDATOR %p transfer failed: %s\n",
383 validator
, strerror ( rc
) );
386 DBGC2 ( validator
, "VALIDATOR %p transfer complete\n", validator
);
388 /* Process completed download */
389 assert ( validator
->done
!= NULL
);
390 if ( ( rc
= validator
->done ( validator
, validator
->buffer
.data
,
391 validator
->buffer
.len
) ) != 0 )
394 /* Free downloaded data */
395 xferbuf_free ( &validator
->buffer
);
397 /* Resume validation process */
398 process_add ( &validator
->process
);
404 validator_finished ( validator
, rc
);
410 * @v validator Certificate validator
411 * @v iobuf I/O buffer
412 * @v meta Data transfer metadata
413 * @ret rc Return status code
415 static int validator_xfer_deliver ( struct validator
*validator
,
416 struct io_buffer
*iobuf
,
417 struct xfer_metadata
*meta
) {
420 /* Add data to buffer */
421 if ( ( rc
= xferbuf_deliver ( &validator
->buffer
, iob_disown ( iobuf
),
423 DBGC ( validator
, "VALIDATOR %p could not receive data: %s\n",
424 validator
, strerror ( rc
) );
425 validator_finished ( validator
, rc
);
432 /** Certificate validator data transfer interface operations */
433 static struct interface_operation validator_xfer_operations
[] = {
434 INTF_OP ( xfer_deliver
, struct validator
*, validator_xfer_deliver
),
435 INTF_OP ( intf_close
, struct validator
*, validator_xfer_close
),
438 /** Certificate validator data transfer interface descriptor */
439 static struct interface_descriptor validator_xfer_desc
=
440 INTF_DESC ( struct validator
, xfer
, validator_xfer_operations
);
442 /****************************************************************************
449 * Certificate validation process
451 * @v validator Certificate validator
453 static void validator_step ( struct validator
*validator
) {
454 struct x509_link
*link
;
455 struct x509_certificate
*cert
;
456 struct x509_certificate
*issuer
= NULL
;
457 struct x509_certificate
*last
;
461 /* Try validating chain. Try even if the chain is incomplete,
462 * since certificates may already have been validated
466 if ( ( rc
= x509_validate_chain ( validator
->chain
, now
, NULL
,
468 validator_finished ( validator
, 0 );
472 /* If there is a certificate that could be validated using
475 list_for_each_entry ( link
, &validator
->chain
->links
, list
) {
480 if ( ! issuer
->valid
)
482 /* The issuer is valid, but this certificate is not
483 * yet valid. If OCSP is applicable, start it.
485 if ( cert
->extensions
.auth_info
.ocsp
.uri
.len
&&
486 ( ! cert
->extensions
.auth_info
.ocsp
.good
) ) {
488 if ( ( rc
= validator_start_ocsp ( validator
, cert
,
490 validator_finished ( validator
, rc
);
495 /* Otherwise, this is a permanent failure */
496 validator_finished ( validator
, rc
);
500 /* If chain ends with a self-issued certificate, then there is
501 * nothing more to do.
503 last
= x509_last ( validator
->chain
);
504 if ( asn1_compare ( &last
->issuer
.raw
, &last
->subject
.raw
) == 0 ) {
505 validator_finished ( validator
, rc
);
509 /* Otherwise, try to download a suitable cross-signing
512 if ( ( rc
= validator_start_download ( validator
,
513 &last
->issuer
.raw
) ) != 0 ) {
514 validator_finished ( validator
, rc
);
519 /** Certificate validator process descriptor */
520 static struct process_descriptor validator_process_desc
=
521 PROC_DESC_ONCE ( struct validator
, process
, validator_step
);
523 /****************************************************************************
530 * Instantiate a certificate validator
532 * @v job Job control interface
533 * @v chain X.509 certificate chain
534 * @ret rc Return status code
536 int create_validator ( struct interface
*job
, struct x509_chain
*chain
) {
537 struct validator
*validator
;
546 /* Allocate and initialise structure */
547 validator
= zalloc ( sizeof ( *validator
) );
552 ref_init ( &validator
->refcnt
, validator_free
);
553 intf_init ( &validator
->job
, &validator_job_desc
,
554 &validator
->refcnt
);
555 intf_init ( &validator
->xfer
, &validator_xfer_desc
,
556 &validator
->refcnt
);
557 process_init ( &validator
->process
, &validator_process_desc
,
558 &validator
->refcnt
);
559 validator
->chain
= x509_chain_get ( chain
);
560 xferbuf_malloc_init ( &validator
->buffer
);
562 /* Attach parent interface, mortalise self, and return */
563 intf_plug_plug ( &validator
->job
, job
);
564 ref_put ( &validator
->refcnt
);
565 DBGC2 ( validator
, "VALIDATOR %p validating X509 chain %p\n",
566 validator
, validator
->chain
);
569 validator_finished ( validator
, rc
);
570 ref_put ( &validator
->refcnt
);