X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/libsecurity_apple_x509_tp/lib/tpOcspCertVfy.cpp diff --git a/libsecurity_apple_x509_tp/lib/tpOcspCertVfy.cpp b/libsecurity_apple_x509_tp/lib/tpOcspCertVfy.cpp deleted file mode 100644 index f0a7815a..00000000 --- a/libsecurity_apple_x509_tp/lib/tpOcspCertVfy.cpp +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -/* - * tpOcspCertVfy.cpp - OCSP cert verification routines - */ - -#include "tpOcspCertVfy.h" -#include "tpdebugging.h" -#include "certGroupUtils.h" -#include -#include -#include - -/* - * Is signerCert authorized to sign OCSP responses by issuerCert? IssuerCert is - * assumed to be (i.e., must, but we don't check that here) the signer of the - * cert being verified, which is not in the loop for this op. Just a bool returned; - * it's autoritized or it's not. - */ -static bool tpIsAuthorizedOcspSigner( - TPCertInfo &issuerCert, // issuer of cert being verified - TPCertInfo &signerCert) // potential signer of OCSP response -{ - CSSM_DATA_PTR fieldValue = NULL; // mallocd by CL - CSSM_RETURN crtn; - bool ourRtn = false; - CE_ExtendedKeyUsage *eku = NULL; - bool foundEku = false; - - /* - * First see if issuerCert issued signerCert (No signature vfy yet, just - * subject/issuer check). - */ - if(!issuerCert.isIssuerOf(signerCert)) { - return false; - } - - /* Fetch ExtendedKeyUse field from signerCert */ - crtn = signerCert.fetchField(&CSSMOID_ExtendedKeyUsage, &fieldValue); - if(crtn) { - tpOcspDebug("tpIsAuthorizedOcspSigner: signer is issued by issuer, no EKU"); - return false; - } - CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data; - if(cssmExt->format != CSSM_X509_DATAFORMAT_PARSED) { - tpOcspDebug("tpIsAuthorizedOcspSigner: bad extension format"); - goto errOut; - } - eku = (CE_ExtendedKeyUsage *)cssmExt->value.parsedValue; - - /* Look for OID_KP_OCSPSigning */ - for(unsigned dex=0; dexnumPurposes; dex++) { - if(tpCompareCssmData(&eku->purposes[dex], &CSSMOID_OCSPSigning)) { - foundEku = true; - break; - } - } - if(!foundEku) { - tpOcspDebug("tpIsAuthorizedOcspSigner: signer is issued by issuer, no OCSP " - "signing EKU"); - goto errOut; - } - - /* - * OK, signerCert is authorized by *someone* to sign OCSP requests, and - * it claims to be issued by issuer. Sig verify to be sure. - * FIXME this is not handling partial public keys, which would be a colossal - * mess to handle in this module...so we don't. - */ - crtn = signerCert.verifyWithIssuer(&issuerCert, NULL); - if(crtn == CSSM_OK) { - tpOcspDebug("tpIsAuthorizedOcspSigner: FOUND authorized signer"); - ourRtn = true; - } - else { - /* This is a highly irregular situation... */ - tpOcspDebug("tpIsAuthorizedOcspSigner: signer sig verify FAIL"); - } -errOut: - if(fieldValue != NULL) { - signerCert.freeField(&CSSMOID_ExtendedKeyUsage, fieldValue); - } - return ourRtn; -} - -/* - * Check ResponderID linkage between an OCSPResponse and a cert we believe to - * be the issuer of both that response and the cert being verified. Returns - * true if OK. - */ -static -bool tpOcspResponderIDCheck( - OCSPResponse &ocspResp, - TPCertInfo &signer) -{ - bool shouldBeSigner = false; - if(ocspResp.responderIDTag() == RIT_Name) { - /* - * Name inside response must == signer's SubjectName. - * Note we can't use signer.subjectName(); that's normalized. - */ - - const CSSM_DATA *respIdName = ocspResp.encResponderName(); - CSSM_DATA *subjectName = NULL; - CSSM_RETURN crtn = signer.fetchField(&CSSMOID_X509V1SubjectNameStd, - &subjectName); - if(crtn) { - /* bad cert */ - tpOcspDebug("tpOcspResponderIDCheck: error on fetchField(subjectName"); - return false; - } - if(tpCompareCssmData(respIdName, subjectName)) { - tpOcspDebug("tpOcspResponderIDCheck: good ResponderID.byName"); - shouldBeSigner = true; - } - else { - tpOcspDebug("tpOcspResponderIDCheck: BAD ResponderID.byName"); - } - signer.freeField(&CSSMOID_X509V1SubjectNameStd, subjectName); - } - else { - /* ResponderID.byKey must == SHA1(signer's public key) */ - const CSSM_KEY *pubKey = signer.pubKey(); - assert(pubKey != NULL); - uint8 digest[CC_SHA1_DIGEST_LENGTH]; - CSSM_DATA keyHash = {CC_SHA1_DIGEST_LENGTH, digest}; - ocspdSha1(pubKey->KeyData.Data, (CC_LONG)pubKey->KeyData.Length, digest); - const CSSM_DATA *respKeyHash = &ocspResp.responderID().byKey; - if(tpCompareCssmData(&keyHash, respKeyHash)) { - tpOcspDebug("tpOcspResponderIDCheck: good ResponderID.byKey"); - shouldBeSigner = true; - } - else { - tpOcspDebug("tpOcspResponderIDCheck: BAD ResponderID.byKey"); - } - } - return shouldBeSigner; -} - -/* - * Verify the signature of an OCSP response. Caller is responsible for all other - * verification of the response, this is just the crypto. - * Returns true on success. - */ -static bool tpOcspResponseSigVerify( - TPVerifyContext &vfyCtx, - OCSPResponse &ocspResp, // parsed response - TPCertInfo &signer) -{ - /* get signature algorithm in CSSM form from the response */ - const SecAsn1OCSPBasicResponse &basicResp = ocspResp.basicResponse(); - const CSSM_OID *algOid = &basicResp.algId.algorithm; - CSSM_ALGORITHMS sigAlg; - - if(!cssmOidToAlg(algOid, &sigAlg)) { - tpOcspDebug("tpOcspResponseSigVerify: unknown signature algorithm"); - } - - /* signer's public key from the cert */ - const CSSM_KEY *pubKey = signer.pubKey(); - - /* signature: on decode, length is in BITS */ - CSSM_DATA sig = basicResp.sig; - sig.Length /= 8; - - CSSM_RETURN crtn; - CSSM_CC_HANDLE sigHand; - bool ourRtn = false; - crtn = CSSM_CSP_CreateSignatureContext(vfyCtx.cspHand, sigAlg, NULL, - pubKey, &sigHand); - if(crtn) { - #ifndef NDEBUG - cssmPerror("tpOcspResponseSigVerify, CSSM_CSP_CreateSignatureContext", crtn); - #endif - return false; - } - crtn = CSSM_VerifyData(sigHand, &basicResp.tbsResponseData, 1, - CSSM_ALGID_NONE, &sig); - if(crtn) { - #ifndef NDEBUG - cssmPerror("tpOcspResponseSigVerify, CSSM_VerifyData", crtn); - #endif - } - else { - ourRtn = true; - } - CSSM_DeleteContext(sigHand); - return ourRtn; -} - -/* possible return from tpIsOcspIssuer() */ -typedef enum { - OIS_No, // not the issuer - OIS_Good, // is the issuer and signature matches - OIS_BadSig, // appears to be issuer, but signature doesn't match -} OcspIssuerStatus; - -/* type of rawCert passed to tpIsOcspIssuer */ -typedef enum { - OCT_Local, // LocalResponder - no checking other than signature - OCT_Issuer, // it's the issuer of the cert being verified - OCT_Provided, // came with response, provenance unknown -} OcspCertType; - -/* - * Did specified cert issue the OCSP response? - * - * This implements the algorithm described in RFC2560, section 4.2.2.2, - * "Authorized Responders". It sees if the cert could be the issuer of the - * OCSP response per that algorithm; then if it could, it performs signature - * verification. - */ -static OcspIssuerStatus tpIsOcspIssuer( - TPVerifyContext &vfyCtx, - OCSPResponse &ocspResp, // parsed response - /* on input specify at least one of the following two */ - const CSSM_DATA *signerData, - TPCertInfo *signer, - OcspCertType certType, // where rawCert came from - TPCertInfo *issuer, // OPTIONAL, if known - TPCertInfo **signerRtn) // optionally RETURNED if at all possible -{ - assert((signerData != NULL) || (signer != NULL)); - - /* get signer as TPCertInfo if caller hasn't provided */ - TPCertInfo *tmpSigner = NULL; - if(signer == NULL) { - try { - tmpSigner = new TPCertInfo(vfyCtx.clHand, vfyCtx.cspHand, signerData, - TIC_CopyData, vfyCtx.verifyTime); - } - catch(...) { - tpOcspDebug("tpIsOcspIssuer: bad cert"); - return OIS_No; - } - signer = tmpSigner; - } - if(signer == NULL) { - return OIS_No; - } - if(signerRtn != NULL) { - *signerRtn = signer; - } - - /* - * Qualification of "this can be the signer" depends on where the - * signer came from. - */ - bool shouldBeSigner = false; - OcspIssuerStatus ourRtn = OIS_No; - - switch(certType) { - case OCT_Local: // caller trusts this and thinks it's the signer - shouldBeSigner = true; - break; - case OCT_Issuer: // last resort, the actual issuer - /* check ResponderID linkage */ - shouldBeSigner = tpOcspResponderIDCheck(ocspResp, *signer); - break; - case OCT_Provided: - { - /* - * This cert came with the response. - */ - if(issuer == NULL) { - /* - * careful, might not know the issuer...how would this path ever - * work then? I don't think it needs to because you can NOT - * do OCSP on a cert without its issuer in hand. - */ - break; - } - - /* check EKU linkage */ - shouldBeSigner = tpIsAuthorizedOcspSigner(*issuer, *signer); - break; - } - } - if(!shouldBeSigner) { - goto errOut; - } - - /* verify the signature */ - if(tpOcspResponseSigVerify(vfyCtx, ocspResp, *signer)) { - ourRtn = OIS_Good; - } - -errOut: - if((signerRtn == NULL) && (tmpSigner != NULL)) { - delete tmpSigner; - } - return ourRtn; - -} - -OcspRespStatus tpVerifyOcspResp( - TPVerifyContext &vfyCtx, - SecNssCoder &coder, - TPCertInfo *issuer, // issuer of the related cert, may be issuer of - // reply, may not be known - OCSPResponse &ocspResp, - CSSM_RETURN &cssmErr) // possible per-cert error -{ - OcspRespStatus ourRtn = ORS_Unknown; - CSSM_RETURN crtn; - - tpOcspDebug("tpVerifyOcspResp top"); - - switch(ocspResp.responseStatus()) { - case RS_Success: - crtn = CSSM_OK; - break; - case RS_MalformedRequest: - crtn = CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ; - break; - case RS_InternalError: - crtn = CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR; - break; - case RS_TryLater: - crtn = CSSMERR_APPLETP_OCSP_RESP_TRY_LATER; - break; - case RS_SigRequired: - crtn = CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED; - break; - case RS_Unauthorized: - crtn = CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED; - break; - default: - crtn = CSSMERR_APPLETP_OCSP_BAD_RESPONSE; - break; - } - if(crtn) { - tpOcspDebug("tpVerifyOcspResp aborting due to response status %d", - (int)(ocspResp.responseStatus())); - cssmErr = crtn; - return ORS_Unknown; - } - cssmErr = CSSM_OK; - - /* one of our main jobs is to locate the signer of the response, here */ - TPCertInfo *signerInfo = NULL; - TPCertInfo *signerInfoTBD = NULL; // if non NULL at end, we delete - /* we'll be verifying into this cert group */ - TPCertGroup ocspCerts(vfyCtx.alloc, TGO_Caller); - CSSM_BOOL verifiedToRoot; - CSSM_BOOL verifiedToAnchor; - CSSM_BOOL verifiedViaTrustSetting; - - const CSSM_APPLE_TP_OCSP_OPTIONS *ocspOpts = vfyCtx.ocspOpts; - OcspIssuerStatus issuerStat; - - /* - * Set true if we ever find an apparent issuer which does not correctly - * pass signature verify. If true and we never success, that's a XXX error. - */ - bool foundBadIssuer = false; - bool foundLocalResponder = false; - uint32 numSignerCerts = ocspResp.numSignerCerts(); - - /* - * This cert group, allocated by AppleTPSession::CertGroupVerify(), - * serves two functions here: - * - * -- it accumulates certs we get from the net (as parts of OCSP responses) - * for user in verifying OCSPResponse-related certs. - * TPCertGroup::buildCertGroup() uses this group as one of the many - * sources of certs when building a cert chain. - * - * -- it provides a container into which to stash TPCertInfos which - * persist at least as long as the TPVerifyContext; it's of type TGO_Group, - * so all of the certs added to it get freed when the group does. - */ - assert(vfyCtx.signerCerts != NULL); - - TPCertGroup &gatheredCerts = vfyCtx.gatheredCerts; - - /* set up for disposal of TPCertInfos created by TPCertGroup::buildCertGroup() */ - TPCertGroup certsToBeFreed(vfyCtx.alloc, TGO_Group); - - /* - * First job is to find the cert which signed this response. - * Give priority to caller's LocalResponderCert. - */ - if((ocspOpts != NULL) && (ocspOpts->LocalResponderCert != NULL)) { - TPCertInfo *responderInfo = NULL; - issuerStat = tpIsOcspIssuer(vfyCtx, ocspResp, - ocspOpts->LocalResponderCert, NULL, - OCT_Local, issuer, &responderInfo); - switch(issuerStat) { - case OIS_BadSig: - foundBadIssuer = true; - /* drop thru */ - case OIS_No: - if(responderInfo != NULL) { - /* can't use it - should this be an immediate error? */ - delete responderInfo; - } - break; - case OIS_Good: - assert(responderInfo != NULL); - signerInfo = signerInfoTBD = responderInfo; - foundLocalResponder = true; - tpOcspDebug("tpVerifyOcspResp: signer := LocalResponderCert"); - break; - } - } - - if((signerInfo == NULL) && (numSignerCerts != 0)) { - /* - * App did not specify a local responder (or provided a bad one) - * and the response came with some certs. Try those. - */ - TPCertInfo *respCert = NULL; - for(unsigned dex=0; dexisStatusFatal(CSSMERR_APPLETP_OCSP_NO_SIGNER)) { - /* user wants to proceed without verifying! */ - tpOcspDebug("tpVerifyOcspResp: no signer found, user allows!"); - ourRtn = ORS_Good; - } - else { - tpOcspDebug("tpVerifyOcspResp: no signer found"); - ourRtn = ORS_Unknown; - /* caller adds to per-cert status */ - cssmErr = CSSMERR_APPLETP_OCSP_NO_SIGNER; - } - goto errOut; - } - - if(signerInfo != NULL && !foundLocalResponder) { - /* - * tpIsOcspIssuer has verified that signerInfo is the signer of the - * OCSP response, and that it is either the issuer of the cert being - * checked or is a valid authorized responder for that issuer based on - * key id linkage and EKU. There is no stipulation in RFC2560 to also - * build the chain back to a trusted anchor; however, we'll continue to - * enforce this for the local responder case. (10742723) - */ - tpOcspDebug("tpVerifyOcspResp SUCCESS"); - ourRtn = ORS_Good; - goto errOut; - } - - /* - * Last remaining task is to verify the signer, and all the certs back to - * an anchor - */ - - /* start from scratch with both of these groups */ - gatheredCerts.setAllUnused(); - vfyCtx.signerCerts->setAllUnused(); - crtn = ocspCerts.buildCertGroup( - *signerInfo, // subject item - vfyCtx.signerCerts, // inCertGroup the original group-to-be-verified - vfyCtx.dbList, // optional - vfyCtx.clHand, - vfyCtx.cspHand, - vfyCtx.verifyTime, - vfyCtx.numAnchorCerts, - vfyCtx.anchorCerts, - certsToBeFreed, // local to-be-freed right now - &gatheredCerts, // accumulate gathered certs here - CSSM_FALSE, // subjectIsInGroup - vfyCtx.actionFlags, - vfyCtx.policyOid, - vfyCtx.policyStr, - vfyCtx.policyStrLen, - kSecTrustSettingsKeyUseSignRevocation, - verifiedToRoot, - verifiedToAnchor, - verifiedViaTrustSetting); - if(crtn) { - tpOcspDebug("tpVerifyOcspResp buildCertGroup failure"); - cssmErr = crtn; - ourRtn = ORS_Unknown; - goto errOut; - } - - if(!verifiedToAnchor && !verifiedViaTrustSetting) { - /* required */ - ourRtn = ORS_Unknown; - if(verifiedToRoot) { - /* verified to root which is not an anchor */ - tpOcspDebug("tpVerifyOcspResp root, no anchor"); - cssmErr = CSSMERR_APPLETP_OCSP_INVALID_ANCHOR_CERT; - } - else { - /* partial chain, no root, not verifiable by anchor */ - tpOcspDebug("tpVerifyOcspResp no root, no anchor"); - cssmErr = CSSMERR_APPLETP_OCSP_NOT_TRUSTED; - } - if((issuer != NULL) && !issuer->isStatusFatal(cssmErr)) { - tpOcspDebug("...ignoring last error per trust setting"); - ourRtn = ORS_Good; - } - else { - ourRtn = ORS_Unknown; - } - } - else { - tpOcspDebug("tpVerifyOcspResp SUCCESS; chain verified"); - ourRtn = ORS_Good; - } - - /* FIXME policy verify? */ - -errOut: - delete signerInfoTBD; - /* any other cleanup? */ - return ourRtn; -}