// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC #include #include "../internal.h" #include "internal.h" #define SIGNER_IN_PROVIDED_CERTS 2 #define SIGNER_IN_OCSP_CERTS 1 #define SIGNER_NOT_FOUND 0 // Set up |X509_STORE_CTX| to verify signer and returns cert chain if verify is // OK. A |OCSP_RESPID| can be identified either by name or its keyhash. // https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.2.3 static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_RESPID *id) { if (certs == NULL || id == NULL) { return NULL; } // Easy if lookup by name. if (id->type == V_OCSP_RESPID_NAME) { return X509_find_by_subject(certs, id->value.byName); } // Lookup by key hash. unsigned char tmphash[SHA_DIGEST_LENGTH], *keyhash; // If key hash isn't SHA1 length then forget it. if (id->value.byKey == NULL || id->value.byKey->length != SHA_DIGEST_LENGTH) { return NULL; } keyhash = id->value.byKey->data; // Calculate hash of each key and compare. X509 *cert; for (size_t i = 0; i < sk_X509_num(certs); i++) { cert = sk_X509_value(certs, i); if (X509_pubkey_digest(cert, EVP_sha1(), tmphash, NULL)) { if (memcmp(keyhash, tmphash, SHA_DIGEST_LENGTH) == 0) { return cert; } } } return NULL; } // Find signer in cert stack or |OCSP_BASICRESP|'s cert stack. static int ocsp_find_signer(X509 **psigner, OCSP_BASICRESP *bs, STACK_OF(X509) *certs, unsigned long flags) { if (psigner == NULL) { OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER); return -1; } X509 *signer; OCSP_RESPID *rid = bs->tbsResponseData->responderId; // look for signer in certs stack. signer = ocsp_find_signer_sk(certs, rid); if (signer != NULL) { *psigner = signer; return SIGNER_IN_PROVIDED_CERTS; } // look in certs stack the responder may have included in |OCSP_BASICRESP|, // unless the flags contain |OCSP_NOINTERN|. signer = ocsp_find_signer_sk(bs->certs, rid); if (signer != NULL && !IS_OCSP_FLAG_SET(flags, OCSP_NOINTERN)) { *psigner = signer; return SIGNER_IN_OCSP_CERTS; } // Maybe lookup from store if by subject name. *psigner = NULL; return SIGNER_NOT_FOUND; } // check if public key in signer matches key in |OCSP_BASICRESP|. static int ocsp_verify_key(OCSP_BASICRESP *bs, X509 *signer) { if (signer == NULL) { OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER); return -1; } EVP_PKEY *skey = X509_get_pubkey(signer); if (skey == NULL) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_SIGNER_KEY); return -1; } int ret = ASN1_item_verify(ASN1_ITEM_rptr(OCSP_RESPDATA), bs->signatureAlgorithm, bs->signature, bs->tbsResponseData, skey); EVP_PKEY_free(skey); if (ret <= 0) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_SIGNATURE_FAILURE); } return ret; } // Set untrusted certificate stack from |OCSP_BASICRESP|. static int ocsp_setup_untrusted(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, STACK_OF(X509) **untrusted, unsigned long flags) { if (untrusted == NULL) { OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER); return -1; } if (IS_OCSP_FLAG_SET(flags, OCSP_NOCHAIN)) { *untrusted = NULL; } else if (bs->certs && certs) { *untrusted = sk_X509_dup(bs->certs); for (size_t i = 0; i < sk_X509_num(certs); i++) { if (!sk_X509_push(*untrusted, sk_X509_value(certs, i))) { return -1; } } } else if (certs != NULL) { *untrusted = sk_X509_dup(certs); } else { *untrusted = sk_X509_dup(bs->certs); } return 1; } static int ocsp_verify_signer(X509 *signer, X509_STORE *st, STACK_OF(X509) *untrusted, STACK_OF(X509) **chain) { if (signer == NULL) { OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER); return -1; } // Set up |X509_STORE_CTX| with |*signer|, |*st|, and |*untrusted|. X509_STORE_CTX *ctx = X509_STORE_CTX_new(); int ret = -1; if (ctx == NULL) { goto end; } if (!X509_STORE_CTX_init(ctx, st, signer, untrusted)) { OPENSSL_PUT_ERROR(OCSP, ERR_R_X509_LIB); goto end; } if (!X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER)) { OPENSSL_PUT_ERROR(OCSP, ERR_R_X509_LIB); goto end; } // Verify |X509_STORE_CTX| and return certificate chain. ret = X509_verify_cert(ctx); if (ret <= 0) { int err = X509_STORE_CTX_get_error(ctx); OPENSSL_PUT_ERROR(OCSP, OCSP_R_CERTIFICATE_VERIFY_ERROR); ERR_add_error_data(2, "Verify error: ", X509_verify_cert_error_string(err)); goto end; } if (chain != NULL) { *chain = X509_STORE_CTX_get1_chain(ctx); } end: X509_STORE_CTX_free(ctx); return ret; } // Check the issuer certificate IDs for equality. If the issuer IDs all match // then we just need to check equality against one of them. If there is a // mismatch with the same algorithm then there's no point trying to match any // certificates against the issuer, and |*ret| will be set to NULL. static int ocsp_check_ids(STACK_OF(OCSP_SINGLERESP) *sresp, OCSP_CERTID **ret) { if (sresp == NULL || ret == NULL) { OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER); return -1; } OCSP_CERTID *tmpid, *cid; size_t idcount = sk_OCSP_SINGLERESP_num(sresp); if (idcount == 0) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA); return -1; } cid = sk_OCSP_SINGLERESP_value(sresp, 0)->certId; *ret = NULL; for (size_t i = 1; i < idcount; i++) { tmpid = sk_OCSP_SINGLERESP_value(sresp, i)->certId; // Check to see if IDs match. if (OCSP_id_issuer_cmp(cid, tmpid) != 0) { // If algorithm mismatch, let caller deal with it instead. if (OBJ_cmp(tmpid->hashAlgorithm->algorithm, cid->hashAlgorithm->algorithm) != 0) { return 1; } return 0; } } // All IDs match: only need to check one ID. *ret = cid; return 1; } static int ocsp_match_issuerid(X509 *cert, OCSP_CERTID *cid, STACK_OF(OCSP_SINGLERESP) *sresp) { if (cert == NULL) { OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER); return -1; } // If only one ID to match then do it. if (cid) { const EVP_MD *dgst; X509_NAME *iname; unsigned char md[EVP_MAX_MD_SIZE]; // Set up message digest for comparison. dgst = EVP_get_digestbyobj(cid->hashAlgorithm->algorithm); if (dgst == NULL) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_UNKNOWN_MESSAGE_DIGEST); return -1; } size_t mdlen = EVP_MD_size(dgst); iname = X509_get_subject_name(cert); if (!X509_NAME_digest(iname, dgst, md, NULL)) { return -1; } // Compare message digest with |OCSP_CERTID| if (cid->issuerNameHash->length >= 0 && cid->issuerKeyHash->length >= 0) { if (((size_t)cid->issuerNameHash->length != mdlen) || (size_t)cid->issuerKeyHash->length != mdlen) { return 0; } } if (memcmp(md, cid->issuerNameHash->data, mdlen) != 0) { return 0; } if (0 <= X509_pubkey_digest(cert, dgst, md, NULL)) { if (memcmp(md, cid->issuerKeyHash->data, mdlen) != 0) { return 0; } } return 1; } // We have to match the whole stack recursively, if |cid| is NULL. This // means that the issuer certificate's hash algorithm did not match with // the rest of the |certId|s in the |OCSP_SINGLERESP| stack. (Issuer // certificate is taken from the root of the |OCSP_SINGLERESP| stack). // We'll have to find a match from the signer or responder CA certificate // (both are certificates in the certificate chain) in the // |OCSP_SINGLERESP| stack instead. else { int ret; OCSP_CERTID *tmpid; for (size_t i = 0; i < sk_OCSP_SINGLERESP_num(sresp); i++) { tmpid = sk_OCSP_SINGLERESP_value(sresp, i)->certId; ret = ocsp_match_issuerid(cert, tmpid, NULL); if (ret <= 0) { return ret; } } return 1; } } static int ocsp_check_delegated(X509 *x) { if (x == NULL) { OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER); return -1; } if ((X509_get_extension_flags(x) & EXFLAG_XKUSAGE) && (X509_get_extended_key_usage(x) & XKU_OCSP_SIGN)) { return 1; } OPENSSL_PUT_ERROR(OCSP, OCSP_R_MISSING_OCSPSIGNING_USAGE); return 0; } // check OCSP responder issuer and ids static int ocsp_check_issuer(OCSP_BASICRESP *bs, STACK_OF(X509) *chain) { if (chain == NULL) { OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER); return -1; } STACK_OF(OCSP_SINGLERESP) *sresp = bs->tbsResponseData->responses; OCSP_CERTID *caid = NULL; int ret; if (sk_X509_num(chain) <= 0) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_CERTIFICATES_IN_CHAIN); return -1; } // See if the issuer IDs match. ret = ocsp_check_ids(sresp, &caid); // If ID mismatch or other error then return. if (ret <= 0) { return ret; } X509 *signer, *sca; signer = sk_X509_value(chain, 0); // Check to see if OCSP responder CA matches request CA. if (sk_X509_num(chain) > 1) { sca = sk_X509_value(chain, 1); ret = ocsp_match_issuerid(sca, caid, sresp); if (ret < 0) { return ret; } if (ret != 0) { // If matches, then check extension flags. if (ocsp_check_delegated(signer)) { return 1; } return 0; } } // Otherwise check if OCSP request signed directly by request CA. return ocsp_match_issuerid(signer, caid, sresp); } int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags) { if (bs == NULL || st == NULL) { OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER); return -1; } X509 *signer; STACK_OF(X509) *chain = NULL; STACK_OF(X509) *untrusted = NULL; // Look for signer certificate. int ret = ocsp_find_signer(&signer, bs, certs, flags); if (ret <= SIGNER_NOT_FOUND) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND); goto end; } if ((ret == SIGNER_IN_PROVIDED_CERTS) && IS_OCSP_FLAG_SET(flags, OCSP_TRUSTOTHER)) { // We skip verification if the flag to trust |certs| is set and the signer // is found within that stack. flags |= OCSP_NOVERIFY; } // Check if public key in signer matches key in |OCSP_BASICRESP|. ret = ocsp_verify_key(bs, signer); if (ret <= 0) { goto end; } if (!IS_OCSP_FLAG_SET(flags, OCSP_NOVERIFY)) { // Verify signer and if valid, check the certificate chain. ret = ocsp_setup_untrusted(bs, certs, &untrusted, flags); if (ret <= 0) { goto end; } ret = ocsp_verify_signer(signer, st, untrusted, &chain); if (ret <= 0) { goto end; } // At this point we have a valid certificate chain, need to verify it // against the OCSP issuer criteria. ret = ocsp_check_issuer(bs, chain); // If a certificate chain is not verifiable against the OCSP issuer // criteria, we try to check for explicit trust. if (ret == 0) { // Easy case: explicitly trusted. Get root CA and check for explicit // trust. if (IS_OCSP_FLAG_SET(flags, OCSP_NOEXPLICIT)) { goto end; } X509 *root_cert = sk_X509_value(chain, sk_X509_num(chain) - 1); if (X509_check_trust(root_cert, NID_OCSP_sign, 0) != X509_TRUST_TRUSTED) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_ROOT_CA_NOT_TRUSTED); ret = 0; goto end; } ret = 1; } } end: sk_X509_pop_free(chain, X509_free); sk_X509_free(untrusted); return ret; } // ocsp_req_find_signer assigns |*psigner| to the signing certificate and // returns |SIGNER_IN_OCSP_CERTS| if it was found within |req| or returns // |SIGNER_IN_TRUSTED_CERTS| if it was found within |certs|. It returns // |SIGNER_NOT_FOUND| if the signing certificate is not found. static int ocsp_req_find_signer(X509 **psigner, OCSP_REQUEST *req, X509_NAME *nm, STACK_OF(X509) *certs, unsigned long flags) { X509 *signer = NULL; if (!IS_OCSP_FLAG_SET(flags, OCSP_NOINTERN)) { signer = X509_find_by_subject(req->optionalSignature->certs, nm); if (signer != NULL) { *psigner = signer; return SIGNER_IN_OCSP_CERTS; } } signer = X509_find_by_subject(certs, nm); if (signer != NULL) { *psigner = signer; return SIGNER_IN_PROVIDED_CERTS; } return SIGNER_NOT_FOUND; } int OCSP_request_verify(OCSP_REQUEST *req, STACK_OF(X509) *certs, X509_STORE *store, unsigned long flags) { GUARD_PTR(req); GUARD_PTR(req->tbsRequest); GUARD_PTR(store); // Check if |req| has signature to check against. if (req->optionalSignature == NULL) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_REQUEST_NOT_SIGNED); return 0; } GENERAL_NAME *gen = req->tbsRequest->requestorName; if (gen == NULL || gen->type != GEN_DIRNAME) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE); return 0; } // Find |signer| from |certs| or |req->optionalSignature->certs| against criteria. X509 *signer = NULL; int signer_status = ocsp_req_find_signer(&signer, req, gen->d.directoryName, certs, flags); if (signer_status <= SIGNER_NOT_FOUND || signer == NULL) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND); return 0; } if (signer_status == SIGNER_IN_PROVIDED_CERTS && IS_OCSP_FLAG_SET(flags, OCSP_TRUSTOTHER)) { // We skip certificate verification if the flag to trust |certs| is set and // the signer is found within that stack. flags |= OCSP_NOVERIFY; } // Validate |req|'s signature. EVP_PKEY *skey = X509_get0_pubkey(signer); if (skey == NULL) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_SIGNER_KEY); return 0; } if (ASN1_item_verify(ASN1_ITEM_rptr(OCSP_REQINFO), req->optionalSignature->signatureAlgorithm, req->optionalSignature->signature, req->tbsRequest, skey) <= 0) { OPENSSL_PUT_ERROR(OCSP, OCSP_R_SIGNATURE_FAILURE); return 0; } // Set up |ctx| and start doing the actual verification. X509_STORE_CTX *ctx = X509_STORE_CTX_new(); if (ctx == NULL) { return 0; } // Validate the signing certificate. int ret = 0; if (!IS_OCSP_FLAG_SET(flags, OCSP_NOVERIFY)) { // Initialize and set purpose of |ctx| for verification. if (!X509_STORE_CTX_init(ctx, store, signer, NULL) && !X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER)) { OPENSSL_PUT_ERROR(OCSP, ERR_R_X509_LIB); goto end; } if (!IS_OCSP_FLAG_SET(flags, OCSP_NOCHAIN)) { X509_STORE_CTX_set_chain(ctx, req->optionalSignature->certs); } // Do the verification. if (X509_verify_cert(ctx) <= 0) { int err = X509_STORE_CTX_get_error(ctx); OPENSSL_PUT_ERROR(OCSP, OCSP_R_CERTIFICATE_VERIFY_ERROR); ERR_add_error_data(2, "Verify error:", X509_verify_cert_error_string(err)); goto end; } } ret = 1; end: X509_STORE_CTX_free(ctx); return ret; }