diff options
author | Michael Brown <mcb30@ipxe.org> | 2012-05-20 16:46:00 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2012-05-22 00:47:19 +0100 |
commit | 073331c2ee7cf332c5df32bf9c74819cada848b9 (patch) | |
tree | aff625cd293f551eff5a690f643b3698f01978a4 /src/net/validator.c | |
parent | 071171e807b26d6f16c880674b52a553f26ccd08 (diff) | |
download | ipxe-073331c2ee7cf332c5df32bf9c74819cada848b9.tar.gz |
[crypto] Automatically perform OCSP checks when applicable
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net/validator.c')
-rw-r--r-- | src/net/validator.c | 263 |
1 files changed, 195 insertions, 68 deletions
diff --git a/src/net/validator.c b/src/net/validator.c index 80fecea89..c4051d48f 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/dhcp.h> #include <ipxe/base64.h> #include <ipxe/crc32.h> +#include <ipxe/ocsp.h> #include <ipxe/validator.h> /** @file @@ -51,12 +52,19 @@ struct validator { struct interface job; /** Data transfer interface */ struct interface xfer; + /** Process */ struct process process; + /** X.509 certificate chain */ struct x509_chain *chain; + /** OCSP check */ + struct ocsp_check *ocsp; /** Data buffer */ struct xfer_buffer buffer; + /** Action to take upon completed transfer */ + int ( * done ) ( struct validator *validator, const void *data, + size_t len ); }; /** @@ -70,6 +78,7 @@ static void validator_free ( struct refcnt *refcnt ) { DBGC2 ( validator, "VALIDATOR %p freed\n", validator ); x509_chain_put ( validator->chain ); + ocsp_put ( validator->ocsp ); xferbuf_done ( &validator->buffer ); free ( validator ); } @@ -123,6 +132,89 @@ struct setting crosscert_setting __setting ( SETTING_CRYPTO ) = { static const char crosscert_default[] = "http://ca.ipxe.org/auto"; /** + * Append cross-signing certificates to certificate chain + * + * @v validator Certificate validator + * @v data Raw cross-signing certificate data + * @v len Length of raw data + * @ret rc Return status code + */ +static int validator_append ( struct validator *validator, + const void *data, size_t len ) { + struct asn1_cursor cursor; + struct x509_chain *certs; + struct x509_certificate *cert; + struct x509_certificate *last; + int rc; + + /* Allocate certificate list */ + certs = x509_alloc_chain(); + if ( ! certs ) { + rc = -ENOMEM; + goto err_alloc_certs; + } + + /* Initialise cursor */ + cursor.data = data; + cursor.len = len; + + /* Enter certificateSet */ + if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) { + DBGC ( validator, "VALIDATOR %p could not enter " + "certificateSet: %s\n", validator, strerror ( rc ) ); + goto err_certificateset; + } + + /* Add each certificate to list */ + while ( cursor.len ) { + + /* Add certificate to chain */ + if ( ( rc = x509_append_raw ( certs, cursor.data, + cursor.len ) ) != 0 ) { + DBGC ( validator, "VALIDATOR %p could not append " + "certificate: %s\n", + validator, strerror ( rc) ); + DBGC_HDA ( validator, 0, cursor.data, cursor.len ); + return rc; + } + cert = x509_last ( certs ); + DBGC ( validator, "VALIDATOR %p found certificate %s\n", + validator, cert->subject.name ); + + /* Move to next certificate */ + asn1_skip_any ( &cursor ); + } + + /* Append certificates to chain */ + last = x509_last ( validator->chain ); + if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) { + DBGC ( validator, "VALIDATOR %p could not append " + "certificates: %s\n", validator, strerror ( rc ) ); + goto err_auto_append; + } + + /* Check that at least one certificate has been added */ + if ( last == x509_last ( validator->chain ) ) { + DBGC ( validator, "VALIDATOR %p failed to append any " + "applicable certificates\n", validator ); + rc = -EACCES; + goto err_no_progress; + } + + /* Drop reference to certificate list */ + x509_chain_put ( certs ); + + return 0; + + err_no_progress: + err_auto_append: + err_certificateset: + x509_chain_put ( certs ); + err_alloc_certs: + return rc; +} + +/** * Start download of cross-signing certificate * * @v validator Certificate validator @@ -169,6 +261,9 @@ static int validator_start_download ( struct validator *validator, DBGC ( validator, "VALIDATOR %p downloading cross-signed certificate " "from %s\n", validator, uri_string ); + /* Set completion handler */ + validator->done = validator_append; + /* Open URI */ if ( ( rc = xfer_open_uri_string ( &validator->xfer, uri_string ) ) != 0 ) { @@ -188,87 +283,84 @@ static int validator_start_download ( struct validator *validator, return rc; } +/**************************************************************************** + * + * OCSP checks + * + */ + /** - * Append cross-signing certificates to certificate chain + * Validate OCSP response * * @v validator Certificate validator - * @v data Raw cross-signing certificate data + * @v data Raw OCSP response * @v len Length of raw data * @ret rc Return status code */ -static int validator_append ( struct validator *validator, - const void *data, size_t len ) { - struct asn1_cursor cursor; - struct x509_chain *certs; - struct x509_certificate *cert; - struct x509_certificate *last; +static int validator_ocsp_validate ( struct validator *validator, + const void *data, size_t len ) { + time_t now; int rc; - /* Allocate certificate list */ - certs = x509_alloc_chain(); - if ( ! certs ) { - rc = -ENOMEM; - goto err_alloc_certs; + /* Record OCSP response */ + if ( ( rc = ocsp_response ( validator->ocsp, data, len ) ) != 0 ) { + DBGC ( validator, "VALIDATOR %p could not record OCSP " + "response: %s\n", validator, strerror ( rc ) ); + return rc; } - /* Initialise cursor */ - cursor.data = data; - cursor.len = len; - - /* Enter certificateSet */ - if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not enter " - "certificateSet: %s\n", validator, strerror ( rc ) ); - goto err_certificateset; + /* Validate OCSP response */ + now = time ( NULL ); + if ( ( rc = ocsp_validate ( validator->ocsp, now ) ) != 0 ) { + DBGC ( validator, "VALIDATOR %p could not validate OCSP " + "response: %s\n", validator, strerror ( rc ) ); + return rc; } - /* Add each certificate to list */ - while ( cursor.len ) { + /* Drop reference to OCSP check */ + ocsp_put ( validator->ocsp ); + validator->ocsp = NULL; - /* Add certificate to chain */ - if ( ( rc = x509_append_raw ( certs, cursor.data, - cursor.len ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not append " - "certificate: %s\n", - validator, strerror ( rc) ); - DBGC_HDA ( validator, 0, cursor.data, cursor.len ); - return rc; - } - cert = x509_last ( certs ); - DBGC ( validator, "VALIDATOR %p found certificate %s\n", - validator, cert->subject.name ); + return 0; +} - /* Move to next certificate */ - asn1_skip_any ( &cursor ); - } +/** + * Start OCSP check + * + * @v validator Certificate validator + * @v cert Certificate to check + * @v issuer Issuing certificate + * @ret rc Return status code + */ +static int validator_start_ocsp ( struct validator *validator, + struct x509_certificate *cert, + struct x509_certificate *issuer ) { + const char *uri_string; + int rc; - /* Append certificates to chain */ - last = x509_last ( validator->chain ); - if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not append " - "certificates: %s\n", validator, strerror ( rc ) ); - goto err_auto_append; + /* Create OCSP check */ + assert ( validator->ocsp == NULL ); + if ( ( rc = ocsp_check ( cert, issuer, &validator->ocsp ) ) != 0 ) { + DBGC ( validator, "VALIDATOR %p could not create OCSP check: " + "%s\n", validator, strerror ( rc ) ); + return rc; } - /* Check that at least one certificate has been added */ - if ( last == x509_last ( validator->chain ) ) { - DBGC ( validator, "VALIDATOR %p failed to append any " - "applicable certificates\n", validator ); - rc = -EACCES; - goto err_no_progress; - } + /* Set completion handler */ + validator->done = validator_ocsp_validate; - /* Drop reference to certificate list */ - x509_chain_put ( certs ); + /* Open URI */ + uri_string = validator->ocsp->uri_string; + DBGC ( validator, "VALIDATOR %p performing OCSP check at %s\n", + validator, uri_string ); + if ( ( rc = xfer_open_uri_string ( &validator->xfer, + uri_string ) ) != 0 ) { + DBGC ( validator, "VALIDATOR %p could not open %s: %s\n", + validator, uri_string, strerror ( rc ) ); + return rc; + } return 0; - - err_no_progress: - err_auto_append: - err_certificateset: - x509_chain_put ( certs ); - err_alloc_certs: - return rc; } /**************************************************************************** @@ -290,14 +382,15 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { /* Check for errors */ if ( rc != 0 ) { - DBGC ( validator, "VALIDATOR %p download failed: %s\n", + DBGC ( validator, "VALIDATOR %p transfer failed: %s\n", validator, strerror ( rc ) ); - goto err_download; + goto err_transfer; } - DBGC2 ( validator, "VALIDATOR %p download complete\n", validator ); + DBGC2 ( validator, "VALIDATOR %p transfer complete\n", validator ); - /* Append downloaded certificates */ - if ( ( rc = validator_append ( validator, validator->buffer.data, + /* Process completed download */ + assert ( validator->done != NULL ); + if ( ( rc = validator->done ( validator, validator->buffer.data, validator->buffer.len ) ) != 0 ) goto err_append; @@ -310,7 +403,7 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { return; err_append: - err_download: + err_transfer: validator_finished ( validator, rc ); } @@ -361,7 +454,11 @@ static struct interface_descriptor validator_xfer_desc = * @v validator Certificate validator */ static void validator_step ( struct validator *validator ) { - struct x509_certificate *last = x509_last ( validator->chain ); + struct x509_link *link; + struct x509_link *previous; + struct x509_certificate *cert; + struct x509_certificate *issuer = NULL; + struct x509_certificate *last; time_t now; int rc; @@ -376,9 +473,39 @@ static void validator_step ( struct validator *validator ) { return; } + /* If there is a certificate that could be validated using + * OCSP, try it. + */ + list_for_each_entry ( link, &validator->chain->links, list ) { + cert = issuer; + issuer = link->cert; + previous = link; + if ( ! cert ) + continue; + if ( ! issuer->valid ) + continue; + /* The issuer is valid, but this certificate is not + * yet valid. If OCSP is applicable, start it. + */ + if ( cert->extensions.auth_info.ocsp.uri && + ( ! cert->extensions.auth_info.ocsp.good ) ) { + /* Start OCSP */ + if ( ( rc = validator_start_ocsp ( validator, cert, + issuer ) ) != 0 ) { + validator_finished ( validator, rc ); + return; + } + return; + } + /* Otherwise, this is a permanent failure */ + validator_finished ( validator, rc ); + return; + } + /* If chain ends with a self-issued certificate, then there is * nothing more to do. */ + last = x509_last ( validator->chain ); if ( asn1_compare ( &last->issuer.raw, &last->subject.raw ) == 0 ) { validator_finished ( validator, rc ); return; |