/* * Copyright (C) 2012 Michael Brown . * * 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 #include #include #include #include #include #include #include #include #include #include #include /** @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_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_tail = &ocsp->request.cert_id_tail; 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_tail->data = builder->data; cert_id_tail->len = builder->len; if ( ( rc = ( asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_skip ( cert_id_tail, 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; 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; } /** * 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->root != NULL ); /* 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 ) { static struct asn1_cursor algorithm = { .data = ocsp_algorithm_id, .len = sizeof ( ocsp_algorithm_id ), }; struct asn1_cursor cert_id; struct asn1_cursor cursor; int rc; /* Enter cert ID */ memcpy ( &cert_id, raw, sizeof ( cert_id ) ); asn1_enter ( &cert_id, ASN1_SEQUENCE ); /* Check certID algorithm (but not parameters) */ memcpy ( &cursor, &cert_id, sizeof ( cursor ) ); if ( ( rc = ( asn1_enter ( &cursor, ASN1_SEQUENCE ), asn1_shrink ( &cursor, ASN1_OID ), asn1_shrink ( &algorithm, ASN1_OID ) ) ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" certID missing algorithm:\n", ocsp, x509_name ( ocsp->cert ) ); DBGC_HDA ( ocsp, 0, cursor.data, cursor.len ); return -EACCES_CERT_MISMATCH; } if ( asn1_compare ( &cursor, &algorithm ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" certID wrong algorithm:\n", ocsp, x509_name ( ocsp->cert ) ); DBGC_HDA ( ocsp, 0, cursor.data, cursor.len ); return -EACCES_CERT_MISMATCH; } /* Check remaining certID fields */ asn1_skip ( &cert_id, ASN1_SEQUENCE ); if ( asn1_compare ( &cert_id, &ocsp->request.cert_id_tail ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" certID mismatch:\n", ocsp, x509_name ( ocsp->cert ) ); DBGC_HDA ( ocsp, 0, ocsp->request.cert_id_tail.data, ocsp->request.cert_id_tail.len ); DBGC_HDA ( ocsp, 0, cert_id.data, cert_id.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; } /** * 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 asn1_cursor *key = &signer->subject.public_key.raw; uint8_t digest_ctx[ digest->ctxsize ]; uint8_t digest_out[ digest->digestsize ]; 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 ); /* Verify digest */ if ( ( rc = pubkey_verify ( pubkey, key, 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 )); return rc; } DBGC2 ( ocsp, "OCSP %p \"%s\" signature is correct\n", ocsp, x509_name ( ocsp->cert ) ); return 0; } /** * 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->issuer->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->issuer->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; }