static CSSM_RETURN tpIssuerCertViaNet( const CSSM_DATA &url, CSSM_CL_HANDLE clHand, CSSM_CSP_HANDLE cspHand, const char *verifyTime, TPCertInfo &subject, TPCertInfo *&rtnCert) { TPCertInfo *issuer = NULL; CSSM_DATA certData; CSSM_RETURN crtn; Allocator &alloc = Allocator::standard(); crtn = tpFetchViaNet(url, NULL, LT_Cert, NULL, alloc, certData); if(crtn) { tpErrorLog("tpIssuerCertViaNet: net fetch failed\n"); return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER; } try { issuer = new TPCertInfo(clHand, cspHand, &certData, TIC_CopyData, verifyTime); } catch(...) { tpErrorLog("tpIssuerCertViaNet: bad cert via net fetch\n"); alloc.free(certData.Data); rtnCert = NULL; return CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER; } alloc.free(certData.Data); /* subject/issuer match? */ if(!issuer->isIssuerOf(subject)) { tpErrorLog("tpIssuerCertViaNet: wrong issuer cert via net fetch\n"); crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER; } else { /* yep, do a sig verify */ crtn = subject.verifyWithIssuer(issuer); if(crtn) { tpErrorLog("tpIssuerCertViaNet: sig verify fail for cert via net " "fetch\n"); crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER; } } if(crtn) { assert(issuer != NULL); delete issuer; issuer = NULL; } rtnCert = issuer; return crtn; }
/* * Does this cert have a CrlDistributionPoints extension? We don't parse it, we * just tell the caller whether or not it has one. */ static bool tpCertHasCrlDistPt( TPCertInfo &cert) { CSSM_DATA_PTR fieldValue; CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints, &fieldValue); if(crtn) { return false; } else { cert.freeField(&CSSMOID_CrlDistributionPoints, fieldValue); return true; } }
/* * Get current CRL status for a certificate and its issuers. * * Possible results: * * CSSM_OK (we have a valid CRL; certificate is not revoked) * CSSMERR_TP_CERT_REVOKED (we have a valid CRL; certificate is revoked) * CSSMERR_APPLETP_NETWORK_FAILURE (CRL not available, download in progress) * CSSMERR_APPLETP_CRL_NOT_FOUND (CRL not available, and not being fetched) * CSSMERR_TP_INTERNAL_ERROR (unexpected error) * * Note that ocspdCRLStatus does NOT wait for the CRL to be downloaded before * returning, nor does it initiate a CRL download. */ static CSSM_RETURN tpGetCrlStatusForCert( TPCertInfo &subject, const CSSM_DATA &issuers) { CSSM_DATA *serialNumber=NULL; CSSM_RETURN crtn = subject.fetchField(&CSSMOID_X509V1SerialNumber, &serialNumber); if(crtn || !serialNumber) { return CSSMERR_TP_INTERNAL_ERROR; } crtn = ocspdCRLStatus(*serialNumber, issuers, subject.issuerName(), NULL); subject.freeField(&CSSMOID_X509V1SerialNumber, serialNumber); return crtn; }
/* * Perform CRL verification on a cert group. * The cert group has already passed basic issuer/subject and signature * verification. The status of the incoming CRLs is completely unknown. * * FIXME - No mechanism to get CRLs from net with non-NULL verifyTime. * How are we supposed to get the CRL which was valid at a specified * time in the past? */ CSSM_RETURN tpVerifyCertGroupWithCrls( TPVerifyContext &vfyCtx, TPCertGroup &certGroup) // to be verified { CSSM_RETURN crtn; CSSM_RETURN ourRtn = CSSM_OK; assert(vfyCtx.clHand != 0); assert(vfyCtx.policy == kRevokeCrlBasic); tpCrlDebug("tpVerifyCertGroupWithCrls numCerts %u", certGroup.numCerts()); CSSM_DATA issuers = { 0, NULL }; CSSM_APPLE_TP_CRL_OPT_FLAGS optFlags = 0; if(vfyCtx.crlOpts != NULL) { optFlags = vfyCtx.crlOpts->CrlFlags; } /* found & verified CRLs we need to release */ TPCrlGroup foundCrls(vfyCtx.alloc, TGO_Caller); try { unsigned certDex; TPCrlInfo *crl = NULL; /* get issuers as PEM-encoded data blob; we need to release */ certGroup.encodeIssuers(issuers); /* main loop, verify each cert */ for(certDex=0; certDex<certGroup.numCerts(); certDex++) { TPCertInfo *cert = certGroup.certAtIndex(certDex); tpCrlDebug("...verifying %s cert %u", cert->isAnchor() ? "anchor " : "", cert->index()); if(cert->isSelfSigned() || cert->trustSettingsFound()) { /* CRL meaningless for a root or trusted cert */ continue; } if(cert->revokeCheckComplete()) { /* Another revocation policy claimed that this cert is good to go */ tpCrlDebug(" ...cert at index %u revokeCheckComplete; skipping", cert->index()); continue; } crl = NULL; do { /* first, see if we have CRL status available for this cert */ crtn = tpGetCrlStatusForCert(*cert, issuers); tpCrlDebug("...tpGetCrlStatusForCert: %u", crtn); if(crtn == CSSM_OK) { tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u verified by local .crl\n", cert->index()); cert->revokeCheckGood(true); if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) { /* no more revocation checking necessary for this cert */ cert->revokeCheckComplete(true); } break; } if(crtn == CSSMERR_TP_CERT_REVOKED) { tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u revoked in local .crl\n", cert->index()); cert->addStatusCode(crtn); break; } if(crtn == CSSMERR_APPLETP_NETWORK_FAILURE) { /* crl is being fetched from net, but we don't have it yet */ if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) && tpCertHasCrlDistPt(*cert)) { /* crl is required; we don't have it yet, so we fail */ tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort", cert->index()); break; } /* "Best Attempt" case, so give the cert a pass for now */ tpCrlDebug(" ...cert %u: no CRL; tolerating", cert->index()); crtn = CSSM_OK; break; } /* all other CRL status results: try to fetch the CRL */ /* find a CRL for this cert by hook or crook */ crtn = tpFindCrlForCert(*cert, crl, vfyCtx); if(crtn) { /* tpFindCrlForCert may have simply caused ocspd to start * downloading a CRL asynchronously; depending on the speed * of the network and the CRL size, this may return 0 bytes * of data with a CSSMERR_APPLETP_NETWORK_FAILURE result. * We won't know the actual revocation result until the * next time we call tpGetCrlStatusForCert after the full * CRL has been downloaded successfully. */ if(optFlags & CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT) { tpCrlDebug(" ...cert %u: REQUIRE_CRL_PER_CERT abort", cert->index()); break; } if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) && tpCertHasCrlDistPt(*cert)) { tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort", cert->index()); break; } /* * This is the only place where "Best Attempt" tolerates an error */ tpCrlDebug(" ...cert %u: no CRL; tolerating", cert->index()); crtn = CSSM_OK; assert(crl == NULL); break; } /* Keep track; we'll release all when done. */ assert(crl != NULL); foundCrls.appendCrl(*crl); /* revoked? */ crtn = crl->isCertRevoked(*cert, vfyCtx.verifyTime); if(crtn) { break; } tpCrlDebug(" ...cert %u VERIFIED by CRL", cert->index()); cert->revokeCheckGood(true); if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) { /* no more revocation checking necessary for this cert */ cert->revokeCheckComplete(true); } } while(0); /* done processing one cert */ if(crtn) { tpCrlDebug(" ...cert at index %u FAILED crl vfy", cert->index()); if(ourRtn == CSSM_OK) { ourRtn = crtn; } /* continue on to next cert */ } /* error on one cert */ } /* for each cert */ } catch(const CssmError &cerr) { if(ourRtn == CSSM_OK) { ourRtn = cerr.error; } } /* other exceptions fatal */ /* release all found CRLs */ for(unsigned dex=0; dex<foundCrls.numCrls(); dex++) { TPCrlInfo *crl = foundCrls.crlAtIndex(dex); assert(crl != NULL); tpDisposeCrl(*crl, vfyCtx); } /* release issuers */ if(issuers.Data) { free(issuers.Data); } return ourRtn; }
/* * Find CRL for specified cert. Only returns a fully verified CRL. * Cert-specific errors such as CSSMERR_APPLETP_CRL_NOT_FOUND will be added * to cert's return codes. */ static CSSM_RETURN tpFindCrlForCert( TPCertInfo &subject, TPCrlInfo *&foundCrl, // RETURNED TPVerifyContext &vfyCtx) { tpCrlDebug("tpFindCrlForCert top"); TPCrlInfo *crl = NULL; foundCrl = NULL; CSSM_APPLE_TP_CRL_OPT_FLAGS crlOptFlags = 0; if(vfyCtx.crlOpts) { crlOptFlags = vfyCtx.crlOpts->CrlFlags; } /* Search inputCrls for a CRL for subject cert */ if(vfyCtx.inputCrls != NULL) { crl = vfyCtx.inputCrls->findCrlForCert(subject); if(crl && (crl->verifyWithContextNow(vfyCtx, &subject) == CSSM_OK)) { foundCrl = crl; crl->mFromWhere = CFW_InGroup; tpCrlDebug(" ...CRL found in CrlGroup"); return CSSM_OK; } } /* local process-wide cache */ crl = tpGlobalCrlCache().search(subject, vfyCtx); if(crl) { tpCrlDebug("...tpFindCrlForCert found CRL in cache, calling verifyWithContext"); if(crl->verifyWithContextNow(vfyCtx, &subject) == CSSM_OK) { foundCrl = crl; crl->mFromWhere = CFW_LocalCache; tpCrlDebug(" ...CRL found in local cache"); return CSSM_OK; } else { tpGlobalCrlCache().release(*crl); } } /* * Try DL/DB. * Note tpDbFindIssuerCrl() returns a verified CRL. */ crl = tpDbFindIssuerCrl(vfyCtx, *subject.issuerName(), subject); if(crl) { foundCrl = crl; crl->mFromWhere = CFW_DlDb; tpCrlDebug(" ...CRL found in DlDb"); return CSSM_OK; } /* Last resort: try net if enabled */ CSSM_RETURN crtn = CSSMERR_APPLETP_CRL_NOT_FOUND; crl = NULL; if(crlOptFlags & CSSM_TP_ACTION_FETCH_CRL_FROM_NET) { crtn = tpFetchCrlFromNet(subject, vfyCtx, crl); } if(crtn) { tpCrlDebug(" ...tpFindCrlForCert: CRL not found"); if(subject.addStatusCode(crtn)) { return crtn; } else { return CSSM_OK; } } /* got one from net - add to global cache */ assert(crl != NULL); tpGlobalCrlCache().add(*crl); crl->mFromWhere = CFW_Net; tpCrlDebug(" ...CRL found from net"); foundCrl = crl; return CSSM_OK; }
/* * Returns true if we are to allow/trust the specified * cert as a PKINIT-only anchor. */ static bool tpCheckPkinitServerCert( TPCertGroup &certGroup) { /* * Basic requirement: exactly one cert, self-signed. * The numCerts == 1 requirement might change... */ unsigned numCerts = certGroup.numCerts(); if(numCerts != 1) { tpDebug("tpCheckPkinitServerCert: too many certs"); return false; } /* end of chain... */ TPCertInfo *theCert = certGroup.certAtIndex(numCerts - 1); if(!theCert->isSelfSigned()) { tpDebug("tpCheckPkinitServerCert: 1 cert, not self-signed"); return false; } const CSSM_DATA *subjectName = theCert->subjectName(); /* * Open the magic keychain. * We're going up and over the Sec layer here, not generally * kosher, but this is a temp hack. */ OSStatus ortn; SecKeychainRef kcRef = NULL; string fullPathName; const char *homeDir = getenv("HOME"); if (homeDir == NULL) { // If $HOME is unset get the current user's home directory // from the passwd file. uid_t uid = geteuid(); if (!uid) uid = getuid(); struct passwd *pw = getpwuid(uid); if (!pw) { return false; } homeDir = pw->pw_dir; } fullPathName = homeDir; fullPathName += "/Library/Application Support/PKINIT/TrustedServers.keychain"; ortn = SecKeychainOpen(fullPathName.c_str(), &kcRef); if(ortn) { tpDebug("tpCheckPkinitServerCert: keychain not found (1)"); return false; } /* subsequent errors to errOut: */ bool ourRtn = false; SecKeychainStatus kcStatus; CSSM_DATA_PTR subjSerial = NULL; CSSM_RETURN crtn; SecKeychainSearchRef srchRef = NULL; SecKeychainAttributeList attrList; SecKeychainAttribute attrs[2]; SecKeychainItemRef foundItem = NULL; ortn = SecKeychainGetStatus(kcRef, &kcStatus); if(ortn) { tpDebug("tpCheckPkinitServerCert: keychain not found (2)"); goto errOut; } /* * We already have this cert's normalized name; get its * serial number. */ crtn = theCert->fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial); if(crtn) { /* should never happen */ tpDebug("tpCheckPkinitServerCert: error fetching serial number"); goto errOut; } attrs[0].tag = kSecSubjectItemAttr; attrs[0].length = subjectName->Length; attrs[0].data = subjectName->Data; attrs[1].tag = kSecSerialNumberItemAttr; attrs[1].length = subjSerial->Length; attrs[1].data = subjSerial->Data; attrList.count = 2; attrList.attr = attrs; ortn = SecKeychainSearchCreateFromAttributes(kcRef, kSecCertificateItemClass, &attrList, &srchRef); if(ortn) { tpDebug("tpCheckPkinitServerCert: search failure"); goto errOut; } for(;;) { ortn = SecKeychainSearchCopyNext(srchRef, &foundItem); if(ortn) { tpDebug("tpCheckPkinitServerCert: end search"); break; } /* found a matching cert; do byte-for-byte compare */ CSSM_DATA certData; ortn = SecCertificateGetData((SecCertificateRef)foundItem, &certData); if(ortn) { tpDebug("tpCheckPkinitServerCert: SecCertificateGetData failure"); continue; } if(tpCompareCssmData(&certData, theCert->itemData())){ tpDebug("tpCheckPkinitServerCert: FOUND CERT"); ourRtn = true; break; } tpDebug("tpCheckPkinitServerCert: skipping matching cert"); CFRelease(foundItem); foundItem = NULL; } errOut: CFRELEASE(kcRef); CFRELEASE(srchRef); CFRELEASE(foundItem); if(subjSerial != NULL) { theCert->freeField(&CSSMOID_X509V1SerialNumber, subjSerial); } return ourRtn; }
/* * Private version of CertGroupConstruct, used by CertGroupConstruct and * CertGroupVerify. Populates a TP-style TPCertGroup for further processing. * This only throws CSSM-style exceptions in the following cases: * * -- input parameter errors * -- the first (leaf) cert is bad (doesn't parse, expired, not valid yet). * -- root found but it doesn't self-verify * * All other cert-related errors simply result in the bad cert being ignored. * Other exceptions are gross system errors like malloc failure. */ void AppleTPSession::CertGroupConstructPriv(CSSM_CL_HANDLE clHand, CSSM_CSP_HANDLE cspHand, TPCertGroup &inCertGroup, const CSSM_DL_DB_LIST *DBList, // optional here const char *cssmTimeStr, // optional /* trusted anchors, optional */ /* FIXME - maybe this should be a TPCertGroup */ uint32 numAnchorCerts, const CSSM_DATA *anchorCerts, /* CSSM_TP_ACTION_FETCH_CERT_FROM_NET, CSSM_TP_ACTION_TRUST_SETTINGS */ CSSM_APPLE_TP_ACTION_FLAGS actionFlags, /* optional user trust parameters */ const CSSM_OID *policyOid, const char *policyStr, uint32 policyStrLen, SecTrustSettingsKeyUsage keyUse, /* * Certs to be freed by caller (i.e., TPCertInfo which we allocate * as a result of using a cert from anchorCerts or dbList) are added * to this group. */ TPCertGroup &certsToBeFreed, /* returned */ CSSM_BOOL &verifiedToRoot, // end of chain self-verifies CSSM_BOOL &verifiedToAnchor, // end of chain in anchors CSSM_BOOL &verifiedViaTrustSetting, // chain ends per User Trust setting TPCertGroup &outCertGroup) // RETURNED { TPCertInfo *subjectCert; // the one we're working on CSSM_RETURN outErr = CSSM_OK; /* this'll be the first subject cert in the main loop */ subjectCert = inCertGroup.certAtIndex(0); /* Append leaf cert to outCertGroup */ outCertGroup.appendCert(subjectCert); subjectCert->isLeaf(true); subjectCert->isFromInputCerts(true); outCertGroup.setAllUnused(); subjectCert->used(true); outErr = outCertGroup.buildCertGroup( *subjectCert, &inCertGroup, DBList, clHand, cspHand, cssmTimeStr, numAnchorCerts, anchorCerts, certsToBeFreed, &certsToBeFreed, // gatheredCerts to accumulate net/DB fetches CSSM_TRUE, // subjectIsInGroup - enables root check on // subject cert actionFlags, policyOid, policyStr, policyStrLen, keyUse, verifiedToRoot, verifiedToAnchor, verifiedViaTrustSetting); if(outErr) { CssmError::throwMe(outErr); } }
/* * Fetch issuer cert of specified cert if the cert has an issuerAltName * with a URI. If non-NULL cert is returned, it has passed subject/issuer * name comparison and signature verification with target cert. * * Return values: * CSSM_OK - found and returned issuer cert * CSSMERR_TP_CERTGROUP_INCOMPLETE - no URL in issuerAltName * CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE - found and returned issuer * cert, but signature verification needs subsequent retry. * Anything else - gross error, typically from last LDAP/HTTP attempt */ CSSM_RETURN tpFetchIssuerFromNet( TPCertInfo &subject, CSSM_CL_HANDLE clHand, CSSM_CSP_HANDLE cspHand, const char *verifyTime, TPCertInfo *&issuer) // RETURNED { CSSM_OID_PTR fieldOid = NULL; CSSM_DATA_PTR fieldValue = NULL; // mallocd by CL CSSM_RETURN crtn; bool hasAIA = false; /* look for the Authority Info Access extension first */ fieldOid = (CSSM_OID_PTR)&CSSMOID_AuthorityInfoAccess; crtn = subject.fetchField(fieldOid, &fieldValue); hasAIA = (crtn == CSSM_OK); if (!hasAIA) { /* fall back to Issuer Alternative Name extension */ fieldOid = (CSSM_OID_PTR)&CSSMOID_IssuerAltName; crtn = subject.fetchField(fieldOid, &fieldValue); } switch(crtn) { case CSSM_OK: break; case CSSMERR_CL_NO_FIELD_VALUES: /* field not present */ return CSSMERR_TP_CERTGROUP_INCOMPLETE; default: /* gross error */ return crtn; } if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) { tpPolicyError("tpFetchIssuerFromNet: malformed CSSM_FIELD"); return CSSMERR_TP_UNKNOWN_FORMAT; } CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data; CE_GeneralNames *names = (CE_GeneralNames *)cssmExt->value.parsedValue; TPCertInfo *rtnCert = NULL; if (hasAIA) { /* authority info access */ CE_AuthorityInfoAccess *access = (CE_AuthorityInfoAccess *)cssmExt->value.parsedValue; for (uint32 index = 0; access && index < access->numAccessDescriptions; index++) { CE_AccessDescription *accessDesc = &access->accessDescriptions[index]; CSSM_OID_PTR methodOid = (CSSM_OID_PTR)&accessDesc->accessMethod; /* look for the CA Issuers method */ if(methodOid->Data != NULL && methodOid->Length == CSSMOID_CA_ISSUERS.Length && !memcmp(methodOid->Data, CSSMOID_CA_ISSUERS.Data, methodOid->Length)) { CE_GeneralNames aiaNames = { 1, &accessDesc->accessLocation }; /* attempt to fetch cert from named location */ crtn = tpFetchViaGeneralNames(&aiaNames, subject, NULL, // issuer - not used NULL, // verifyContext clHand, cspHand, verifyTime, &rtnCert, NULL); if (crtn == CSSM_OK || crtn == CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE) { break; // got one } } } subject.freeField(fieldOid, fieldValue); } else { /* issuer alt name */ crtn = tpFetchViaGeneralNames(names, subject, NULL, // issuer - not used NULL, // verifyContext clHand, cspHand, verifyTime, &rtnCert, NULL); subject.freeField(fieldOid, fieldValue); } switch(crtn) { case CSSM_OK: case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: issuer = rtnCert; break; default: break; } return crtn; }
/* * Fetch CRL(s) from specified cert if the cert has a cRlDistributionPoint * extension. * * Return values: * CSSM_OK - found and returned fully verified CRL * CSSMERR_APPLETP_CRL_NOT_FOUND - no CRL in cRlDistributionPoint * Anything else - gross error, typically from last LDAP/HTTP attempt * * FIXME - this whole mechanism sort of falls apart if verifyContext.verifyTime * is non-NULL. How are we supposed to get the CRL which was valid at * a specified time in the past? */ CSSM_RETURN tpFetchCrlFromNet( TPCertInfo &cert, TPVerifyContext &vfyCtx, TPCrlInfo *&crl) // RETURNED { /* does the cert have a cRlDistributionPoint? */ CSSM_DATA_PTR fieldValue; // mallocd by CL CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints, &fieldValue); switch(crtn) { case CSSM_OK: break; case CSSMERR_CL_NO_FIELD_VALUES: /* field not present */ return CSSMERR_APPLETP_CRL_NOT_FOUND; default: /* gross error */ return crtn; } if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) { tpErrorLog("tpFetchCrlFromNet: malformed CSSM_FIELD"); return CSSMERR_TP_UNKNOWN_FORMAT; } CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data; CE_CRLDistPointsSyntax *dps = (CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue; TPCrlInfo *rtnCrl = NULL; /* default return if we don't find anything */ crtn = CSSMERR_APPLETP_CRL_NOT_FOUND; for(unsigned dex=0; dex<dps->numDistPoints; dex++) { CE_CRLDistributionPoint *dp = &dps->distPoints[dex]; if(dp->distPointName == NULL) { continue; } /* * FIXME if this uses an indirect CRL, we need to follow the * crlIssuer field... TBD. */ switch(dp->distPointName->nameType) { case CE_CDNT_NameRelativeToCrlIssuer: /* not yet */ tpErrorLog("tpFetchCrlFromNet: " "CE_CDNT_NameRelativeToCrlIssuer not implemented\n"); break; case CE_CDNT_FullName: { /* * Since we don't support indirect CRLs (yet), we always pass * the cert-to-be-verified's issuer as the CRL issuer for * cache lookup. */ CE_GeneralNames *names = dp->distPointName->dpn.fullName; crtn = tpFetchViaGeneralNames(names, cert, cert.issuerName(), &vfyCtx, 0, // clHand, use the one in vfyCtx 0, // cspHand, ditto vfyCtx.verifyTime, NULL, &rtnCrl); break; } /* CE_CDNT_FullName */ default: /* not yet */ tpErrorLog("tpFetchCrlFromNet: " "unknown distPointName->nameType (%u)\n", (unsigned)dp->distPointName->nameType); break; } /* switch distPointName->nameType */ if(crtn == CSSM_OK) { /* i.e., tpFetchViaGeneralNames SUCCEEDED */ break; } } /* for each distPoints */ cert.freeField(&CSSMOID_CrlDistributionPoints, fieldValue); if(crtn == CSSM_OK) { assert(rtnCrl != NULL); crl = rtnCrl; } return crtn; }
/* * Search a list of DBs for a cert which verifies specified subject item. * Just a boolean return - we found it, or not. If we did, we return * TPCertInfo associated with the raw cert. * A true partialIssuerKey on return indicates that caller must deal * with partial public key processing later. * If verifyCurrent is true, we will not return a cert which is not * temporally valid; else we may well do so. */ TPCertInfo *tpDbFindIssuerCert( Allocator &alloc, CSSM_CL_HANDLE clHand, CSSM_CSP_HANDLE cspHand, const TPClItemInfo *subjectItem, const CSSM_DL_DB_LIST *dbList, const char *verifyTime, // may be NULL bool &partialIssuerKey) // RETURNED { StLock<Mutex> _(SecTrustKeychainsGetMutex()); uint32 dbDex; CSSM_HANDLE resultHand; CSSM_DATA cert; CSSM_DL_DB_HANDLE dlDb; CSSM_DB_UNIQUE_RECORD_PTR record; TPCertInfo *issuerCert = NULL; bool foundIt; TPCertInfo *expiredIssuer = NULL; TPCertInfo *nonRootIssuer = NULL; partialIssuerKey = false; if(dbList == NULL) { return NULL; } for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) { dlDb = dbList->DLDBHandle[dbDex]; cert.Data = NULL; cert.Length = 0; resultHand = 0; record = tpCertLookup(dlDb, subjectItem->issuerName(), &resultHand, &cert); /* remember we have to: * -- abort this query regardless, and * -- free the CSSM_DATA cert regardless, and * -- free the unique record if we don't use it * (by placing it in issuerCert)... */ if(record != NULL) { /* Found one */ assert(cert.Data != NULL); tpDbDebug("tpDbFindIssuerCert: found cert record (1) %p", record); issuerCert = NULL; CSSM_RETURN crtn = CSSM_OK; try { issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData, verifyTime); } catch(...) { crtn = CSSMERR_TP_INVALID_CERTIFICATE; } /* we're done with raw cert data */ tpFreePluginMemory(dlDb.DLHandle, cert.Data); cert.Data = NULL; cert.Length = 0; /* Does it verify the subject cert? */ if(crtn == CSSM_OK) { crtn = subjectItem->verifyWithIssuer(issuerCert); } /* * Handle temporal invalidity - if so and this is the first one * we've seen, hold on to it while we search for better one. */ if((crtn == CSSM_OK) && (expiredIssuer == NULL)) { if(issuerCert->isExpired() || issuerCert->isNotValidYet()) { /* * Exact value not important here, this just uniquely identifies * this situation in the switch below. */ tpDbDebug("tpDbFindIssuerCert: holding expired cert (1)"); crtn = CSSM_CERT_STATUS_EXPIRED; expiredIssuer = issuerCert; expiredIssuer->dlDbHandle(dlDb); expiredIssuer->uniqueRecord(record); } } /* * Prefer a root over an intermediate issuer if we can get one * (in case a cross-signed intermediate and root are both available) */ if((crtn == CSSM_OK) && (nonRootIssuer == NULL)) { if(!issuerCert->isSelfSigned()) { /* * Exact value not important here, this just uniquely identifies * this situation in the switch below. */ tpDbDebug("tpDbFindIssuerCert: holding non-root cert (1)"); crtn = CSSM_CERT_STATUS_IS_ROOT; nonRootIssuer = issuerCert; nonRootIssuer->dlDbHandle(dlDb); nonRootIssuer->uniqueRecord(record); } } switch(crtn) { case CSSM_OK: break; case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: partialIssuerKey = true; break; default: if(issuerCert != NULL) { /* either holding onto this cert, or done with it. */ if(crtn != CSSM_CERT_STATUS_EXPIRED && crtn != CSSM_CERT_STATUS_IS_ROOT) { delete issuerCert; CSSM_DL_FreeUniqueRecord(dlDb, record); } issuerCert = NULL; } /* * Continue searching this DB. Break on finding the holy * grail or no more records found. */ for(;;) { cert.Data = NULL; cert.Length = 0; record = NULL; CSSM_RETURN crtn = CSSM_DL_DataGetNext(dlDb, resultHand, NULL, // no attrs &cert, &record); if(crtn) { /* no more, done with this DB */ assert(cert.Data == NULL); break; } assert(cert.Data != NULL); tpDbDebug("tpDbFindIssuerCert: found cert record (2) %p", record); /* found one - does it verify subject? */ try { issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData, verifyTime); } catch(...) { crtn = CSSMERR_TP_INVALID_CERTIFICATE; } /* we're done with raw cert data */ tpFreePluginMemory(dlDb.DLHandle, cert.Data); cert.Data = NULL; cert.Length = 0; if(crtn == CSSM_OK) { crtn = subjectItem->verifyWithIssuer(issuerCert); } /* temporal validity check, again */ if((crtn == CSSM_OK) && (expiredIssuer == NULL)) { if(issuerCert->isExpired() || issuerCert->isNotValidYet()) { tpDbDebug("tpDbFindIssuerCert: holding expired cert (2)"); crtn = CSSM_CERT_STATUS_EXPIRED; expiredIssuer = issuerCert; expiredIssuer->dlDbHandle(dlDb); expiredIssuer->uniqueRecord(record); } } /* self-signed check, again */ if((crtn == CSSM_OK) && (nonRootIssuer == NULL)) { if(!issuerCert->isSelfSigned()) { tpDbDebug("tpDbFindIssuerCert: holding non-root cert (2)"); crtn = CSSM_CERT_STATUS_IS_ROOT; nonRootIssuer = issuerCert; nonRootIssuer->dlDbHandle(dlDb); nonRootIssuer->uniqueRecord(record); } } foundIt = false; switch(crtn) { case CSSM_OK: foundIt = true; break; case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: partialIssuerKey = true; foundIt = true; break; default: break; } if(foundIt) { /* yes! */ break; } if(issuerCert != NULL) { /* either holding onto this cert, or done with it. */ if(crtn != CSSM_CERT_STATUS_EXPIRED && crtn != CSSM_CERT_STATUS_IS_ROOT) { delete issuerCert; CSSM_DL_FreeUniqueRecord(dlDb, record); } issuerCert = NULL; } } /* searching subsequent records */ } /* switch verify */ if(record != NULL) { /* NULL record --> end of search --> DB auto-aborted */ crtn = CSSM_DL_DataAbortQuery(dlDb, resultHand); assert(crtn == CSSM_OK); } if(issuerCert != NULL) { /* successful return */ tpDbDebug("tpDbFindIssuer: returning record %p", record); issuerCert->dlDbHandle(dlDb); issuerCert->uniqueRecord(record); if(expiredIssuer != NULL) { /* We found a replacement */ tpDbDebug("tpDbFindIssuer: discarding expired cert"); expiredIssuer->freeUniqueRecord(); delete expiredIssuer; } /* Avoid deleting the non-root cert if same as expired cert */ if(nonRootIssuer != NULL && nonRootIssuer != expiredIssuer) { /* We found a replacement */ tpDbDebug("tpDbFindIssuer: discarding non-root cert"); nonRootIssuer->freeUniqueRecord(); delete nonRootIssuer; } return issuerCert; } } /* tpCertLookup, i.e., CSSM_DL_DataGetFirst, succeeded */ else { assert(cert.Data == NULL); assert(resultHand == 0); } } /* main loop searching dbList */ if(nonRootIssuer != NULL) { /* didn't find root issuer, so use this one */ tpDbDebug("tpDbFindIssuer: taking non-root issuer cert, record %p", nonRootIssuer->uniqueRecord()); if(expiredIssuer != NULL && expiredIssuer != nonRootIssuer) { expiredIssuer->freeUniqueRecord(); delete expiredIssuer; } return nonRootIssuer; } if(expiredIssuer != NULL) { /* OK, we'll take this one */ tpDbDebug("tpDbFindIssuer: taking expired cert after all, record %p", expiredIssuer->uniqueRecord()); return expiredIssuer; } /* issuer not found */ return NULL; }