david/ipxe
Archived
1
0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/src/crypto/ocsp.c
Michael Brown 5cbdc41778 [crypto] Fix parsing of OCSP responder ID key hash
We currently compare the entirety of the KeyHash object (including the
ASN.1 tag and length byte) against the raw SHA-1 hash of the
certificate's public key.  This causes OCSP validation to fail for any
responses which identify the responder by key hash rather than by
name, and hence prevents the use of X.509 certificates where any
certificate in the chain has an OCSP responder which chooses to
identify itself via its key hash.

Fix by adding the missing asn1_enter() required to enter the ASN.1
octet string containing the key hash.

Also add a corresponding test case including an OCSP response where
the responder is identified by key hash, to ensure that this
functionality cannot be broken in future.

Debugged-by: Brian Rak <brak@gameservers.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2014-11-24 15:05:43 +00:00

961 lines
28 KiB
C

/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#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>
/** @file
*
* Online Certificate Status Protocol
*
*/
/* Disambiguate the various error causes */
#define EACCES_CERT_STATUS \
__einfo_error ( EINFO_EACCES_CERT_STATUS )
#define EINFO_EACCES_CERT_STATUS \
__einfo_uniqify ( EINFO_EACCES, 0x01, \
"Certificate status not good" )
#define EACCES_CERT_MISMATCH \
__einfo_error ( EINFO_EACCES_CERT_MISMATCH )
#define EINFO_EACCES_CERT_MISMATCH \
__einfo_uniqify ( EINFO_EACCES, 0x02, \
"Certificate ID mismatch" )
#define EACCES_NON_OCSP_SIGNING \
__einfo_error ( EINFO_EACCES_NON_OCSP_SIGNING )
#define EINFO_EACCES_NON_OCSP_SIGNING \
__einfo_uniqify ( EINFO_EACCES, 0x03, \
"Not an OCSP signing certificate" )
#define EACCES_STALE \
__einfo_error ( EINFO_EACCES_STALE )
#define EINFO_EACCES_STALE \
__einfo_uniqify ( EINFO_EACCES, 0x04, \
"Stale (or premature) OCSP repsonse" )
#define EACCES_NO_RESPONDER \
__einfo_error ( EINFO_EACCES_NO_RESPONDER )
#define EINFO_EACCES_NO_RESPONDER \
__einfo_uniqify ( EINFO_EACCES, 0x05, \
"Missing OCSP responder certificate" )
#define ENOTSUP_RESPONSE_TYPE \
__einfo_error ( EINFO_ENOTSUP_RESPONSE_TYPE )
#define EINFO_ENOTSUP_RESPONSE_TYPE \
__einfo_uniqify ( EINFO_ENOTSUP, 0x01, \
"Unsupported OCSP response type" )
#define ENOTSUP_RESPONDER_ID \
__einfo_error ( EINFO_ENOTSUP_RESPONDER_ID )
#define EINFO_ENOTSUP_RESPONDER_ID \
__einfo_uniqify ( EINFO_ENOTSUP, 0x02, \
"Unsupported OCSP responder ID" )
#define EPROTO_MALFORMED_REQUEST \
__einfo_error ( EINFO_EPROTO_MALFORMED_REQUEST )
#define EINFO_EPROTO_MALFORMED_REQUEST \
__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_MALFORMED_REQUEST, \
"Illegal confirmation request" )
#define EPROTO_INTERNAL_ERROR \
__einfo_error ( EINFO_EPROTO_INTERNAL_ERROR )
#define EINFO_EPROTO_INTERNAL_ERROR \
__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_INTERNAL_ERROR, \
"Internal error in issuer" )
#define EPROTO_TRY_LATER \
__einfo_error ( EINFO_EPROTO_TRY_LATER )
#define EINFO_EPROTO_TRY_LATER \
__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_TRY_LATER, \
"Try again later" )
#define EPROTO_SIG_REQUIRED \
__einfo_error ( EINFO_EPROTO_SIG_REQUIRED )
#define EINFO_EPROTO_SIG_REQUIRED \
__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_SIG_REQUIRED, \
"Must sign the request" )
#define EPROTO_UNAUTHORIZED \
__einfo_error ( EINFO_EPROTO_UNAUTHORIZED )
#define EINFO_EPROTO_UNAUTHORIZED \
__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_UNAUTHORIZED, \
"Request unauthorized" )
#define EPROTO_STATUS( status ) \
EUNIQ ( EINFO_EPROTO, (status), EPROTO_MALFORMED_REQUEST, \
EPROTO_INTERNAL_ERROR, EPROTO_TRY_LATER, \
EPROTO_SIG_REQUIRED, EPROTO_UNAUTHORIZED )
/** OCSP digest algorithm */
#define ocsp_digest_algorithm sha1_algorithm
/** OCSP digest algorithm identifier */
static const uint8_t ocsp_algorithm_id[] =
{ OCSP_ALGORITHM_IDENTIFIER ( ASN1_OID_SHA1 ) };
/** OCSP basic response type */
static const uint8_t oid_basic_response_type[] = { ASN1_OID_OCSP_BASIC };
/** OCSP basic response type cursor */
static struct asn1_cursor oid_basic_response_type_cursor =
ASN1_OID_CURSOR ( oid_basic_response_type );
/**
* Free OCSP check
*
* @v refcnt Reference count
*/
static void ocsp_free ( struct refcnt *refcnt ) {
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 );
}
/**
* Build OCSP request
*
* @v ocsp OCSP check
* @ret rc Return status code
*/
static int ocsp_request ( struct ocsp_check *ocsp ) {
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;
}
/**
* Build OCSP URI string
*
* @v ocsp OCSP check
* @ret rc Return status code
*/
static int ocsp_uri_string ( struct ocsp_check *ocsp ) {
struct x509_ocsp_responder *responder =
&ocsp->cert->extensions.auth_info.ocsp;
struct uri path_uri;
char *path_base64_string;
char *path_uri_string;
size_t path_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;
}
/* Base64-encode the request as the URI path */
path_len = ( base64_encoded_len ( ocsp->request.builder.len )
+ 1 /* NUL */ );
path_base64_string = malloc ( path_len );
if ( ! path_base64_string ) {
rc = -ENOMEM;
goto err_path_base64;
}
base64_encode ( ocsp->request.builder.data, ocsp->request.builder.len,
path_base64_string );
/* URI-encode the Base64-encoded request */
memset ( &path_uri, 0, sizeof ( path_uri ) );
path_uri.path = path_base64_string;
path_uri_string = format_uri_alloc ( &path_uri );
if ( ! path_uri_string ) {
rc = -ENOMEM;
goto err_path_uri;
}
/* Construct URI string */
len = ( responder->uri.len + strlen ( path_uri_string ) + 1 /* NUL */ );
ocsp->uri_string = zalloc ( len );
if ( ! ocsp->uri_string ) {
rc = -ENOMEM;
goto err_ocsp_uri;
}
memcpy ( ocsp->uri_string, responder->uri.data, responder->uri.len );
strcpy ( &ocsp->uri_string[responder->uri.len], path_uri_string );
DBGC2 ( ocsp, "OCSP %p \"%s\" URI is %s\n",
ocsp, x509_name ( ocsp->cert ), ocsp->uri_string );
/* Success */
rc = 0;
err_ocsp_uri:
free ( path_uri_string );
err_path_uri:
free ( path_base64_string );
err_path_base64:
err_no_uri:
return rc;
}
/**
* Create OCSP check
*
* @v cert Certificate to check
* @v issuer Issuing certificate
* @ret ocsp OCSP check
* @ret rc Return status code
*/
int ocsp_check ( struct x509_certificate *cert,
struct x509_certificate *issuer,
struct ocsp_check **ocsp ) {
int rc;
/* Sanity checks */
assert ( cert != NULL );
assert ( issuer != NULL );
assert ( issuer->valid );
/* 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;
}
/**
* Parse OCSP response status
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_response_status ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
/**
* Parse OCSP response type
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_response_type ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
/**
* Compare responder's certificate name
*
* @v ocsp OCSP check
* @v cert Certificate
* @ret difference Difference as returned by memcmp()
*/
static int ocsp_compare_responder_name ( struct ocsp_check *ocsp,
struct x509_certificate *cert ) {
struct ocsp_responder *responder = &ocsp->response.responder;
/* Compare responder ID with certificate's subject */
return asn1_compare ( &responder->id, &cert->subject.raw );
}
/**
* Compare responder's certificate public key hash
*
* @v ocsp OCSP check
* @v cert Certificate
* @ret difference Difference as returned by memcmp()
*/
static int ocsp_compare_responder_key_hash ( struct ocsp_check *ocsp,
struct x509_certificate *cert ) {
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 ) );
}
/**
* Parse OCSP responder ID
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_responder_id ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
}
/**
* Parse OCSP certificate ID
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_cert_id ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
/**
* Parse OCSP responses
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_responses ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
/**
* Parse OCSP response data
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_tbs_response_data ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
/**
* Parse OCSP certificates
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_certs ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
/**
* Parse OCSP basic response
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_basic_response ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
/**
* Parse OCSP response bytes
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_response_bytes ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
/**
* Parse OCSP response
*
* @v ocsp OCSP check
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
static int ocsp_parse_response ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
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;
}
/**
* Receive OCSP response
*
* @v ocsp OCSP check
* @v data Response data
* @v len Length of response data
* @ret rc Return status code
*/
int ocsp_response ( struct ocsp_check *ocsp, const void *data, size_t len ) {
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;
}
/**
* OCSP dummy root certificate store
*
* OCSP validation uses no root certificates, since it takes place
* only when there already exists a validated issuer certificate.
*/
static struct x509_root ocsp_root = {
.digest = &ocsp_digest_algorithm,
.count = 0,
.fingerprints = NULL,
};
/**
* Check OCSP response signature
*
* @v ocsp OCSP check
* @v signer Signing certificate
* @ret rc Return status code
*/
static int ocsp_check_signature ( struct ocsp_check *ocsp,
struct x509_certificate *signer ) {
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;
}
/**
* Validate OCSP response
*
* @v ocsp OCSP check
* @v time Time at which to validate response
* @ret rc Return status code
*/
int ocsp_validate ( struct ocsp_check *ocsp, time_t time ) {
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;
}