OCSPExtensions::OCSPExtensions( NSS_CertExtension **nssExts) : mCoder(NULL), mNumExtensions(0), mExtensions(NULL) { SecAsn1CoderCreate(&mCoder); mNumExtensions = ocspdArraySize((const void **)nssExts); if(mNumExtensions == 0) { return; } mExtensions = (OCSPExtension **)SecAsn1Malloc(mCoder, (mNumExtensions * sizeof(OCSPExtension *))); for(unsigned dex=0; dex<mNumExtensions; dex++) { try { mExtensions[dex] = OCSPExtension::createFromNSS(mCoder, *nssExts[dex]); if(mExtensions[dex] == NULL) { ocspdErrorLog("OCSPExtensions: extension failure (NULL) dex %u\n", dex); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } if(mExtensions[dex]->unrecognizedCritical()) { ocspdErrorLog("OCSPExtensions: unrecognized critical extension\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } } catch (...) { ocspdErrorLog("OCSPExtensions: extension failure dex %u\n", dex); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } } }
/* * Calculate temporal validity; set mLatestNextUpdate and mExpireTime. Only * called from constructor. Returns true if valid, else returns false. */ bool OCSPResponse::calculateValidity(CFTimeInterval defaultTTL) { mLatestNextUpdate = NULL_TIME; CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); unsigned numResponses = ocspdArraySize((const void **)mResponseData.responses); for(unsigned dex=0; dex<numResponses; dex++) { SecAsn1OCSPSingleResponse *resp = mResponseData.responses[dex]; /* * First off, a thisUpdate later than 'now' invalidates the whole response. */ CFAbsoluteTime thisUpdate = genTimeToCFAbsTime(&resp->thisUpdate); if(thisUpdate > now) { ocspdErrorLog("OCSPResponse::calculateValidity: thisUpdate not passed\n"); return false; } /* * Accumulate latest nextUpdate */ if(resp->nextUpdate != NULL) { CFAbsoluteTime nextUpdate = genTimeToCFAbsTime(resp->nextUpdate); if(nextUpdate > mLatestNextUpdate) { mLatestNextUpdate = nextUpdate; } } } CFAbsoluteTime defaultExpire = now + defaultTTL; if(mLatestNextUpdate == NULL_TIME) { /* absolute expire time = current time plus default TTL */ mExpireTime = defaultExpire; } else if(defaultExpire < mLatestNextUpdate) { /* caller more stringent than response */ mExpireTime = defaultExpire; } else { /* response more stringent than caller */ if(mLatestNextUpdate < now) { ocspdErrorLog("OCSPResponse::calculateValidity: now > mLatestNextUpdate\n"); return false; } mExpireTime = mLatestNextUpdate; } return true; }
/* * Given time strings representing 'update time' and 'stale time', * calculate mIsExpired and mIsStale. */ void CrlInfo::validateTimes( const char *updateTime, // now - expireOverlap const char *staleTime, // now - staleTime unsigned dex) // for debug info { CSSM_DATA *nextUpdateData = fetchValidAttr(ATTR_DEX_NEXT_UPDATE); if((nextUpdateData == NULL) || (nextUpdateData->Length != CSSM_TIME_STRLEN)) { ocspdErrorLog("CrlInfo::validateTimes: Badly formed NextUpdate attr on " "CRL %u", dex); mIsBadlyFormed = true; return; } printString("NextUpdate ", nextUpdateData); char *nextUpdate = (char *)nextUpdateData->Data; if(compareTimes(nextUpdate, updateTime) < 0) { ocspdCrlDebug("...CRL %u is expired", dex); mIsExpired = true; if(compareTimes(nextUpdate, staleTime) < 0) { ocspdCrlDebug("...CRL %u is stale", dex); mIsStale = true; } /* note it can't be stale and not expired */ } }
/* * Constructor, called by OCSPResponse. */ OCSPSingleResponse::OCSPSingleResponse( SecAsn1OCSPSingleResponse *resp) : mCertStatus(CS_NotParsed), mThisUpdate(NULL_TIME), mNextUpdate(NULL_TIME), mRevokedTime(NULL_TIME), mCrlReason(CrlReason_NONE), mExtensions(NULL) { assert(resp != NULL); SecAsn1CoderCreate(&mCoder); if((resp->certStatus.Data == NULL) || (resp->certStatus.Length == 0)) { ocspdErrorLog("OCSPSingleResponse: bad certStatus\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } mCertStatus = (SecAsn1OCSPCertStatusTag)(resp->certStatus.Data[0] & SEC_ASN1_TAGNUM_MASK); if(mCertStatus == CS_Revoked) { /* decode further to get SecAsn1OCSPRevokedInfo */ SecAsn1OCSPCertStatus certStatus; memset(&certStatus, 0, sizeof(certStatus)); if(SecAsn1DecodeData(mCoder, &resp->certStatus, kSecAsn1OCSPCertStatusRevokedTemplate, &certStatus)) { ocspdErrorLog("OCSPSingleResponse: err decoding certStatus\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } SecAsn1OCSPRevokedInfo *revokedInfo = certStatus.revokedInfo; if(revokedInfo != NULL) { /* Treat this as optional even for CS_Revoked */ mRevokedTime = genTimeToCFAbsTime(&revokedInfo->revocationTime); const CSSM_DATA *revReason = revokedInfo->revocationReason; if((revReason != NULL) && (revReason->Data != NULL) && (revReason->Length != 0)) { mCrlReason = revReason->Data[0]; } } } mThisUpdate = genTimeToCFAbsTime(&resp->thisUpdate); if(resp->nextUpdate != NULL) { mNextUpdate = genTimeToCFAbsTime(resp->nextUpdate); } mExtensions = new OCSPExtensions(resp->singleExtensions); ocspdDebug("OCSPSingleResponse: status %d reason %d", (int)mCertStatus, (int)mCrlReason); }
/* Fill out a CSSM_DATA with the subset of public key bytes from the given * CSSM_KEY_PTR which should be hashed to produce the issuerKeyHash field * of a CertID in an OCSP request. * * For RSA keys, this simply copies the input key pointer and length. * For EC keys, we need to further deconstruct the SubjectPublicKeyInfo * to obtain the key bytes (i.e. curve point) for hashing. * * Returns CSSM_OK on success, or non-zero error if the bytes could not * be retrieved. */ CSSM_RETURN ocspdGetPublicKeyBytes( SecAsn1CoderRef coder, // optional CSSM_KEY_PTR publicKey, // input public key CSSM_DATA &publicKeyBytes) // filled in by this function { CSSM_RETURN crtn = CSSM_OK; SecAsn1CoderRef _coder = NULL; if(publicKey == NULL) { crtn = CSSMERR_CSP_INVALID_KEY_POINTER; goto exit; } if(coder == NULL) { crtn = SecAsn1CoderCreate(&_coder); if(crtn) { goto exit; } coder = _coder; } publicKeyBytes.Length = publicKey->KeyData.Length; publicKeyBytes.Data = publicKey->KeyData.Data; if(publicKey->KeyHeader.AlgorithmId == CSSM_ALGID_ECDSA) { /* * For an EC key, publicKey->KeyData is a SubjectPublicKeyInfo * ASN.1 sequence that includes the algorithm identifier. * We only want to return the bit string portion of the key here. */ SecAsn1PubKeyInfo pkinfo; memset(&pkinfo, 0, sizeof(pkinfo)); if(SecAsn1Decode(coder, publicKey->KeyData.Data, publicKey->KeyData.Length, kSecAsn1SubjectPublicKeyInfoTemplate, &pkinfo) == 0) { if(pkinfo.subjectPublicKey.Length && pkinfo.subjectPublicKey.Data) { publicKeyBytes.Length = pkinfo.subjectPublicKey.Length >> 3; publicKeyBytes.Data = pkinfo.subjectPublicKey.Data; /* * Important: if we allocated the SecAsn1Coder, the memory * being pointed to by pkinfo.subjectPublicKey.Data will be * deallocated when the coder is released below. We want to * point to the identical data inside the caller's public key, * now that the decoder has identified it for us. */ if(publicKeyBytes.Length <= publicKey->KeyData.Length) { publicKeyBytes.Data = (uint8*)((uintptr_t)publicKey->KeyData.Data + (publicKey->KeyData.Length - publicKeyBytes.Length)); goto exit; } /* intentional fallthrough to error exit */ } ocspdErrorLog("ocspdGetPublicKeyBytes: invalid SecAsn1PubKeyInfo\n"); crtn = CSSMERR_CSP_INVALID_KEY_POINTER; }
/* * Validate each CRL's integrity (Not including expiration or stale time). */ static void validateCrls( CrlInfo **crlInfo, unsigned numCrls) { CrlInfo *crl; for(unsigned dex=0; dex<numCrls; dex++) { crl = crlInfo[dex]; /* get CrlType, make sure it's acceptable */ uint32 i; if(crl->fetchIntAttr(ATTR_DEX_CRL_TYPE, i)) { continue; } switch(i) { case CSSM_CRL_TYPE_X_509v1: case CSSM_CRL_TYPE_X_509v2: /* OK */ break; default: ocspdErrorLog("validateCrls: bad CRL type (%u) on CRL %u\n", (unsigned)i, dex); crl->mIsBadlyFormed = true; continue; } /* ditto for encoding */ if(crl->fetchIntAttr(ATTR_DEX_CRL_ENC, i)) { continue; } switch(i) { case CSSM_CRL_ENCODING_BER: case CSSM_CRL_ENCODING_DER: /* OK */ break; default: ocspdErrorLog("validateCrls: bad CRL encoding (%u) on CRL %u\n", (unsigned)i, dex); crl->mIsBadlyFormed = true; continue; } /* any other grounds for deletion? */ } }
const CSSM_DATA *OCSPResponse::signerCert(uint32 dex) { uint32 numCerts = numSignerCerts(); if(dex >= numCerts) { ocspdErrorLog("OCSPResponse::signerCert: numCerts overflow\n"); CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); } return mBasicResponse.certs[dex]; }
/* * Perform full crypto CRL validation. * We use the system-wide intermediate cert keychain here, but do * NOT use the CRL cache we're working on (or any other), since * we don't really want to trust anything at this point. */ static void cryptoValidateCrls( CrlInfo **crlInfo, unsigned numCrls, CSSM_TP_HANDLE tpHand, CSSM_CSP_HANDLE cspHand, CSSM_CL_HANDLE clHand, CSSM_DL_HANDLE dlHand) { CrlInfo *crl; /* the system-wide intermediate certs */ CSSM_DL_DB_HANDLE certDb; CSSM_DL_DB_HANDLE_PTR certDbPtr = NULL; CSSM_RETURN crtn = CSSM_DL_DbOpen(dlHand, SYSTEM_CERT_STORE_PATH, NULL, // DbLocation CSSM_DB_ACCESS_READ, NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred NULL, // void *OpenParameters &certDb.DBHandle); if(crtn) { ocspdErrorLog("cryptoValidateCrls: CSSM_DL_DbOpen returned %u", (unsigned)crtn); /* Oh well, keep trying */ } else { certDb.DLHandle = dlHand; certDbPtr = &certDb; } for(unsigned dex=0; dex<numCrls; dex++) { crl = crlInfo[dex]; crtn = cuCrlVerify(tpHand, clHand, cspHand, &crl->mCrlBlob, certDbPtr, NULL, // anchors - use Trust Settings 0); // anchorCount switch(crtn) { case CSSMERR_APPLETP_CRL_EXPIRED: /* special case, we'll handle this via its attrs */ case CSSM_OK: break; default: ocspdCrlDebug("...CRL %u FAILED crypto verify", dex); crl->printName("FAILED crypto verify"); crl->mIsBadlyFormed = true; break; } } CSSM_DL_DbClose(certDb); }
/* * Fetch uint32 attr if it's there at specified attr index. * Returns non zero if it's not there and flags the CRL as bad. */ int CrlInfo::fetchIntAttr( unsigned dex, uint32 &rtn) { CSSM_DATA *val = fetchValidAttr(dex); if((val == NULL) || (val->Length != sizeof(uint32))) { ocspdErrorLog("CrlInfo::fetchIntAttr: Badly formed uint32 attr at dex %u\n", dex); mIsBadlyFormed = true; return 1; } rtn = *(uint32 *)val->Data; return 0; }
/* * Delete all stale and badly formed CRLs from cache. */ static void deleteBadCrls( CrlInfo **crlInfo, unsigned numCrls) { CrlInfo *crl; for(unsigned dex=0; dex<numCrls; dex++) { crl = crlInfo[dex]; /* * expired is not grounds for deletion; mIsStale is. */ if(crl->mIsBadlyFormed || crl->mIsStale) { crl->printName("deleting"); CSSM_RETURN crtn = CSSM_DL_DataDelete(crl->dlDbHand(), crl->record()); if(crtn) { ocspdErrorLog("deleteBadCrls: CSSM_DL_DataDelete returned %u", (unsigned)crtn); } } } }
/* * Calculate expired/stale state for all CRLs. */ int calcCurrent( CrlInfo **crlInfo, unsigned numCrls, int expireOverlapSeconds, int staleTimeSeconds) { if(expireOverlapSeconds > staleTimeSeconds) { ocspdErrorLog("calcCurrent: ExpireOverlap greater than StaleTime; aborting.\n"); return 1; } char *updateTime = cuTimeAtNowPlus(expireOverlapSeconds, TIME_CSSM); char *staleTime = cuTimeAtNowPlus(-staleTimeSeconds, TIME_CSSM); ocspdCrlDebug("updateTime : %s", updateTime); ocspdCrlDebug("staleTime : %s", staleTime); for(unsigned dex=0; dex<numCrls; dex++) { crlInfo[dex]->validateTimes(updateTime, staleTime, dex); } APP_FREE(updateTime); APP_FREE(staleTime); return 0; }
/* free attribute(s) allocated by DL */ static void freeAttrs( CSSM_DB_ATTRIBUTE_DATA *attrs, unsigned numAttrs) { unsigned i; for(i=0; i<numAttrs; i++) { CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &attrs[i]; unsigned j; for(j=0; j<attrData->NumberOfValues; j++) { CSSM_DATA_PTR data = &attrData->Value[j]; if(data == NULL) { /* fault of DL, who said there was a value here */ ocspdErrorLog("***freeAttrs screwup: NULL data\n"); return; } APP_FREE(data->Data); data->Data = NULL; data->Length = 0; } APP_FREE(attrData->Value); attrData->Value = NULL; } }
static void handle_server_response(CFReadStreamRef stream, CFStreamEventType type, void *info) { asynchttp_t *http = (asynchttp_t *)info; if (!http->stream) { secerror("Avoiding crash due to CFReadStream invoking us after we called CFReadStreamSetDispatchQueue(stream, NULL) on a different block on our serial queue"); return; } switch (type) { case kCFStreamEventHasBytesAvailable: { UInt8 buffer[POST_BUFSIZE]; CFIndex length; do { #if 1 length = CFReadStreamRead(stream, buffer, sizeof(buffer)); #else const UInt8 *buffer = CFReadStreamGetBuffer(stream, -1, &length); #endif secdebug("http", "stream: %@ kCFStreamEventHasBytesAvailable read: %lu bytes", stream, length); if (length < 0) { /* Negative length == error */ asynchttp_complete(http); break; } else if (length > 0) { //CFHTTPMessageAppendBytes(http->response, buffer, length); CFDataAppendBytes(http->data, buffer, length); } else { /* Read 0 bytes, are we are done or do we wait for kCFStreamEventEndEncountered? */ asynchttp_complete(http); break; } } while (CFReadStreamHasBytesAvailable(stream)); break; } case kCFStreamEventErrorOccurred: { CFStreamError error = CFReadStreamGetError(stream); secdebug("http", "stream: %@ kCFStreamEventErrorOccurred domain: %ld error: %ld", stream, error.domain, (long) error.error); if (error.domain == kCFStreamErrorDomainPOSIX) { ocspdErrorLog("CFReadStream posix: %s", strerror(error.error)); } else if (error.domain == kCFStreamErrorDomainMacOSStatus) { ocspdErrorLog("CFReadStream osstatus: %"PRIstatus, error.error); } else { ocspdErrorLog("CFReadStream domain: %ld error: %"PRIstatus, error.domain, error.error); } asynchttp_complete(http); break; } case kCFStreamEventEndEncountered: { http->response = (CFHTTPMessageRef)CFReadStreamCopyProperty( stream, kCFStreamPropertyHTTPResponseHeader); secdebug("http", "stream: %@ kCFStreamEventEndEncountered hdr: %@", stream, http->response); CFHTTPMessageSetBody(http->response, http->data); asynchttp_complete(http); break; } default: ocspdErrorLog("handle_server_response unexpected event type: %lu", type); break; } }
/* * Fetch attrs for all CRLs from DB. CRL blobs themselves are not fetched * unless the fetchBlobs argument is asserted. */ static CSSM_RETURN fetchAllCrls( CSSM_DL_DB_HANDLE dlDbHand, bool fetchBlobs, // fetch actual CRL data CrlInfo **&rtnCrlInfo, // RETURNED unsigned &numCrls) // RETURNED { CSSM_QUERY query; CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; CSSM_DB_UNIQUE_RECORD_PTR record = NULL; CSSM_RETURN crtn; CSSM_HANDLE resultHand; unsigned attrDex; CSSM_DB_ATTRIBUTE_DATA attrData[NUM_CRL_ATTRS]; CSSM_DATA_PTR crlDataPtr = NULL; CSSM_DATA crlData; numCrls = 0; rtnCrlInfo = NULL; /* build an ATTRIBUTE_DATA array from list attrs */ memset(attrData, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * NUM_CRL_ATTRS); for(attrDex=0; attrDex<NUM_CRL_ATTRS; attrDex++) { attrData[attrDex].Info = x509CrlRecordAttrs[attrDex]; } recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CRL; recordAttrs.NumberOfAttributes = NUM_CRL_ATTRS; recordAttrs.AttributeData = &attrData[0]; /* just search by recordType, no predicates */ query.RecordType = CSSM_DL_DB_RECORD_X509_CRL; query.Conjunctive = CSSM_DB_NONE; query.NumSelectionPredicates = 0; query.SelectionPredicate = NULL; query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used? if(fetchBlobs) { crlDataPtr = &crlData; } crtn = CSSM_DL_DataGetFirst(dlDbHand, &query, &resultHand, &recordAttrs, crlDataPtr, &record); switch(crtn) { case CSSM_OK: break; // proceed case CSSMERR_DL_ENDOFDATA: /* we're done here */ return CSSM_OK; case CSSMERR_DL_INVALID_RECORDTYPE: /* this means that this keychain hasn't been initialized * for CRL schema; treat it as empty. */ return CSSM_OK; default: ocspdErrorLog("fetchAllCrls: DataGetFirst returned %u", (unsigned)crtn); return crtn; } /* Cook up a CrlInfo, add it to outgoing array */ CrlInfo *crlInfo = new CrlInfo(dlDbHand, &attrData[0], record, crlDataPtr); rtnCrlInfo = (CrlInfo **)malloc(sizeof(CrlInfo*)); rtnCrlInfo[0] = crlInfo; numCrls++; /* now the rest of them */ for(;;) { crtn = CSSM_DL_DataGetNext(dlDbHand, resultHand, &recordAttrs, crlDataPtr, &record); switch(crtn) { case CSSM_OK: rtnCrlInfo = (CrlInfo **)realloc(rtnCrlInfo, sizeof(CrlInfo *) * (numCrls + 1)); rtnCrlInfo[numCrls] = new CrlInfo(dlDbHand, &attrData[0], record, crlDataPtr); numCrls++; break; // and go again case CSSMERR_DL_ENDOFDATA: /* normal termination */ return CSSM_OK; default: ocspdErrorLog("fetchAllCrls: DataGetNext returned %u", (unsigned)crtn); return crtn; } } /* not reached */ }
OCSPResponse::OCSPResponse( const CSSM_DATA &resp, CFTimeInterval defaultTTL) // default time-to-live in seconds : mLatestNextUpdate(NULL_TIME), mExpireTime(NULL_TIME), mExtensions(NULL) { SecAsn1CoderCreate(&mCoder); memset(&mTopResp, 0, sizeof(mTopResp)); memset(&mBasicResponse, 0, sizeof(mBasicResponse)); memset(&mResponseData, 0, sizeof(mResponseData)); memset(&mResponderId, 0, sizeof(mResponderId)); mResponderIdTag = (SecAsn1OCSPResponderIDTag)0; // invalid mEncResponderName.Data = NULL; mEncResponderName.Length = 0; if(SecAsn1DecodeData(mCoder, &resp, kSecAsn1OCSPResponseTemplate, &mTopResp)) { ocspdErrorLog("OCSPResponse: decode failure at top level\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } /* remainder is valid only on RS_Success */ if((mTopResp.responseStatus.Data == NULL) || (mTopResp.responseStatus.Length == 0)) { ocspdErrorLog("OCSPResponse: no responseStatus\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } if(mTopResp.responseStatus.Data[0] != RS_Success) { /* not a failure of our constructor; this object is now useful, but * only for this one byte of status info */ return; } if(mTopResp.responseBytes == NULL) { /* I don't see how this can be legal on RS_Success */ ocspdErrorLog("OCSPResponse: empty responseBytes\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } if(!ocspdCompareCssmData(&mTopResp.responseBytes->responseType, &CSSMOID_PKIX_OCSP_BASIC)) { ocspdErrorLog("OCSPResponse: unknown responseType\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } /* decode the SecAsn1OCSPBasicResponse */ if(SecAsn1DecodeData(mCoder, &mTopResp.responseBytes->response, kSecAsn1OCSPBasicResponseTemplate, &mBasicResponse)) { ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPBasicResponse\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } /* signature and cert evaluation done externally */ /* decode the SecAsn1OCSPResponseData */ if(SecAsn1DecodeData(mCoder, &mBasicResponse.tbsResponseData, kSecAsn1OCSPResponseDataTemplate, &mResponseData)) { ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPResponseData\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } if(mResponseData.responderID.Data == NULL) { ocspdErrorLog("OCSPResponse: bad responderID\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } /* choice processing for ResponderID */ mResponderIdTag = (SecAsn1OCSPResponderIDTag) (mResponseData.responderID.Data[0] & SEC_ASN1_TAGNUM_MASK); const SecAsn1Template *templ; switch(mResponderIdTag) { case RIT_Name: templ = kSecAsn1OCSPResponderIDAsNameTemplate; break; case RIT_Key: templ = kSecAsn1OCSPResponderIDAsKeyTemplate; break; default: ocspdErrorLog("OCSPResponse: bad responderID tag\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } if(SecAsn1DecodeData(mCoder, &mResponseData.responderID, templ, &mResponderId)) { ocspdErrorLog("OCSPResponse: decode failure at responderID\n"); CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } /* check temporal validity */ if(!calculateValidity(defaultTTL)) { /* Whoops, abort */ CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE); } /* * Individual responses looked into when we're asked for a specific one * via singleResponse() */ mExtensions = new OCSPExtensions(mResponseData.responseExtensions); }
/* * For each expired CRL, fetch a new one if we don't have a current * CRL from the same place. */ static void refreshExpiredCrls( CrlInfo **crlInfo, unsigned numCrls, CSSM_CL_HANDLE clHand) { CrlInfo *crl; bool haveCurrent; CSSM_DATA newCrl; for(unsigned dex=0; dex<numCrls; dex++) { crl = crlInfo[dex]; if(!crl->mIsExpired || crl->mRefreshed) { continue; } /* do we have one for the same issuer that's current? */ haveCurrent = false; for(unsigned i=0; i<numCrls; i++) { if(i == dex) { /* skip identity */ continue; } CrlInfo *checkCrl = crlInfo[i]; if(checkCrl->mIsBadlyFormed) { /* forget this one */ continue; } if(checkCrl->mIsExpired && !checkCrl->mRefreshed) { continue; } if(crl->isSameIssuer(checkCrl)) { /* have a match; this one's OK */ ocspdCrlDebug("up-to-date CRL at dex %u matching expired CRL %u", i, dex); haveCurrent = true; break; } } if(haveCurrent) { continue; } /* * Not all CRLs have a URI attribute, which is required for * refresh */ CSSM_DATA_PTR uri = crl->fetchValidAttr(ATTR_DEX_URI); if(uri == NULL) { ocspdCrlDebug("Expired CRL with no URI at dex %u", dex); continue; } /* fetch a new one */ crl->printName("fetching new"); Allocator &alloc = Allocator::standard(); CSSM_RETURN crtn = ocspdNetFetch(alloc, *uri, LT_Crl, newCrl); if(crtn) { ocspdErrorLog("ocspdNetFetch returned %u", (unsigned)crtn); continue; } /* store it in the DB */ crtn = cuAddCrlToDb(crl->dlDbHand(), clHand, &newCrl, uri); alloc.free(newCrl.Data); /* * One special error case - UNIQUE_INDEX_DATA indicates that * the CRL we just fetched is already in the cache. This * can occur when expireOverlap is sufficiently large that * we decide to fetch before a CRL is actually expired. In * this case process as usual, avoiding any further updates * from this CA/URI. */ switch(crtn) { case CSSM_OK: ocspdCrlDebug("...refreshed CRL added to DB to account " "for expired CRL %u", dex); break; case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: ocspdCrlDebug("...refreshed CRL is a dup of CRL %u; skipping", dex); break; default: continue; } /* * In case there are other CRLs still to be discovered * in our list which are a) expired, and b) from this same issuer, * we flag the current (expired) CRL as refreshed to ensure that * we don't do this fetch again. A lot easier than cooking up * a new CrlInfo object for the CRL we just fetched. */ crl->mRefreshed = true; } }
CSSM_RETURN ocspdCrlRefresh( CSSM_DL_DB_HANDLE dlDbHand, CSSM_CL_HANDLE clHand, unsigned staleDays, unsigned expireOverlapSeconds, bool purgeAll, bool fullCryptoVerify, bool doRefresh) // false: just purge stale entries { CSSM_TP_HANDLE tpHand = 0; CSSM_CSP_HANDLE cspHand = 0; CSSM_RETURN crtn = CSSM_OK; assert((dlDbHand.DLHandle != 0) && (dlDbHand.DBHandle != 0) && (clHand != 0)); if(staleDays == 0) { staleDays = DEFAULT_STALE_DAYS; } if(expireOverlapSeconds == 0) { expireOverlapSeconds = DEFAULT_EXPIRE_OVERLAP_SECONDS; } if(fullCryptoVerify) { /* also need TP, CSP */ cspHand = attachCommon(&gGuidAppleCSP, CSSM_SERVICE_CSP); if(cspHand == 0) { Syslog::alert("Error loading AppleCSP"); return CSSMERR_CSSM_ADDIN_LOAD_FAILED; } tpHand = attachCommon(&gGuidAppleX509TP, CSSM_SERVICE_TP); if(tpHand == 0) { Syslog::alert("Error loading AppleX509TP"); crtn = CSSMERR_CSSM_ADDIN_LOAD_FAILED; goto errOut; } } /* subsequent errors to errOut: */ /* fetch all CRLs from the keychain */ CrlInfo **crlInfo; unsigned numCrls; crtn = fetchAllCrls(dlDbHand, fullCryptoVerify, crlInfo, numCrls); if(crtn) { ocspdErrorLog("ocspdCrlRefresh: Error reading CRLs."); return crtn; } ocspdCrlDebug("ocspdCrlRefresh: %u CRLs found", numCrls); /* basic validation */ validateCrls(crlInfo, numCrls); /* Optional full crypto validation */ if(fullCryptoVerify) { cryptoValidateCrls(crlInfo, numCrls, tpHand, cspHand, clHand, dlDbHand.DLHandle); } /* update the validity time flags on the CRLs */ if(calcCurrent(crlInfo, numCrls, expireOverlapSeconds, staleDays * SECONDS_PER_DAY)) { ocspdErrorLog("ocspdCrlRefresh: Error calculating CRL times."); crtn = CSSMERR_TP_INTERNAL_ERROR; goto errOut; } if(purgeAll) { /* mark all of them stale */ purgeAllCrls(crlInfo, numCrls); } /* * Delete all bad CRLs from DB. We do this before the refresh in * case of the purgeAll option, in which case we really want to * insert newly fetched CRLs in the DB even if they appear to * be trhe same as the ones they're replacing. */ deleteBadCrls(crlInfo, numCrls); /* refresh the out-of-date CRLs */ if(doRefresh) { refreshExpiredCrls(crlInfo, numCrls, clHand); } /* clean up */ for(unsigned dex=0; dex<numCrls; dex++) { delete crlInfo[dex]; } free(crlInfo); errOut: if(cspHand) { detachCommon(&gGuidAppleCSP, cspHand); } if(tpHand) { detachCommon(&gGuidAppleX509TP, tpHand); } return crtn; }