iPXE
Data Structures | Functions | Variables
validator.c File Reference

Certificate validator. More...

#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <ipxe/refcnt.h>
#include <ipxe/malloc.h>
#include <ipxe/interface.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/iobuf.h>
#include <ipxe/xferbuf.h>
#include <ipxe/process.h>
#include <ipxe/x509.h>
#include <ipxe/settings.h>
#include <ipxe/dhcp.h>
#include <ipxe/base64.h>
#include <ipxe/crc32.h>
#include <ipxe/ocsp.h>
#include <ipxe/validator.h>
#include <config/crypto.h>

Go to the source code of this file.

Data Structures

struct  validator
 A certificate validator. More...

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static void validator_free (struct refcnt *refcnt)
 Free certificate validator.
static void validator_finished (struct validator *validator, int rc)
 Mark certificate validation as finished.
struct setting crosscert_setting __setting (SETTING_CRYPTO, crosscert)
 Cross-signed certificate source setting.
static int validator_append (struct validator *validator, const void *data, size_t len)
 Append cross-signing certificates to certificate chain.
static int validator_start_download (struct validator *validator, const struct asn1_cursor *issuer)
 Start download of cross-signing certificate.
static int validator_ocsp_validate (struct validator *validator, const void *data, size_t len)
 Validate OCSP response.
static int validator_start_ocsp (struct validator *validator, struct x509_certificate *cert, struct x509_certificate *issuer)
 Start OCSP check.
static void validator_xfer_close (struct validator *validator, int rc)
 Close data transfer interface.
static int validator_xfer_deliver (struct validator *validator, struct io_buffer *iobuf, struct xfer_metadata *meta)
 Receive data.
static void validator_step (struct validator *validator)
 Certificate validation process.
int create_validator (struct interface *job, struct x509_chain *chain)
 Instantiate a certificate validator.

Variables

static struct interface_operation validator_job_operations []
 Certificate validator job control interface operations.
static struct interface_descriptor validator_job_desc
 Certificate validator job control interface descriptor.
static const char crosscert_default [] = CROSSCERT
 Default cross-signed certificate source.
static struct interface_operation validator_xfer_operations []
 Certificate validator data transfer interface operations.
static struct interface_descriptor validator_xfer_desc
 Certificate validator data transfer interface descriptor.
static struct process_descriptor validator_process_desc
 Certificate validator process descriptor.

Detailed Description

Certificate validator.

Definition in file validator.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static void validator_free ( struct refcnt refcnt) [static]

Free certificate validator.

Parameters:
refcntReference count

Definition at line 80 of file validator.c.

References validator::buffer, validator::chain, container_of, DBGC2, free, validator::ocsp, ocsp_put(), x509_chain_put(), and xferbuf_free().

Referenced by create_validator().

                                                     {
        struct validator *validator =
                container_of ( refcnt, struct validator, refcnt );

        DBGC2 ( validator, "VALIDATOR %p freed\n", validator );
        x509_chain_put ( validator->chain );
        ocsp_put ( validator->ocsp );
        xferbuf_free ( &validator->buffer );
        free ( validator );
}
static void validator_finished ( struct validator validator,
int  rc 
) [static]

Mark certificate validation as finished.

Parameters:
validatorCertificate validator
rcReason for finishing

Definition at line 97 of file validator.c.

References intf_shutdown(), validator::job, validator::process, process_del(), and validator::xfer.

Referenced by create_validator(), validator_step(), validator_xfer_close(), and validator_xfer_deliver().

                                                                       {

        /* Remove process */
        process_del ( &validator->process );

        /* Close all interfaces */
        intf_shutdown ( &validator->xfer, rc );
        intf_shutdown ( &validator->job, rc );
}
struct setting crosscert_setting __setting ( SETTING_CRYPTO  ,
crosscert   
) [read]

Cross-signed certificate source setting.

static int validator_append ( struct validator validator,
const void *  data,
size_t  len 
) [static]

Append cross-signing certificates to certificate chain.

Parameters:
validatorCertificate validator
dataRaw cross-signing certificate data
lenLength of raw data
Return values:
rcReturn status code

Definition at line 147 of file validator.c.

References asn1_enter(), ASN1_SET, asn1_skip_any(), validator::chain, asn1_cursor::data, data, DBGC, DBGC_HDA, EACCES, ENOMEM, last, asn1_cursor::len, len, rc, strerror(), x509_alloc_chain(), x509_append_raw(), x509_auto_append(), x509_chain_put(), x509_last(), and x509_name().

Referenced by validator_start_download().

                                                             {
        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, x509_name ( cert ) );

                /* 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;
}
static int validator_start_download ( struct validator validator,
const struct asn1_cursor issuer 
) [static]

Start download of cross-signing certificate.

Parameters:
validatorCertificate validator
issuerRequired issuer
Return values:
rcReturn status code

Definition at line 229 of file validator.c.

References base64_encode(), base64_encoded_len(), crc32_le(), crosscert_default, asn1_cursor::data, DBGC, validator::done, EINVAL, ENOMEM, fetch_string_setting_copy(), free, asn1_cursor::len, len, NULL, rc, snprintf(), strerror(), strlen(), validator_append(), validator::xfer, xfer_open_uri_string(), and zalloc().

Referenced by validator_step().

                                                                         {
        const char *crosscert;
        char *crosscert_copy;
        char *uri_string;
        size_t uri_string_len;
        uint32_t crc;
        int len;
        int rc;

        /* Determine cross-signed certificate source */
        fetch_string_setting_copy ( NULL, &crosscert_setting, &crosscert_copy );
        crosscert = ( crosscert_copy ? crosscert_copy : crosscert_default );
        if ( ! crosscert[0] ) {
                rc = -EINVAL;
                goto err_check_uri_string;
        }

        /* Allocate URI string */
        uri_string_len = ( strlen ( crosscert ) + 22 /* "/%08x.der?subject=" */
                           + base64_encoded_len ( issuer->len ) + 1 /* NUL */ );
        uri_string = zalloc ( uri_string_len );
        if ( ! uri_string ) {
                rc = -ENOMEM;
                goto err_alloc_uri_string;
        }

        /* Generate CRC32 */
        crc = crc32_le ( 0xffffffffUL, issuer->data, issuer->len );

        /* Generate URI string */
        len = snprintf ( uri_string, uri_string_len, "%s/%08x.der?subject=",
                         crosscert, crc );
        base64_encode ( issuer->data, issuer->len, ( uri_string + len ),
                        ( uri_string_len - len ) );
        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 ) {
                DBGC ( validator, "VALIDATOR %p could not open %s: %s\n",
                       validator, uri_string, strerror ( rc ) );
                goto err_open_uri_string;
        }

        /* Success */
        rc = 0;

 err_open_uri_string:
        free ( uri_string );
 err_alloc_uri_string:
 err_check_uri_string:
        free ( crosscert_copy );
        return rc;
}
static int validator_ocsp_validate ( struct validator validator,
const void *  data,
size_t  len 
) [static]

Validate OCSP response.

Parameters:
validatorCertificate validator
dataRaw OCSP response
lenLength of raw data
Return values:
rcReturn status code

Definition at line 303 of file validator.c.

References DBGC, NULL, validator::ocsp, ocsp_put(), ocsp_validate(), rc, strerror(), and time.

Referenced by validator_start_ocsp().

                                                                    {
        time_t now;
        int rc;

        /* 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;
        }

        /* 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;
        }

        /* Drop reference to OCSP check */
        ocsp_put ( validator->ocsp );
        validator->ocsp = NULL;

        return 0;
}
static int validator_start_ocsp ( struct validator validator,
struct x509_certificate cert,
struct x509_certificate issuer 
) [static]

Start OCSP check.

Parameters:
validatorCertificate validator
certCertificate to check
issuerIssuing certificate
Return values:
rcReturn status code

Definition at line 338 of file validator.c.

References assert, DBGC, validator::done, NULL, validator::ocsp, rc, strerror(), ocsp_check::uri_string, validator_ocsp_validate(), validator::xfer, and xfer_open_uri_string().

Referenced by validator_step().

                                                                    {
        const char *uri_string;
        int rc;

        /* 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;
        }

        /* Set completion handler */
        validator->done = validator_ocsp_validate;

        /* 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;
}
static void validator_xfer_close ( struct validator validator,
int  rc 
) [static]

Close data transfer interface.

Parameters:
validatorCertificate validator
rcReason for close

Definition at line 381 of file validator.c.

References assert, validator::buffer, xfer_buffer::data, DBGC, DBGC2, validator::done, intf_restart(), xfer_buffer::len, NULL, validator::process, process_add(), strerror(), validator_finished(), validator::xfer, and xferbuf_free().

                                                                         {

        /* Close data transfer interface */
        intf_restart ( &validator->xfer, rc );

        /* Check for errors */
        if ( rc != 0 ) {
                DBGC ( validator, "VALIDATOR %p transfer failed: %s\n",
                       validator, strerror ( rc ) );
                goto err_transfer;
        }
        DBGC2 ( validator, "VALIDATOR %p transfer complete\n", validator );

        /* Process completed download */
        assert ( validator->done != NULL );
        if ( ( rc = validator->done ( validator, validator->buffer.data,
                                       validator->buffer.len ) ) != 0 )
                goto err_append;

        /* Free downloaded data */
        xferbuf_free ( &validator->buffer );

        /* Resume validation process */
        process_add ( &validator->process );

        return;

 err_append:
 err_transfer:
        validator_finished ( validator, rc );
}
static int validator_xfer_deliver ( struct validator validator,
struct io_buffer iobuf,
struct xfer_metadata meta 
) [static]

Receive data.

Parameters:
validatorCertificate validator
iobufI/O buffer
metaData transfer metadata
Return values:
rcReturn status code

Definition at line 421 of file validator.c.

References validator::buffer, DBGC, iob_disown, rc, strerror(), validator_finished(), and xferbuf_deliver().

                                                                 {
        int rc;

        /* Add data to buffer */
        if ( ( rc = xferbuf_deliver ( &validator->buffer, iob_disown ( iobuf ),
                                      meta ) ) != 0 ) {
                DBGC ( validator, "VALIDATOR %p could not receive data: %s\n",
                       validator, strerror ( rc ) );
                validator_finished ( validator, rc );
                return rc;
        }

        return 0;
}
static void validator_step ( struct validator validator) [static]

Certificate validation process.

Parameters:
validatorCertificate validator

Definition at line 459 of file validator.c.

References asn1_compare(), x509_link::cert, validator::chain, x509_certificate::issuer, last, link, x509_chain::links, list_for_each_entry, NULL, ocsp_required(), x509_issuer::raw, x509_subject::raw, rc, x509_certificate::subject, time, validator_finished(), validator_start_download(), validator_start_ocsp(), x509_is_valid(), x509_last(), and x509_validate_chain().

                                                           {
        struct x509_link *link;
        struct x509_certificate *cert;
        struct x509_certificate *issuer = NULL;
        struct x509_certificate *last;
        time_t now;
        int rc;

        /* Try validating chain.  Try even if the chain is incomplete,
         * since certificates may already have been validated
         * previously.
         */
        now = time ( NULL );
        if ( ( rc = x509_validate_chain ( validator->chain, now, NULL,
                                          NULL ) ) == 0 ) {
                validator_finished ( validator, 0 );
                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;
                if ( ! cert )
                        continue;
                if ( ! x509_is_valid ( issuer ) )
                        continue;
                /* The issuer is valid, but this certificate is not
                 * yet valid.  If OCSP is applicable, start it.
                 */
                if ( ocsp_required ( cert ) ) {
                        /* 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;
        }

        /* Otherwise, try to download a suitable cross-signing
         * certificate.
         */
        if ( ( rc = validator_start_download ( validator,
                                               &last->issuer.raw ) ) != 0 ) {
                validator_finished ( validator, rc );
                return;
        }
}
int create_validator ( struct interface job,
struct x509_chain chain 
)

Instantiate a certificate validator.

Parameters:
jobJob control interface
chainX.509 certificate chain
Return values:
rcReturn status code

Definition at line 541 of file validator.c.

References validator::buffer, validator::chain, DBGC2, EINVAL, ENOMEM, intf_init(), intf_plug_plug(), validator::job, validator::process, process_init(), rc, ref_init, ref_put, validator::refcnt, validator_finished(), validator_free(), x509_chain_get(), validator::xfer, and zalloc().

Referenced by imgverify(), and tls_new_server_hello_done().

                                                                         {
        struct validator *validator;
        int rc;

        /* Sanity check */
        if ( ! chain ) {
                rc = -EINVAL;
                goto err_sanity;
        }

        /* Allocate and initialise structure */
        validator = zalloc ( sizeof ( *validator ) );
        if ( ! validator ) {
                rc = -ENOMEM;
                goto err_alloc;
        }
        ref_init ( &validator->refcnt, validator_free );
        intf_init ( &validator->job, &validator_job_desc,
                    &validator->refcnt );
        intf_init ( &validator->xfer, &validator_xfer_desc,
                    &validator->refcnt );
        process_init ( &validator->process, &validator_process_desc,
                       &validator->refcnt );
        validator->chain = x509_chain_get ( chain );
        xferbuf_malloc_init ( &validator->buffer );

        /* Attach parent interface, mortalise self, and return */
        intf_plug_plug ( &validator->job, job );
        ref_put ( &validator->refcnt );
        DBGC2 ( validator, "VALIDATOR %p validating X509 chain %p\n",
                validator, validator->chain );
        return 0;

        validator_finished ( validator, rc );
        ref_put ( &validator->refcnt );
 err_alloc:
 err_sanity:
        return rc;
}

Variable Documentation

Initial value:

Certificate validator job control interface operations.

Definition at line 114 of file validator.c.

Initial value:

Certificate validator job control interface descriptor.

Definition at line 119 of file validator.c.

const char crosscert_default[] = CROSSCERT [static]

Default cross-signed certificate source.

Definition at line 137 of file validator.c.

Referenced by validator_start_download().

Initial value:

Certificate validator data transfer interface operations.

Definition at line 439 of file validator.c.

Initial value:

Certificate validator data transfer interface descriptor.

Definition at line 445 of file validator.c.

Initial value:

Certificate validator process descriptor.

Definition at line 525 of file validator.c.