iPXE
Defines | Functions | Variables
ocsp.c File Reference

Online Certificate Status Protocol. More...

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ipxe/asn1.h>
#include <ipxe/x509.h>
#include <ipxe/sha1.h>
#include <ipxe/base64.h>
#include <ipxe/uri.h>
#include <ipxe/ocsp.h>
#include <config/crypto.h>

Go to the source code of this file.

Defines

#define EACCES_CERT_STATUS   __einfo_error ( EINFO_EACCES_CERT_STATUS )
#define EINFO_EACCES_CERT_STATUS
#define EACCES_CERT_MISMATCH   __einfo_error ( EINFO_EACCES_CERT_MISMATCH )
#define EINFO_EACCES_CERT_MISMATCH
#define EACCES_NON_OCSP_SIGNING   __einfo_error ( EINFO_EACCES_NON_OCSP_SIGNING )
#define EINFO_EACCES_NON_OCSP_SIGNING
#define EACCES_STALE   __einfo_error ( EINFO_EACCES_STALE )
#define EINFO_EACCES_STALE
#define EACCES_NO_RESPONDER   __einfo_error ( EINFO_EACCES_NO_RESPONDER )
#define EINFO_EACCES_NO_RESPONDER
#define ENOTSUP_RESPONSE_TYPE   __einfo_error ( EINFO_ENOTSUP_RESPONSE_TYPE )
#define EINFO_ENOTSUP_RESPONSE_TYPE
#define ENOTSUP_RESPONDER_ID   __einfo_error ( EINFO_ENOTSUP_RESPONDER_ID )
#define EINFO_ENOTSUP_RESPONDER_ID
#define EPROTO_MALFORMED_REQUEST   __einfo_error ( EINFO_EPROTO_MALFORMED_REQUEST )
#define EINFO_EPROTO_MALFORMED_REQUEST
#define EPROTO_INTERNAL_ERROR   __einfo_error ( EINFO_EPROTO_INTERNAL_ERROR )
#define EINFO_EPROTO_INTERNAL_ERROR
#define EPROTO_TRY_LATER   __einfo_error ( EINFO_EPROTO_TRY_LATER )
#define EINFO_EPROTO_TRY_LATER
#define EPROTO_SIG_REQUIRED   __einfo_error ( EINFO_EPROTO_SIG_REQUIRED )
#define EINFO_EPROTO_SIG_REQUIRED
#define EPROTO_UNAUTHORIZED   __einfo_error ( EINFO_EPROTO_UNAUTHORIZED )
#define EINFO_EPROTO_UNAUTHORIZED
#define EPROTO_STATUS(status)
#define ocsp_digest_algorithm   sha1_algorithm
 OCSP digest algorithm.

Functions

 FILE_LICENCE (GPL2_OR_LATER)
static void ocsp_free (struct refcnt *refcnt)
 Free OCSP check.
static int ocsp_request (struct ocsp_check *ocsp)
 Build OCSP request.
static int ocsp_uri_string (struct ocsp_check *ocsp)
 Build OCSP URI string.
int ocsp_check (struct x509_certificate *cert, struct x509_certificate *issuer, struct ocsp_check **ocsp)
 Create OCSP check.
static int ocsp_parse_response_status (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP response status.
static int ocsp_parse_response_type (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP response type.
static int ocsp_compare_responder_name (struct ocsp_check *ocsp, struct x509_certificate *cert)
 Compare responder's certificate name.
static int ocsp_compare_responder_key_hash (struct ocsp_check *ocsp, struct x509_certificate *cert)
 Compare responder's certificate public key hash.
static int ocsp_parse_responder_id (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP responder ID.
static int ocsp_parse_cert_id (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP certificate ID.
static int ocsp_parse_responses (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP responses.
static int ocsp_parse_tbs_response_data (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP response data.
static int ocsp_parse_certs (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP certificates.
static int ocsp_parse_basic_response (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP basic response.
static int ocsp_parse_response_bytes (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP response bytes.
static int ocsp_parse_response (struct ocsp_check *ocsp, const struct asn1_cursor *raw)
 Parse OCSP response.
int ocsp_response (struct ocsp_check *ocsp, const void *data, size_t len)
 Receive OCSP response.
static int ocsp_check_signature (struct ocsp_check *ocsp, struct x509_certificate *signer)
 Check OCSP response signature.
int ocsp_validate (struct ocsp_check *ocsp, time_t time)
 Validate OCSP response.

Variables

static const uint8_t ocsp_algorithm_id []
 OCSP digest algorithm identifier.
static const uint8_t oid_basic_response_type [] = { ASN1_OID_OCSP_BASIC }
 OCSP basic response type.
static struct asn1_cursor oid_basic_response_type_cursor
 OCSP basic response type cursor.
static struct x509_root ocsp_root
 OCSP dummy root certificate store.

Detailed Description

Online Certificate Status Protocol.

Definition in file ocsp.c.


Define Documentation

Definition at line 42 of file ocsp.c.

Referenced by ocsp_parse_responses().

Value:
__einfo_uniqify ( EINFO_EACCES, 0x01,                           \
                          "Certificate status not good" )

Definition at line 44 of file ocsp.c.

Definition at line 47 of file ocsp.c.

Referenced by ocsp_parse_cert_id().

Value:
__einfo_uniqify ( EINFO_EACCES, 0x02,                           \
                          "Certificate ID mismatch" )

Definition at line 49 of file ocsp.c.

Definition at line 52 of file ocsp.c.

Referenced by ocsp_validate().

Value:
__einfo_uniqify ( EINFO_EACCES, 0x03,                           \
                          "Not an OCSP signing certificate" )

Definition at line 54 of file ocsp.c.

Definition at line 57 of file ocsp.c.

Referenced by ocsp_validate().

Value:
__einfo_uniqify ( EINFO_EACCES, 0x04,                           \
                          "Stale (or premature) OCSP repsonse" )

Definition at line 59 of file ocsp.c.

Definition at line 62 of file ocsp.c.

Referenced by ocsp_parse_certs().

Value:
__einfo_uniqify ( EINFO_EACCES, 0x05,                           \
                          "Missing OCSP responder certificate" )

Definition at line 64 of file ocsp.c.

Definition at line 67 of file ocsp.c.

Referenced by ocsp_parse_response_type().

Value:
__einfo_uniqify ( EINFO_ENOTSUP, 0x01,                          \
                          "Unsupported OCSP response type" )

Definition at line 69 of file ocsp.c.

Definition at line 72 of file ocsp.c.

Referenced by ocsp_parse_responder_id().

Value:
__einfo_uniqify ( EINFO_ENOTSUP, 0x02,                          \
                          "Unsupported OCSP responder ID" )

Definition at line 74 of file ocsp.c.

Definition at line 77 of file ocsp.c.

Value:
__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_MALFORMED_REQUEST,  \
                          "Illegal confirmation request" )

Definition at line 79 of file ocsp.c.

Definition at line 82 of file ocsp.c.

Value:
__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_INTERNAL_ERROR,     \
                          "Internal error in issuer" )

Definition at line 84 of file ocsp.c.

Definition at line 87 of file ocsp.c.

Value:

Definition at line 89 of file ocsp.c.

Definition at line 92 of file ocsp.c.

Value:
__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_SIG_REQUIRED,       \
                          "Must sign the request" )

Definition at line 94 of file ocsp.c.

Definition at line 97 of file ocsp.c.

Value:
__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_UNAUTHORIZED,       \
                          "Request unauthorized" )

Definition at line 99 of file ocsp.c.

#define EPROTO_STATUS (   status)

OCSP digest algorithm.

Definition at line 108 of file ocsp.c.

Referenced by ocsp_request().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER  )
static void ocsp_free ( struct refcnt refcnt) [static]

Free OCSP check.

Parameters:
refcntReference count

Definition at line 126 of file ocsp.c.

References ocsp_request::builder, ocsp_check::cert, container_of, asn1_builder::data, ocsp_response::data, free, ocsp_check::issuer, ocsp_check::request, ocsp_check::response, ocsp_response::signer, ocsp_check::uri_string, and x509_put().

Referenced by ocsp_check().

                                                {
        struct ocsp_check *ocsp =
                container_of ( refcnt, struct ocsp_check, refcnt );

        x509_put ( ocsp->cert );
        x509_put ( ocsp->issuer );
        free ( ocsp->uri_string );
        free ( ocsp->request.builder.data );
        free ( ocsp->response.data );
        x509_put ( ocsp->response.signer );
        free ( ocsp );
}
static int ocsp_request ( struct ocsp_check ocsp) [static]

Build OCSP request.

Parameters:
ocspOCSP check
Return values:
rcReturn status code

Definition at line 145 of file ocsp.c.

References asn1_enter(), ASN1_OCTET_STRING, asn1_prepend(), asn1_prepend_raw(), ASN1_SEQUENCE, asn1_wrap(), ocsp_request::builder, ocsp_check::cert, ocsp_request::cert_id, digest_algorithm::ctxsize, asn1_cursor::data, asn1_builder::data, asn1_bit_string::data, DBGC, DBGC2, DBGC2_HDA, digest, digest_final(), digest_init(), digest_update(), digest_algorithm::digestsize, ocsp_check::issuer, x509_certificate::issuer, asn1_cursor::len, asn1_builder::len, asn1_bit_string::len, ocsp_algorithm_id, ocsp_digest_algorithm, x509_subject::public_key, x509_serial::raw, x509_issuer::raw, x509_public_key::raw_bits, rc, ocsp_check::request, x509_certificate::serial, strerror(), x509_certificate::subject, and x509_name().

                                                    {
        struct digest_algorithm *digest = &ocsp_digest_algorithm;
        struct asn1_builder *builder = &ocsp->request.builder;
        struct asn1_cursor *cert_id = &ocsp->request.cert_id;
        uint8_t digest_ctx[digest->ctxsize];
        uint8_t name_digest[digest->digestsize];
        uint8_t pubkey_digest[digest->digestsize];
        int rc;

        /* Generate digests */
        digest_init ( digest, digest_ctx );
        digest_update ( digest, digest_ctx, ocsp->cert->issuer.raw.data,
                        ocsp->cert->issuer.raw.len );
        digest_final ( digest, digest_ctx, name_digest );
        digest_init ( digest, digest_ctx );
        digest_update ( digest, digest_ctx,
                        ocsp->issuer->subject.public_key.raw_bits.data,
                        ocsp->issuer->subject.public_key.raw_bits.len );
        digest_final ( digest, digest_ctx, pubkey_digest );

        /* Construct request */
        if ( ( rc = ( asn1_prepend_raw ( builder, ocsp->cert->serial.raw.data,
                                         ocsp->cert->serial.raw.len ),
                      asn1_prepend ( builder, ASN1_OCTET_STRING,
                                     pubkey_digest, sizeof ( pubkey_digest ) ),
                      asn1_prepend ( builder, ASN1_OCTET_STRING,
                                     name_digest, sizeof ( name_digest ) ),
                      asn1_prepend ( builder, ASN1_SEQUENCE,
                                     ocsp_algorithm_id,
                                     sizeof ( ocsp_algorithm_id ) ),
                      asn1_wrap ( builder, ASN1_SEQUENCE ),
                      asn1_wrap ( builder, ASN1_SEQUENCE ),
                      asn1_wrap ( builder, ASN1_SEQUENCE ),
                      asn1_wrap ( builder, ASN1_SEQUENCE ),
                      asn1_wrap ( builder, ASN1_SEQUENCE ) ) ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" could not build request: %s\n",
                       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
                return rc;
        }
        DBGC2 ( ocsp, "OCSP %p \"%s\" request is:\n",
                ocsp, x509_name ( ocsp->cert ) );
        DBGC2_HDA ( ocsp, 0, builder->data, builder->len );

        /* Parse certificate ID for comparison with response */
        cert_id->data = builder->data;
        cert_id->len = builder->len;
        if ( ( rc = ( asn1_enter ( cert_id, ASN1_SEQUENCE ),
                      asn1_enter ( cert_id, ASN1_SEQUENCE ),
                      asn1_enter ( cert_id, ASN1_SEQUENCE ),
                      asn1_enter ( cert_id, ASN1_SEQUENCE ) ) ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" could not locate certID: %s\n",
                       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
                return rc;
        }

        return 0;
}
static int ocsp_uri_string ( struct ocsp_check ocsp) [static]

Build OCSP URI string.

Parameters:
ocspOCSP check
Return values:
rcReturn status code

Definition at line 209 of file ocsp.c.

References x509_extensions::auth_info, base64, base64_encode(), base64_encoded_len(), ocsp_request::builder, ocsp_check::cert, asn1_cursor::data, asn1_builder::data, DBGC, DBGC2, ENOMEM, ENOTTY, x509_certificate::extensions, free, asn1_cursor::len, asn1_builder::len, len, malloc(), memcpy(), NULL, x509_authority_info_access::ocsp, rc, ocsp_check::request, x509_ocsp_responder::uri, uri_encode(), URI_PATH, ocsp_check::uri_string, x509_name(), and zalloc().

Referenced by ocsp_check().

                                                       {
        struct x509_ocsp_responder *responder =
                &ocsp->cert->extensions.auth_info.ocsp;
        char *base64;
        char *sep;
        size_t base64_len;
        size_t uri_len;
        size_t len;
        int rc;

        /* Sanity check */
        if ( ! responder->uri.len ) {
                DBGC ( ocsp, "OCSP %p \"%s\" has no OCSP URI\n",
                       ocsp, x509_name ( ocsp->cert ) );
                rc = -ENOTTY;
                goto err_no_uri;
        }

        /* Calculate base64-encoded request length */
        base64_len = ( base64_encoded_len ( ocsp->request.builder.len )
                       + 1 /* NUL */ );

        /* Allocate and construct the base64-encoded request */
        base64 = malloc ( base64_len );
        if ( ! base64 ) {
                rc = -ENOMEM;
                goto err_alloc_base64;
        }
        base64_encode ( ocsp->request.builder.data, ocsp->request.builder.len,
                        base64, base64_len );

        /* Calculate URI-encoded base64-encoded request length */
        uri_len = ( uri_encode ( URI_PATH, base64, ( base64_len - 1 /* NUL */ ),
                                 NULL, 0 ) + 1 /* NUL */ );

        /* Allocate and construct the URI string */
        len = ( responder->uri.len + 1 /* possible "/" */ + uri_len );
        ocsp->uri_string = zalloc ( len );
        if ( ! ocsp->uri_string ) {
                rc = -ENOMEM;
                goto err_alloc_uri;
        }
        memcpy ( ocsp->uri_string, responder->uri.data, responder->uri.len );
        sep = &ocsp->uri_string[ responder->uri.len - 1 ];
        if ( *sep != '/' )
                *(++sep) = '/';
        uri_encode ( URI_PATH, base64, base64_len, ( sep + 1 ), uri_len );
        DBGC2 ( ocsp, "OCSP %p \"%s\" URI is %s\n",
                ocsp, x509_name ( ocsp->cert ), ocsp->uri_string );

        /* Success */
        rc = 0;

 err_alloc_uri:
        free ( base64 );
 err_alloc_base64:
 err_no_uri:
        return rc;
}
int ocsp_check ( struct x509_certificate cert,
struct x509_certificate issuer,
struct ocsp_check **  ocsp 
)

Create OCSP check.

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

Definition at line 277 of file ocsp.c.

References assert, ENOMEM, NULL, ocsp_free(), ocsp_put(), ocsp_uri_string(), rc, ref_init, x509_get(), x509_is_valid(), and zalloc().

                                            {
        int rc;

        /* Sanity checks */
        assert ( cert != NULL );
        assert ( issuer != NULL );
        assert ( x509_is_valid ( issuer ) );

        /* Allocate and initialise check */
        *ocsp = zalloc ( sizeof ( **ocsp ) );
        if ( ! *ocsp ) {
                rc = -ENOMEM;
                goto err_alloc;
        }
        ref_init ( &(*ocsp)->refcnt, ocsp_free );
        (*ocsp)->cert = x509_get ( cert );
        (*ocsp)->issuer = x509_get ( issuer );

        /* Build request */
        if ( ( rc = ocsp_request ( *ocsp ) ) != 0 )
                goto err_request;

        /* Build URI string */
        if ( ( rc = ocsp_uri_string ( *ocsp ) ) != 0 )
                goto err_uri_string;

        return 0;

 err_uri_string:
 err_request:
        ocsp_put ( *ocsp );
 err_alloc:
        *ocsp = NULL;
        return rc;
}
static int ocsp_parse_response_status ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP response status.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 322 of file ocsp.c.

References asn1_enter(), ASN1_ENUMERATED, ocsp_check::cert, asn1_cursor::data, DBGC, DBGC_HDA, EINVAL, EPROTO_STATUS, asn1_cursor::len, memcpy(), OCSP_STATUS_SUCCESSFUL, rc, status, strerror(), and x509_name().

Referenced by ocsp_parse_response().

                                                                        {
        struct asn1_cursor cursor;
        uint8_t status;
        int rc;

        /* Enter responseStatus */
        memcpy ( &cursor, raw, sizeof ( cursor ) );
        if ( ( rc = asn1_enter ( &cursor, ASN1_ENUMERATED ) ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" could not locate responseStatus: "
                       "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
                return rc;
        }

        /* Extract response status */
        if ( cursor.len != sizeof ( status ) ) {
                DBGC ( ocsp, "OCSP %p \"%s\" invalid status:\n",
                       ocsp, x509_name ( ocsp->cert ) );
                DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
                return -EINVAL;
        }
        memcpy ( &status, cursor.data, sizeof ( status ) );

        /* Check response status */
        if ( status != OCSP_STATUS_SUCCESSFUL ) {
                DBGC ( ocsp, "OCSP %p \"%s\" response status %d\n",
                       ocsp, x509_name ( ocsp->cert ), status );
                return EPROTO_STATUS ( status );
        }

        return 0;
}
static int ocsp_parse_response_type ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP response type.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 362 of file ocsp.c.

References asn1_compare(), asn1_enter(), ASN1_OID, ocsp_check::cert, asn1_cursor::data, DBGC, DBGC_HDA, ENOTSUP_RESPONSE_TYPE, asn1_cursor::len, memcpy(), and x509_name().

Referenced by ocsp_parse_response_bytes().

                                                                      {
        struct asn1_cursor cursor;

        /* Enter responseType */
        memcpy ( &cursor, raw, sizeof ( cursor ) );
        asn1_enter ( &cursor, ASN1_OID );

        /* Check responseType is "basic" */
        if ( asn1_compare ( &oid_basic_response_type_cursor, &cursor ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" response type not supported:\n",
                       ocsp, x509_name ( ocsp->cert ) );
                DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
                return -ENOTSUP_RESPONSE_TYPE;
        }

        return 0;
}
static int ocsp_compare_responder_name ( struct ocsp_check ocsp,
struct x509_certificate cert 
) [static]

Compare responder's certificate name.

Parameters:
ocspOCSP check
certCertificate
Return values:
differenceDifference as returned by memcmp()

Definition at line 388 of file ocsp.c.

References asn1_compare(), ocsp_responder::id, x509_subject::raw, ocsp_response::responder, ocsp_check::response, and x509_certificate::subject.

Referenced by ocsp_parse_responder_id().

                                                                         {
        struct ocsp_responder *responder = &ocsp->response.responder;

        /* Compare responder ID with certificate's subject */
        return asn1_compare ( &responder->id, &cert->subject.raw );
}
static int ocsp_compare_responder_key_hash ( struct ocsp_check ocsp,
struct x509_certificate cert 
) [static]

Compare responder's certificate public key hash.

Parameters:
ocspOCSP check
certCertificate
Return values:
differenceDifference as returned by memcmp()

Definition at line 403 of file ocsp.c.

References asn1_enter(), ASN1_OCTET_STRING, ctx, asn1_cursor::data, asn1_bit_string::data, digest, digest_final(), digest_init(), digest_update(), ocsp_responder::id, asn1_cursor::len, asn1_bit_string::len, memcmp(), memcpy(), x509_subject::public_key, x509_public_key::raw_bits, ocsp_response::responder, ocsp_check::response, sha1_algorithm, SHA1_CTX_SIZE, SHA1_DIGEST_SIZE, and x509_certificate::subject.

Referenced by ocsp_parse_responder_id().

                                                                             {
        struct ocsp_responder *responder = &ocsp->response.responder;
        struct asn1_cursor key_hash;
        uint8_t ctx[SHA1_CTX_SIZE];
        uint8_t digest[SHA1_DIGEST_SIZE];
        int difference;

        /* Enter responder key hash */
        memcpy ( &key_hash, &responder->id, sizeof ( key_hash ) );
        asn1_enter ( &key_hash, ASN1_OCTET_STRING );

        /* Sanity check */
        difference = ( sizeof ( digest ) - key_hash.len );
        if ( difference )
                return difference;

        /* Generate SHA1 hash of certificate's public key */
        digest_init ( &sha1_algorithm, ctx );
        digest_update ( &sha1_algorithm, ctx,
                        cert->subject.public_key.raw_bits.data,
                        cert->subject.public_key.raw_bits.len );
        digest_final ( &sha1_algorithm, ctx, digest );

        /* Compare responder key hash with hash of certificate's public key */
        return memcmp ( digest, key_hash.data, sizeof ( digest ) );
}
static int ocsp_parse_responder_id ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP responder ID.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 438 of file ocsp.c.

References asn1_enter_any(), ASN1_EXPLICIT_TAG, asn1_type(), ocsp_check::cert, ocsp_responder::compare, DBGC, DBGC2, ENOTSUP_RESPONDER_ID, ocsp_responder::id, memcpy(), ocsp_compare_responder_key_hash(), ocsp_compare_responder_name(), ocsp_response::responder, ocsp_check::response, type, and x509_name().

Referenced by ocsp_parse_tbs_response_data().

                                                                     {
        struct ocsp_responder *responder = &ocsp->response.responder;
        struct asn1_cursor *responder_id = &responder->id;
        unsigned int type;

        /* Enter responder ID */
        memcpy ( responder_id, raw, sizeof ( *responder_id ) );
        type = asn1_type ( responder_id );
        asn1_enter_any ( responder_id );

        /* Identify responder ID type */
        switch ( type ) {
        case ASN1_EXPLICIT_TAG ( 1 ) :
                DBGC2 ( ocsp, "OCSP %p \"%s\" responder identified by name\n",
                        ocsp, x509_name ( ocsp->cert ) );
                responder->compare = ocsp_compare_responder_name;
                return 0;
        case ASN1_EXPLICIT_TAG ( 2 ) :
                DBGC2 ( ocsp, "OCSP %p \"%s\" responder identified by key "
                        "hash\n", ocsp, x509_name ( ocsp->cert ) );
                responder->compare = ocsp_compare_responder_key_hash;
                return 0;
        default:
                DBGC ( ocsp, "OCSP %p \"%s\" unsupported responder ID type "
                       "%d\n", ocsp, x509_name ( ocsp->cert ), type );
                return -ENOTSUP_RESPONDER_ID;
        }
}
static int ocsp_parse_cert_id ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP certificate ID.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 475 of file ocsp.c.

References asn1_compare(), asn1_shrink_any(), ocsp_check::cert, ocsp_request::cert_id, asn1_cursor::data, DBGC, DBGC_HDA, EACCES_CERT_MISMATCH, asn1_cursor::len, memcpy(), ocsp_check::request, and x509_name().

Referenced by ocsp_parse_responses().

                                                                {
        struct asn1_cursor cursor;

        /* Check certID matches request */
        memcpy ( &cursor, raw, sizeof ( cursor ) );
        asn1_shrink_any ( &cursor );
        if ( asn1_compare ( &cursor, &ocsp->request.cert_id ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" certID mismatch:\n",
                       ocsp, x509_name ( ocsp->cert ) );
                DBGC_HDA ( ocsp, 0, ocsp->request.cert_id.data,
                           ocsp->request.cert_id.len );
                DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
                return -EACCES_CERT_MISMATCH;
        }

        return 0;
}
static int ocsp_parse_responses ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP responses.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 501 of file ocsp.c.

References asn1_enter(), ASN1_EXPLICIT_TAG, asn1_generalized_time(), ASN1_IMPLICIT_TAG, ASN1_SEQUENCE, asn1_skip_any(), asn1_type(), ocsp_check::cert, asn1_cursor::data, DBGC, DBGC2, DBGC_HDA, EACCES_CERT_STATUS, asn1_cursor::len, memcpy(), ocsp_response::next_update, NULL, ocsp_parse_cert_id(), rc, ocsp_check::response, strerror(), ocsp_response::this_update, time, and x509_name().

Referenced by ocsp_parse_tbs_response_data().

                                                                  {
        struct ocsp_response *response = &ocsp->response;
        struct asn1_cursor cursor;
        int rc;

        /* Enter responses */
        memcpy ( &cursor, raw, sizeof ( cursor ) );
        asn1_enter ( &cursor, ASN1_SEQUENCE );

        /* Enter first singleResponse */
        asn1_enter ( &cursor, ASN1_SEQUENCE );

        /* Parse certID */
        if ( ( rc = ocsp_parse_cert_id ( ocsp, &cursor ) ) != 0 )
                return rc;
        asn1_skip_any ( &cursor );

        /* Check certStatus */
        if ( asn1_type ( &cursor ) != ASN1_IMPLICIT_TAG ( 0 ) ) {
                DBGC ( ocsp, "OCSP %p \"%s\" non-good certStatus:\n",
                       ocsp, x509_name ( ocsp->cert ) );
                DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
                return -EACCES_CERT_STATUS;
        }
        asn1_skip_any ( &cursor );

        /* Parse thisUpdate */
        if ( ( rc = asn1_generalized_time ( &cursor,
                                            &response->this_update ) ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" could not parse thisUpdate: %s\n",
                       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
                return rc;
        }
        DBGC2 ( ocsp, "OCSP %p \"%s\" this update was at time %lld\n",
                ocsp, x509_name ( ocsp->cert ), response->this_update );
        asn1_skip_any ( &cursor );

        /* Parse nextUpdate, if present */
        if ( asn1_type ( &cursor ) == ASN1_EXPLICIT_TAG ( 0 ) ) {
                asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
                if ( ( rc = asn1_generalized_time ( &cursor,
                                             &response->next_update ) ) != 0 ) {
                        DBGC ( ocsp, "OCSP %p \"%s\" could not parse "
                               "nextUpdate: %s\n", ocsp,
                               x509_name ( ocsp->cert ), strerror ( rc ) );
                        return rc;
                }
                DBGC2 ( ocsp, "OCSP %p \"%s\" next update is at time %lld\n",
                        ocsp, x509_name ( ocsp->cert ), response->next_update );
        } else {
                /* If no nextUpdate is present, this indicates that
                 * "newer revocation information is available all the
                 * time".  Actually, this indicates that there is no
                 * point to performing the OCSP check, since an
                 * attacker could replay the response at any future
                 * time and it would still be valid.
                 */
                DBGC ( ocsp, "OCSP %p \"%s\" responder is a moron\n",
                       ocsp, x509_name ( ocsp->cert ) );
                response->next_update = time ( NULL );
        }

        return 0;
}
static int ocsp_parse_tbs_response_data ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP response data.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 574 of file ocsp.c.

References asn1_enter(), ASN1_EXPLICIT_TAG, ASN1_SEQUENCE, asn1_shrink_any(), asn1_skip_any(), asn1_skip_if_exists(), memcpy(), ocsp_parse_responder_id(), ocsp_parse_responses(), rc, ocsp_check::response, and ocsp_response::tbs.

Referenced by ocsp_parse_basic_response().

                                                                          {
        struct ocsp_response *response = &ocsp->response;
        struct asn1_cursor cursor;
        int rc;

        /* Record raw tbsResponseData */
        memcpy ( &cursor, raw, sizeof ( cursor ) );
        asn1_shrink_any ( &cursor );
        memcpy ( &response->tbs, &cursor, sizeof ( response->tbs ) );

        /* Enter tbsResponseData */
        asn1_enter ( &cursor, ASN1_SEQUENCE );

        /* Skip version, if present */
        asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );

        /* Parse responderID */
        if ( ( rc = ocsp_parse_responder_id ( ocsp, &cursor ) ) != 0 )
                return rc;
        asn1_skip_any ( &cursor );

        /* Skip producedAt */
        asn1_skip_any ( &cursor );

        /* Parse responses */
        if ( ( rc = ocsp_parse_responses ( ocsp, &cursor ) ) != 0 )
                return rc;

        return 0;
}
static int ocsp_parse_certs ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP certificates.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 613 of file ocsp.c.

References asn1_enter(), ASN1_EXPLICIT_TAG, ASN1_SEQUENCE, asn1_skip_any(), ocsp_check::cert, ocsp_responder::compare, asn1_cursor::data, DBGC, DBGC2, DBGC_HDA, EACCES_NO_RESPONDER, asn1_cursor::len, memcpy(), rc, ocsp_response::responder, ocsp_check::response, ocsp_response::signer, strerror(), x509_name(), and x509_put().

Referenced by ocsp_parse_basic_response().

                                                              {
        struct ocsp_response *response = &ocsp->response;
        struct asn1_cursor cursor;
        struct x509_certificate *cert;
        int rc;

        /* Enter certs */
        memcpy ( &cursor, raw, sizeof ( cursor ) );
        asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
        asn1_enter ( &cursor, ASN1_SEQUENCE );

        /* Parse certificate, if present.  The data structure permits
         * multiple certificates, but the protocol requires that the
         * OCSP signing certificate must either be the issuer itself,
         * or must be directly issued by the issuer (see RFC2560
         * section 4.2.2.2 "Authorized Responders").  We therefore
         * need to identify only the single certificate matching the
         * Responder ID.
         */
        while ( cursor.len ) {

                /* Parse certificate */
                if ( ( rc = x509_certificate ( cursor.data, cursor.len,
                                               &cert ) ) != 0 ) {
                        DBGC ( ocsp, "OCSP %p \"%s\" could not parse "
                               "certificate: %s\n", ocsp,
                               x509_name ( ocsp->cert ), strerror ( rc ) );
                        DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
                        return rc;
                }

                /* Use if this certificate matches the responder ID */
                if ( response->responder.compare ( ocsp, cert ) == 0 ) {
                        response->signer = cert;
                        DBGC2 ( ocsp, "OCSP %p \"%s\" response is signed by ",
                                ocsp, x509_name ( ocsp->cert ) );
                        DBGC2 ( ocsp, "\"%s\"\n",
                                x509_name ( response->signer ) );
                        return 0;
                }

                /* Otherwise, discard this certificate */
                x509_put ( cert );
                asn1_skip_any ( &cursor );
        }

        DBGC ( ocsp, "OCSP %p \"%s\" missing responder certificate\n",
               ocsp, x509_name ( ocsp->cert ) );
        return -EACCES_NO_RESPONDER;
}
static int ocsp_parse_basic_response ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP basic response.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 672 of file ocsp.c.

References ocsp_response::algorithm, algorithm, asn1_enter(), ASN1_EXPLICIT_TAG, asn1_integral_bit_string(), ASN1_SEQUENCE, asn1_signature_algorithm(), asn1_skip_any(), asn1_type(), ocsp_check::cert, DBGC, DBGC2, memcpy(), ocsp_parse_certs(), ocsp_parse_tbs_response_data(), rc, ocsp_check::response, ocsp_response::signature, signature, strerror(), and x509_name().

Referenced by ocsp_parse_response_bytes().

                                                                       {
        struct ocsp_response *response = &ocsp->response;
        struct asn1_algorithm **algorithm = &response->algorithm;
        struct asn1_bit_string *signature = &response->signature;
        struct asn1_cursor cursor;
        int rc;

        /* Enter BasicOCSPResponse */
        memcpy ( &cursor, raw, sizeof ( cursor ) );
        asn1_enter ( &cursor, ASN1_SEQUENCE );

        /* Parse tbsResponseData */
        if ( ( rc = ocsp_parse_tbs_response_data ( ocsp, &cursor ) ) != 0 )
                return rc;
        asn1_skip_any ( &cursor );

        /* Parse signatureAlgorithm */
        if ( ( rc = asn1_signature_algorithm ( &cursor, algorithm ) ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" cannot parse signature "
                       "algorithm: %s\n",
                       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
                return rc;
        }
        DBGC2 ( ocsp, "OCSP %p \"%s\" signature algorithm is %s\n",
                ocsp, x509_name ( ocsp->cert ), (*algorithm)->name );
        asn1_skip_any ( &cursor );

        /* Parse signature */
        if ( ( rc = asn1_integral_bit_string ( &cursor, signature ) ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" cannot parse signature: %s\n",
                       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
                return rc;
        }
        asn1_skip_any ( &cursor );

        /* Parse certs, if present */
        if ( ( asn1_type ( &cursor ) == ASN1_EXPLICIT_TAG ( 0 ) ) &&
             ( ( rc = ocsp_parse_certs ( ocsp, &cursor ) ) != 0 ) )
                return rc;

        return 0;
}
static int ocsp_parse_response_bytes ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP response bytes.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 723 of file ocsp.c.

References asn1_enter(), ASN1_EXPLICIT_TAG, ASN1_OCTET_STRING, ASN1_SEQUENCE, asn1_skip_any(), memcpy(), ocsp_parse_basic_response(), ocsp_parse_response_type(), and rc.

Referenced by ocsp_parse_response().

                                                                       {
        struct asn1_cursor cursor;
        int rc;

        /* Enter responseBytes */
        memcpy ( &cursor, raw, sizeof ( cursor ) );
        asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
        asn1_enter ( &cursor, ASN1_SEQUENCE );

        /* Parse responseType */
        if ( ( rc = ocsp_parse_response_type ( ocsp, &cursor ) ) != 0 )
                return rc;
        asn1_skip_any ( &cursor );

        /* Enter response */
        asn1_enter ( &cursor, ASN1_OCTET_STRING );

        /* Parse response */
        if ( ( rc = ocsp_parse_basic_response ( ocsp, &cursor ) ) != 0 )
                return rc;

        return 0;
}
static int ocsp_parse_response ( struct ocsp_check ocsp,
const struct asn1_cursor raw 
) [static]

Parse OCSP response.

Parameters:
ocspOCSP check
rawASN.1 cursor
Return values:
rcReturn status code

Definition at line 755 of file ocsp.c.

References asn1_enter(), ASN1_SEQUENCE, asn1_skip_any(), memcpy(), ocsp_parse_response_bytes(), ocsp_parse_response_status(), and rc.

Referenced by ocsp_response().

                                                                 {
        struct asn1_cursor cursor;
        int rc;

        /* Enter OCSPResponse */
        memcpy ( &cursor, raw, sizeof ( cursor ) );
        asn1_enter ( &cursor, ASN1_SEQUENCE );

        /* Parse responseStatus */
        if ( ( rc = ocsp_parse_response_status ( ocsp, &cursor ) ) != 0 )
                return rc;
        asn1_skip_any ( &cursor );

        /* Parse responseBytes */
        if ( ( rc = ocsp_parse_response_bytes ( ocsp, &cursor ) ) != 0 )
                return rc;

        return 0;
}
int ocsp_response ( struct ocsp_check ocsp,
const void *  data,
size_t  len 
)

Receive OCSP response.

Parameters:
ocspOCSP check
dataResponse data
lenLength of response data
Return values:
rcReturn status code

Definition at line 784 of file ocsp.c.

References asn1_cursor::data, ocsp_response::data, ENOMEM, free, asn1_cursor::len, len, malloc(), memcpy(), NULL, ocsp_parse_response(), rc, ocsp_check::response, ocsp_response::signer, and x509_put().

                                                                            {
        struct ocsp_response *response = &ocsp->response;
        struct asn1_cursor cursor;
        int rc;

        /* Duplicate data */
        x509_put ( response->signer );
        response->signer = NULL;
        free ( response->data );
        response->data = malloc ( len );
        if ( ! response->data )
                return -ENOMEM;
        memcpy ( response->data, data, len );
        cursor.data = response->data;
        cursor.len = len;

        /* Parse response */
        if ( ( rc = ocsp_parse_response ( ocsp, &cursor ) ) != 0 )
                return rc;

        return 0;
}
static int ocsp_check_signature ( struct ocsp_check ocsp,
struct x509_certificate signer 
) [static]

Check OCSP response signature.

Parameters:
ocspOCSP check
signerSigning certificate
Return values:
rcReturn status code

Definition at line 826 of file ocsp.c.

References ocsp_response::algorithm, ocsp_check::cert, digest_algorithm::ctxsize, pubkey_algorithm::ctxsize, asn1_cursor::data, asn1_bit_string::data, DBGC, DBGC2, digest, asn1_algorithm::digest, digest_final(), digest_init(), digest_update(), digest_algorithm::digestsize, asn1_cursor::len, asn1_bit_string::len, asn1_algorithm::pubkey, pubkey_final(), pubkey_init(), pubkey_verify(), x509_subject::public_key, x509_public_key::raw, rc, ocsp_check::response, ocsp_response::signature, strerror(), x509_certificate::subject, ocsp_response::tbs, and x509_name().

Referenced by ocsp_validate().

                                                                    {
        struct ocsp_response *response = &ocsp->response;
        struct digest_algorithm *digest = response->algorithm->digest;
        struct pubkey_algorithm *pubkey = response->algorithm->pubkey;
        struct x509_public_key *public_key = &signer->subject.public_key;
        uint8_t digest_ctx[ digest->ctxsize ];
        uint8_t digest_out[ digest->digestsize ];
        uint8_t pubkey_ctx[ pubkey->ctxsize ];
        int rc;

        /* Generate digest */
        digest_init ( digest, digest_ctx );
        digest_update ( digest, digest_ctx, response->tbs.data,
                        response->tbs.len );
        digest_final ( digest, digest_ctx, digest_out );

        /* Initialise public-key algorithm */
        if ( ( rc = pubkey_init ( pubkey, pubkey_ctx, public_key->raw.data,
                                  public_key->raw.len ) ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" could not initialise public key: "
                       "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
                goto err_init;
        }

        /* Verify digest */
        if ( ( rc = pubkey_verify ( pubkey, pubkey_ctx, digest, digest_out,
                                    response->signature.data,
                                    response->signature.len ) ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" signature verification failed: "
                       "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
                goto err_verify;
        }

        DBGC2 ( ocsp, "OCSP %p \"%s\" signature is correct\n",
                ocsp, x509_name ( ocsp->cert ) );

 err_verify:
        pubkey_final ( pubkey, pubkey_ctx );
 err_init:
        return rc;
}
int ocsp_validate ( struct ocsp_check ocsp,
time_t  time 
)

Validate OCSP response.

Parameters:
ocspOCSP check
timeTime at which to validate response
Return values:
rcReturn status code

Definition at line 876 of file ocsp.c.

References assert, x509_extensions::auth_info, x509_extended_key_usage::bits, ocsp_check::cert, ocsp_response::data, DBGC, DBGC2, EACCES_NON_OCSP_SIGNING, EACCES_STALE, x509_extensions::ext_usage, x509_certificate::extensions, x509_ocsp_responder::good, ocsp_check::issuer, ocsp_response::next_update, NULL, x509_authority_info_access::ocsp, ocsp_check_signature(), rc, ocsp_check::response, ocsp_response::signer, strerror(), ocsp_response::this_update, TIMESTAMP_ERROR_MARGIN, x509_invalidate(), x509_name(), X509_OCSP_SIGNING, and x509_validate().

Referenced by validator_ocsp_validate().

                                                           {
        struct ocsp_response *response = &ocsp->response;
        struct x509_certificate *signer;
        int rc;

        /* Sanity checks */
        assert ( response->data != NULL );

        /* The response may include a signer certificate; if this is
         * not present then the response must have been signed
         * directly by the issuer.
         */
        signer = ( response->signer ? response->signer : ocsp->issuer );

        /* Validate signer, if applicable.  If the signer is not the
         * issuer, then it must be signed directly by the issuer.
         */
        if ( signer != ocsp->issuer ) {
                /* Forcibly invalidate the signer, since we need to
                 * ensure that it was signed by our issuer (and not
                 * some other issuer).  This prevents a sub-CA's OCSP
                 * certificate from fraudulently signing OCSP
                 * responses from the parent CA.
                 */
                x509_invalidate ( signer );
                if ( ( rc = x509_validate ( signer, ocsp->issuer, time,
                                            &ocsp_root ) ) != 0 ) {
                        DBGC ( ocsp, "OCSP %p \"%s\" could not validate ",
                               ocsp, x509_name ( ocsp->cert ) );
                        DBGC ( ocsp, "signer \"%s\": %s\n",
                               x509_name ( signer ), strerror ( rc ) );
                        return rc;
                }

                /* If signer is not the issuer, then it must have the
                 * extendedKeyUsage id-kp-OCSPSigning.
                 */
                if ( ! ( signer->extensions.ext_usage.bits &
                         X509_OCSP_SIGNING ) ) {
                        DBGC ( ocsp, "OCSP %p \"%s\" ",
                               ocsp, x509_name ( ocsp->cert ) );
                        DBGC ( ocsp, "signer \"%s\" is not an OCSP-signing "
                               "certificate\n", x509_name ( signer ) );
                        return -EACCES_NON_OCSP_SIGNING;
                }
        }

        /* Check OCSP response signature */
        if ( ( rc = ocsp_check_signature ( ocsp, signer ) ) != 0 )
                return rc;

        /* Check OCSP response is valid at the specified time
         * (allowing for some margin of error).
         */
        if ( response->this_update > ( time + TIMESTAMP_ERROR_MARGIN ) ) {
                DBGC ( ocsp, "OCSP %p \"%s\" response is not yet valid (at "
                       "time %lld)\n", ocsp, x509_name ( ocsp->cert ), time );
                return -EACCES_STALE;
        }
        if ( response->next_update < ( time - TIMESTAMP_ERROR_MARGIN ) ) {
                DBGC ( ocsp, "OCSP %p \"%s\" response is stale (at time "
                       "%lld)\n", ocsp, x509_name ( ocsp->cert ), time );
                return -EACCES_STALE;
        }
        DBGC2 ( ocsp, "OCSP %p \"%s\" response is valid (at time %lld)\n",
                ocsp, x509_name ( ocsp->cert ), time );

        /* Mark certificate as passing OCSP verification */
        ocsp->cert->extensions.auth_info.ocsp.good = 1;

        /* Validate certificate against issuer */
        if ( ( rc = x509_validate ( ocsp->cert, ocsp->issuer, time,
                                    &ocsp_root ) ) != 0 ) {
                DBGC ( ocsp, "OCSP %p \"%s\" could not validate certificate: "
                       "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
                return rc;
        }
        DBGC ( ocsp, "OCSP %p \"%s\" successfully validated ",
               ocsp, x509_name ( ocsp->cert ) );
        DBGC ( ocsp, "using \"%s\"\n", x509_name ( signer ) );

        return 0;
}

Variable Documentation

const uint8_t ocsp_algorithm_id[] [static]
Initial value:

OCSP digest algorithm identifier.

Definition at line 111 of file ocsp.c.

Referenced by ocsp_request().

OCSP basic response type.

Definition at line 115 of file ocsp.c.

Initial value:

OCSP basic response type cursor.

Definition at line 118 of file ocsp.c.

struct x509_root ocsp_root [static]
Initial value:
 {
        .digest = &ocsp_digest_algorithm,
        .count = 0,
        .fingerprints = NULL,
}

OCSP dummy root certificate store.

OCSP validation uses no root certificates, since it takes place only when there already exists a validated issuer certificate.

Definition at line 813 of file ocsp.c.