]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[crypto] Automatically perform OCSP checks when applicable
authorMichael Brown <mcb30@ipxe.org>
Sun, 20 May 2012 15:46:00 +0000 (16:46 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 21 May 2012 23:47:19 +0000 (00:47 +0100)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/net/validator.c

index 80fecea89ac39301e33cf68065a6168bab82603d..c4051d48f50366d043701d21601873c9e809d610 100644 (file)
@@ -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 );
 }
@@ -122,6 +131,89 @@ struct setting crosscert_setting __setting ( SETTING_CRYPTO ) = {
 /** Default cross-signed certificate source */
 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
  *
@@ -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;