/* * 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; }
/* * 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; }
/* * 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); } }