SecRevocationServer.c [plain text]
#include <AssertMacros.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecInternal.h>
#include <utilities/debugging.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecIOFormat.h>
#include <securityd/SecTrustServer.h>
#include <securityd/SecOCSPRequest.h>
#include <securityd/SecOCSPResponse.h>
#include <securityd/asynchttp.h>
#include <securityd/SecOCSPCache.h>
#include <securityd/SecRevocationDb.h>
#include <securityd/SecCertificateServer.h>
#include <securityd/SecRevocationServer.h>
const CFAbsoluteTime kSecDefaultOCSPResponseTTL = 24.0 * 60.0 * 60.0;
const CFAbsoluteTime kSecOCSPResponseOnlineTTL = 5.0 * 60.0;
#define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
struct OpaqueSecORVC {
asynchttp_t http;
SecPathBuilderRef builder;
SecRVCRef rvc;
SecOCSPRequestRef ocspRequest;
SecOCSPResponseRef ocspResponse;
SecOCSPSingleResponseRef ocspSingleResponse;
CFIndex certIX;
CFIndex responderIX;
CFURLRef responder;
CFAbsoluteTime nextUpdate;
bool done;
};
static void SecORVCFinish(SecORVCRef orvc) {
secdebug("alloc", "%p", orvc);
asynchttp_free(&orvc->http);
if (orvc->ocspRequest) {
SecOCSPRequestFinalize(orvc->ocspRequest);
orvc->ocspRequest = NULL;
}
if (orvc->ocspResponse) {
SecOCSPResponseFinalize(orvc->ocspResponse);
orvc->ocspResponse = NULL;
if (orvc->ocspSingleResponse) {
SecOCSPSingleResponseDestroy(orvc->ocspSingleResponse);
orvc->ocspSingleResponse = NULL;
}
}
}
#define MAX_OCSP_RESPONDERS 3
#define OCSP_REQUEST_THRESHOLD 10
static CFURLRef SecORVCGetNextResponder(SecORVCRef rvc) {
SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
if (ocspResponders) {
CFIndex responderCount = CFArrayGetCount(ocspResponders);
if (responderCount >= OCSP_REQUEST_THRESHOLD) {
secnotice("rvc", "too many ocsp responders (%ld)", (long)responderCount);
return NULL;
}
while (rvc->responderIX < responderCount && rvc->responderIX < MAX_OCSP_RESPONDERS) {
CFURLRef responder = CFArrayGetValueAtIndex(ocspResponders, rvc->responderIX);
rvc->responderIX++;
CFStringRef scheme = CFURLCopyScheme(responder);
if (scheme) {
bool valid_responder = (CFEqual(CFSTR("http"), scheme) ||
CFEqual(CFSTR("https"), scheme));
CFRelease(scheme);
if (valid_responder)
return responder;
}
}
}
return NULL;
}
static bool SecORVCFetchNext(SecORVCRef rvc) {
while ((rvc->responder = SecORVCGetNextResponder(rvc))) {
CFDataRef request = SecOCSPRequestGetDER(rvc->ocspRequest);
if (!request)
goto errOut;
secinfo("rvc", "Sending http ocsp request for cert %ld", rvc->certIX);
if (!asyncHttpPost(rvc->responder, request, OCSP_RESPONSE_TIMEOUT, &rvc->http)) {
return false;
}
}
errOut:
rvc->done = true;
return true;
}
static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef this,
SecORVCRef rvc) {
bool processed;
switch (this->certStatus) {
case CS_Good:
secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex, rvc->certIX);
rvc->nextUpdate = this->nextUpdate == NULL_TIME ? this->thisUpdate + kSecDefaultOCSPResponseTTL : this->nextUpdate;
processed = true;
break;
case CS_Revoked:
secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex, rvc->certIX);
SInt32 reason = this->crlReason;
CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
cfreason, true, kSecTrustResultFatalTrustFailure);
if (rvc->builder) {
CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
if (info) {
CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
}
}
CFRelease(cfreason);
processed = true;
break;
case CS_Unknown:
secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex, rvc->certIX);
processed = false;
break;
default:
secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex,
(int)this->certStatus, rvc->certIX);
processed = false;
break;
}
return processed;
}
static void SecORVCUpdatePVC(SecORVCRef rvc) {
if (rvc->ocspSingleResponse) {
SecOCSPSingleResponseProcess(rvc->ocspSingleResponse, rvc);
}
if (rvc->ocspResponse) {
rvc->nextUpdate = SecOCSPResponseGetExpirationTime(rvc->ocspResponse);
}
}
typedef void (^SecOCSPEvaluationCompleted)(SecTrustResultType tr);
static void
SecOCSPEvaluateCompleted(const void *userData,
SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
SecTrustResultType result) {
SecOCSPEvaluationCompleted evaluated = (SecOCSPEvaluationCompleted)userData;
evaluated(result);
Block_release(evaluated);
}
static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc, CFArrayRef signers, CFArrayRef issuers, CFAbsoluteTime verifyTime) {
__block bool evaluated = false;
bool trusted = false;
if (!signers || !issuers) {
return trusted;
}
const void *ocspSigner = SecPolicyCreateOCSPSigner();
CFArrayRef policies = CFArrayCreate(kCFAllocatorDefault,
&ocspSigner, 1, &kCFTypeArrayCallBacks);
CFRelease(ocspSigner);
SecOCSPEvaluationCompleted completed = Block_copy(^(SecTrustResultType result) {
if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
evaluated = true;
}
});
CFDataRef clientAuditToken = SecPathBuilderCopyClientAuditToken(rvc->builder);
SecPathBuilderRef oBuilder = SecPathBuilderCreate(clientAuditToken,
signers, issuers, true, false,
policies, NULL, NULL, NULL,
verifyTime, NULL, NULL,
SecOCSPEvaluateCompleted, completed);
SecPathBuilderStep(oBuilder);
CFReleaseNull(clientAuditToken);
CFReleaseNull(policies);
if (evaluated) {
SecCertificateRef issuer = NULL, signer = NULL;
SecKeyRef issuerPubKey = NULL;
issuer = (SecCertificateRef)CFArrayGetValueAtIndex(issuers, 0);
signer = (SecCertificateRef)CFArrayGetValueAtIndex(signers, 0);
if (issuer) {
#if TARGET_OS_IPHONE
issuerPubKey = SecCertificateCopyPublicKey(issuer);
#else
issuerPubKey = SecCertificateCopyPublicKey_ios(issuer);
#endif
}
if (signer && issuerPubKey && (errSecSuccess == SecCertificateIsSignedBy(signer, issuerPubKey))) {
trusted = true;
} else {
secnotice("ocsp", "ocsp signer cert not signed by issuer");
}
CFReleaseNull(issuerPubKey);
}
return trusted;
}
static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse, SecORVCRef rvc, CFAbsoluteTime verifyTime) {
bool trusted;
SecCertificatePathVCRef issuers = SecCertificatePathVCCopyFromParent(SecPathBuilderGetPath(rvc->builder), rvc->certIX + 1);
SecCertificateRef issuer = issuers ? CFRetainSafe(SecCertificatePathVCGetCertificateAtIndex(issuers, 0)) : NULL;
CFArrayRef signers = SecOCSPResponseCopySigners(ocspResponse);
SecCertificateRef signer = SecOCSPResponseCopySigner(ocspResponse, issuer);
if (signer && signers) {
if (issuer && CFEqual(signer, issuer)) {
secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
rvc->responder);
trusted = true;
} else {
secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
rvc->responder);
CFMutableArrayRef signerCerts = NULL;
CFArrayRef issuerCerts = NULL;
signerCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(signerCerts, signer);
CFArrayAppendArray(signerCerts, signers, CFRangeMake(0, CFArrayGetCount(signers)));
if (issuers) {
issuerCerts = SecCertificatePathVCCopyCertificates(issuers);
}
if (SecOCSPResponseEvaluateSigner(rvc, signerCerts, issuerCerts, verifyTime)) {
secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
rvc->responder);
trusted = true;
} else {
secnotice("ocsp", "ocsp response signed by certificate which "
"does not satisfy ocspSigner policy");
trusted = false;
}
CFReleaseNull(signerCerts);
CFReleaseNull(issuerCerts);
}
} else {
secnotice("ocsp", "ocsp responder: %@ no signer found for response",
rvc->responder);
trusted = false;
}
#if DUMP_OCSPRESPONSES
char buf[40];
snprintf(buf, 40, "/tmp/ocspresponse%ld%s.der",
rvc->certIX, (trusted ? "t" : "u"));
secdumpdata(ocspResponse->data, buf);
#endif
CFReleaseNull(issuers);
CFReleaseNull(issuer);
CFReleaseNull(signers);
CFReleaseNull(signer);
return trusted;
}
static void SecORVCConsumeOCSPResponse(SecORVCRef rvc, SecOCSPResponseRef ocspResponse , CFTimeInterval maxAge, bool updateCache) {
SecOCSPSingleResponseRef sr = NULL;
require_quiet(ocspResponse, errOut);
SecOCSPResponseStatus orStatus = SecOCSPGetResponseStatus(ocspResponse);
require_action_quiet(orStatus == kSecOCSPSuccess, errOut,
secnotice("ocsp", "responder: %@ returned status: %d", rvc->responder, orStatus));
require_action_quiet(sr = SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest), errOut,
secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc->responder));
require_quiet(!rvc->ocspSingleResponse || rvc->ocspSingleResponse->thisUpdate < sr->thisUpdate, errOut);
CFAbsoluteTime verifyTime = CFAbsoluteTimeGetCurrent();
require_quiet(SecOCSPResponseVerify(ocspResponse, rvc,
sr->certStatus == CS_Revoked ? SecOCSPResponseProducedAt(ocspResponse) : verifyTime), errOut);
bool sr_valid = SecOCSPSingleResponseCalculateValidity(sr, kSecDefaultOCSPResponseTTL, verifyTime);
if (sr->certStatus == CS_Good) {
require_quiet(sr_valid && SecOCSPResponseCalculateValidity(ocspResponse, maxAge, kSecDefaultOCSPResponseTTL, verifyTime), errOut);
} else if (sr->certStatus == CS_Revoked) {
ocspResponse->expireTime = SecCertificateNotValidAfter(SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX));
}
if (updateCache)
SecOCSPCacheReplaceResponse(rvc->ocspResponse, ocspResponse, rvc->responder, verifyTime);
if (rvc->ocspResponse) SecOCSPResponseFinalize(rvc->ocspResponse);
rvc->ocspResponse = ocspResponse;
ocspResponse = NULL;
if (rvc->ocspSingleResponse) SecOCSPSingleResponseDestroy(rvc->ocspSingleResponse);
rvc->ocspSingleResponse = sr;
sr = NULL;
rvc->done = sr_valid;
errOut:
if (sr) SecOCSPSingleResponseDestroy(sr);
if (ocspResponse) SecOCSPResponseFinalize(ocspResponse);
}
static void SecOCSPFetchCompleted(asynchttp_t *http, CFTimeInterval maxAge) {
SecORVCRef rvc = (SecORVCRef)http->info;
SecPathBuilderRef builder = rvc->builder;
SecOCSPResponseRef ocspResponse = NULL;
if (http->response) {
CFDataRef data = CFHTTPMessageCopyBody(http->response);
if (data) {
ocspResponse = SecOCSPResponseCreate(data);
CFRelease(data);
}
}
SecORVCConsumeOCSPResponse(rvc, ocspResponse, maxAge, true);
if (!rvc->done) {
asynchttp_free(http);
SecORVCFetchNext(rvc);
}
if (rvc->done) {
secdebug("rvc", "got OCSP response for cert: %ld", rvc->certIX);
SecORVCUpdatePVC(rvc);
if (!SecPathBuilderDecrementAsyncJobCount(builder)) {
secdebug("rvc", "done with all async jobs");
SecPathBuilderStep(builder);
}
}
}
static SecORVCRef SecORVCCreate(SecRVCRef rvc, SecPathBuilderRef builder, CFIndex certIX) {
SecORVCRef orvc = NULL;
orvc = malloc(sizeof(struct OpaqueSecORVC));
if (orvc) {
memset(orvc, 0, sizeof(struct OpaqueSecORVC));
orvc->builder = builder;
orvc->rvc = rvc;
orvc->certIX = certIX;
orvc->http.queue = SecPathBuilderGetQueue(builder);
orvc->http.token = SecPathBuilderCopyClientAuditToken(builder);
orvc->http.completed = SecOCSPFetchCompleted;
orvc->http.info = orvc;
orvc->ocspRequest = NULL;
orvc->responderIX = 0;
orvc->responder = NULL;
orvc->nextUpdate = NULL_TIME;
orvc->ocspResponse = NULL;
orvc->ocspSingleResponse = NULL;
orvc->done = false;
SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(builder, certIX);
if (SecPathBuilderGetCertificateCount(builder) > (certIX + 1)) {
SecCertificateRef issuer = SecPathBuilderGetCertificateAtIndex(builder, certIX + 1);
orvc->ocspRequest = SecOCSPRequestCreate(cert, issuer);
}
}
return orvc;
}
static void SecORVCProcessStapledResponses(SecORVCRef rvc) {
CFArrayRef ocspResponsesData = SecPathBuilderCopyOCSPResponses(rvc->builder);
if(ocspResponsesData) {
secdebug("rvc", "Checking stapled responses for cert %ld", rvc->certIX);
CFArrayForEach(ocspResponsesData, ^(const void *value) {
SecOCSPResponseRef ocspResponse = SecOCSPResponseCreate(value);
SecORVCConsumeOCSPResponse(rvc, ocspResponse, NULL_TIME, false);
});
CFRelease(ocspResponsesData);
}
}
#if ENABLE_CRLS
#include <../trustd/macOS/SecTrustOSXEntryPoints.h>
#define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
struct OpaqueSecCRVC {
async_ocspd_t async_ocspd;
SecPathBuilderRef builder;
SecRVCRef rvc;
OSStatus status;
CFIndex certIX;
CFIndex distributionPointIX;
CFURLRef distributionPoint;
CFAbsoluteTime nextUpdate;
bool done;
};
static void SecCRVCFinish(SecCRVCRef crvc) {
}
#define MAX_CRL_DPS 3
#define CRL_REQUEST_THRESHOLD 10
static CFURLRef SecCRVCGetNextDistributionPoint(SecCRVCRef rvc) {
SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
CFArrayRef crlDPs = SecCertificateGetCRLDistributionPoints(cert);
if (crlDPs) {
CFIndex crlDPCount = CFArrayGetCount(crlDPs);
if (crlDPCount >= CRL_REQUEST_THRESHOLD) {
secnotice("rvc", "too many CRL DP entries (%ld)", (long)crlDPCount);
return NULL;
}
while (rvc->distributionPointIX < crlDPCount && rvc->distributionPointIX < MAX_CRL_DPS) {
CFURLRef distributionPoint = CFArrayGetValueAtIndex(crlDPs, rvc->distributionPointIX);
rvc->distributionPointIX++;
CFStringRef scheme = CFURLCopyScheme(distributionPoint);
if (scheme) {
bool valid_DP = (CFEqual(CFSTR("http"), scheme) ||
CFEqual(CFSTR("https"), scheme) ||
CFEqual(CFSTR("ldap"), scheme));
CFRelease(scheme);
if (valid_DP)
return distributionPoint;
}
}
}
return NULL;
}
static void SecCRVCGetCRLStatus(SecCRVCRef rvc) {
SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
SecCertificatePathRef nonVCpath = SecCertificatePathVCCopyCertificatePath(path);
CFArrayRef serializedCertPath = SecCertificatePathCreateSerialized(nonVCpath, NULL);
CFReleaseNull(nonVCpath);
secdebug("rvc", "searching CRL cache for cert: %ld", rvc->certIX);
rvc->status = SecTrustLegacyCRLStatus(cert, serializedCertPath, rvc->distributionPoint);
CFReleaseNull(serializedCertPath);
if (rvc->status == errSecSuccess || rvc->status == errSecCertificateRevoked) {
rvc->done = true;
rvc->nextUpdate = SecPathBuilderGetVerifyTime(rvc->builder) + kSecDefaultCRLTTL;
}
}
static void SecCRVCCheckRevocationCache(SecCRVCRef rvc) {
while ((rvc->distributionPoint = SecCRVCGetNextDistributionPoint(rvc))) {
SecCRVCGetCRLStatus(rvc);
if (rvc->status == errSecCertificateRevoked) {
return;
}
}
}
static bool SecCRVCFetchNext(SecCRVCRef rvc) {
while ((rvc->distributionPoint = SecCRVCGetNextDistributionPoint(rvc))) {
SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
SecCertificatePathRef nonVCpath = SecCertificatePathVCCopyCertificatePath(path);
CFArrayRef serializedCertPath = SecCertificatePathCreateSerialized(nonVCpath, NULL);
CFReleaseNull(nonVCpath);
secinfo("rvc", "fetching CRL for cert: %ld", rvc->certIX);
if (!SecTrustLegacyCRLFetch(&rvc->async_ocspd, rvc->distributionPoint,
CFAbsoluteTimeGetCurrent(), cert, serializedCertPath)) {
CFDataRef clientAuditToken = NULL;
SecTaskRef task = NULL;
audit_token_t auditToken = {};
clientAuditToken = SecPathBuilderCopyClientAuditToken(rvc->builder);
require(clientAuditToken, out);
require(sizeof(auditToken) == CFDataGetLength(clientAuditToken), out);
CFDataGetBytes(clientAuditToken, CFRangeMake(0, sizeof(auditToken)), (uint8_t *)&auditToken);
require(task = SecTaskCreateWithAuditToken(NULL, auditToken), out);
secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
rvc->distributionPoint, task);
out:
CFReleaseNull(clientAuditToken);
CFReleaseNull(task);
return false;
}
}
rvc->done = true;
return true;
}
static void SecCRVCUpdatePVC(SecCRVCRef rvc) {
if (rvc->status == errSecCertificateRevoked) {
secdebug("rvc", "CRL revoked cert %" PRIdCFIndex, rvc->certIX);
SInt32 reason = 0; CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
cfreason, true, kSecTrustResultFatalTrustFailure);
if (rvc->builder) {
CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
if (info) {
CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
}
}
CFReleaseNull(cfreason);
}
}
static void SecCRVCFetchCompleted(async_ocspd_t *ocspd) {
SecCRVCRef rvc = ocspd->info;
SecPathBuilderRef builder = rvc->builder;
if (ocspd->response == errSecSuccess || ocspd->response == errSecCertificateRevoked) {
rvc->status = ocspd->response;
rvc->done = true;
rvc->nextUpdate = SecPathBuilderGetVerifyTime(rvc->builder) + kSecDefaultCRLTTL;
secdebug("rvc", "got CRL response for cert: %ld", rvc->certIX);
SecCRVCUpdatePVC(rvc);
if (!SecPathBuilderDecrementAsyncJobCount(builder)) {
secdebug("rvc", "done with all async jobs");
SecPathBuilderStep(builder);
}
} else {
if(SecCRVCFetchNext(rvc)) {
if (!SecPathBuilderDecrementAsyncJobCount(builder)) {
secdebug("rvc", "done with all async jobs");
SecPathBuilderStep(builder);
}
}
}
}
static SecCRVCRef SecCRVCCreate(SecRVCRef rvc, SecPathBuilderRef builder, CFIndex certIX) {
SecCRVCRef crvc = NULL;
crvc = malloc(sizeof(struct OpaqueSecCRVC));
if (crvc) {
memset(crvc, 0, sizeof(struct OpaqueSecCRVC));
crvc->builder = builder;
crvc->rvc = rvc;
crvc->certIX = certIX;
crvc->status = errSecInternal;
crvc->distributionPointIX = 0;
crvc->distributionPoint = NULL;
crvc->nextUpdate = NULL_TIME;
crvc->async_ocspd.queue = SecPathBuilderGetQueue(builder);
crvc->async_ocspd.completed = SecCRVCFetchCompleted;
crvc->async_ocspd.response = errSecInternal;
crvc->async_ocspd.info = crvc;
crvc->done = false;
}
return crvc;
}
static bool SecRVCShouldCheckCRL(SecRVCRef rvc) {
CFStringRef revocation_method = SecPathBuilderGetRevocationMethod(rvc->builder);
if (revocation_method &&
CFEqual(kSecPolicyCheckRevocationCRL, revocation_method)) {
secinfo("rvc", "client told us to check CRL");
return true;
}
SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
if ((!ocspResponders || CFArrayGetCount(ocspResponders) == 0) &&
(revocation_method && !CFEqual(kSecPolicyCheckRevocationOCSP, revocation_method))) {
secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc->certIX);
return true;
}
return false;
}
#endif
void SecRVCDelete(SecRVCRef rvc) {
if (rvc->orvc) {
SecORVCFinish(rvc->orvc);
free(rvc->orvc);
rvc->orvc = NULL;
}
#if ENABLE_CRLS
if (rvc->crvc) {
SecCRVCFinish(rvc->crvc);
free(rvc->crvc);
rvc->crvc = NULL;
}
#endif
if (rvc->valid_info) {
SecValidInfoRelease(rvc->valid_info);
rvc->valid_info = NULL;
}
}
static void SecRVCInit(SecRVCRef rvc, SecPathBuilderRef builder, CFIndex certIX) {
secdebug("alloc", "%p", rvc);
rvc->builder = builder;
rvc->certIX = certIX;
rvc->orvc = SecORVCCreate(rvc, builder, certIX);
#if ENABLE_CRLS
rvc->crvc = SecCRVCCreate(rvc, builder, certIX);
#endif
if (!rvc->orvc
#if ENABLE_CRLS
|| !rvc->crvc
#endif
) {
SecRVCDelete(rvc);
rvc->done = true;
} else {
rvc->done = false;
}
}
#if ENABLE_CRLS
static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
CFStringRef revocation_method = SecPathBuilderGetRevocationMethod(rvc->builder);
if (!revocation_method
|| !CFEqual(revocation_method, kSecPolicyCheckRevocationCRL)) {
return true;
}
return false;
}
#else
static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
return true;
}
#endif
static void SecRVCProcessValidInfoResults(SecRVCRef rvc) {
if (!rvc || !rvc->valid_info || !rvc->builder) {
return;
}
SecValidInfoFormat format = rvc->valid_info->format;
bool valid = rvc->valid_info->valid;
bool noCACheck = rvc->valid_info->noCACheck;
bool checkOCSP = rvc->valid_info->checkOCSP;
bool complete = rvc->valid_info->complete;
bool isOnList = rvc->valid_info->isOnList;
bool definitive = false;
if (format == kSecValidInfoFormatSerial || format == kSecValidInfoFormatSHA256) {
if (((!valid && complete && isOnList) || (valid && complete && !isOnList)) && noCACheck) {
SInt32 reason = 0;
CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
cfreason, true, kSecTrustResultFatalTrustFailure);
CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
if (info) {
CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
}
CFReleaseNull(cfreason);
definitive = true;
}
else if (valid && complete && isOnList && noCACheck) {
SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
if (path) {
SecCertificatePathVCSetIsAllowlisted(path, true);
} else {
secdebug("validupdate", "rvc: no certificate path for builder");
}
definitive = true;
}
if (definitive) {
secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex,
(valid && complete && isOnList) ? "allowed" : "revoked", rvc->certIX);
rvc->done = true;
return;
}
checkOCSP = true;
}
if (format == kSecValidInfoFormatNto1) {
checkOCSP = true;
}
if (checkOCSP) {
CFIndex count = SecPathBuilderGetCertificateCount(rvc->builder);
CFIndex issuerIX = rvc->certIX + 1;
if (issuerIX >= count) {
return;
}
CFIndex pvcIX;
for (pvcIX = 0; pvcIX < SecPathBuilderGetPVCCount(rvc->builder); pvcIX++) {
SecPVCRef pvc = SecPathBuilderGetPVCAtIndex(rvc->builder, pvcIX);
if (!pvc) { continue; }
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, 0);
CFStringRef policyName = (policy) ? SecPolicyGetName(policy) : NULL;
if (policyName && CFEqual(CFSTR("sslServer"), policyName)) {
if (0 == rvc->certIX) {
SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
CFArrayRef resps = (cert) ? SecCertificateGetOCSPResponders(cert) : NULL;
CFIndex rcount = (resps) ? CFArrayGetCount(resps) : 0;
if (rcount > 0) {
}
}
secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex " (will check OCSP)",
(complete) ? "" : "possibly ", (valid) ? "allowed" : "revoked",
rvc->certIX);
SecPathBuilderSetRevocationMethod(rvc->builder, kSecPolicyCheckRevocationAny);
}
}
}
}
static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc) {
if (SecPathBuilderGetPVCCount(rvc->builder) == 1) {
SecPVCRef pvc = SecPathBuilderGetPVCAtIndex(rvc->builder, 0);
if (!pvc) { return false; }
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, 0);
CFStringRef policyName = (policy) ? SecPolicyGetName(policy) : NULL;
if (policyName && CFEqual(policyName, CFSTR("OCSPSigner"))) {
return false;
}
}
SecRevocationDbCheckNextUpdate();
SecValidInfoRef info = NULL;
CFIndex count = SecPathBuilderGetCertificateCount(rvc->builder);
if (count) {
bool isSelfSigned = false;
SecCertificateRef cert = NULL;
SecCertificateRef issuer = NULL;
CFIndex issuerIX = rvc->certIX + 1;
if (count > issuerIX) {
issuer = SecPathBuilderGetCertificateAtIndex(rvc->builder, issuerIX);
} else if (count == issuerIX) {
CFIndex rootIX = SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc->builder));
if (rootIX == rvc->certIX) {
issuer = SecPathBuilderGetCertificateAtIndex(rvc->builder, rootIX);
isSelfSigned = true;
}
}
cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
if (!isSelfSigned) {
info = SecRevocationDbCopyMatching(cert, issuer);
}
SecValidInfoSetAnchor(info, SecPathBuilderGetCertificateAtIndex(rvc->builder, count-1));
}
if (info) {
SecValidInfoRef old_info = rvc->valid_info;
rvc->valid_info = info;
if (old_info) {
SecValidInfoRelease(old_info);
}
return true;
}
return false;
}
static void SecRVCCheckRevocationCaches(SecRVCRef rvc) {
if (SecRVCShouldCheckOCSP(rvc) && (rvc->orvc->ocspRequest)) {
secdebug("ocsp", "Checking cached responses for cert %ld", rvc->certIX);
SecOCSPResponseRef response = NULL;
if (SecPathBuilderGetCheckRevocationOnline(rvc->builder)) {
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
response = SecOCSPCacheCopyMatchingWithMinInsertTime(rvc->orvc->ocspRequest, NULL, now - kSecOCSPResponseOnlineTTL);
} else {
response = SecOCSPCacheCopyMatching(rvc->orvc->ocspRequest, NULL);
}
SecORVCConsumeOCSPResponse(rvc->orvc,
response,
NULL_TIME, false);
}
#if ENABLE_CRLS
if (SecRVCShouldCheckCRL(rvc)) {
SecCRVCCheckRevocationCache(rvc->crvc);
}
#endif
}
static void SecRVCUpdatePVC(SecRVCRef rvc) {
SecRVCProcessValidInfoResults(rvc);
if (rvc->orvc) { SecORVCUpdatePVC(rvc->orvc); }
#if ENABLE_CRLS
if (rvc->crvc) { SecCRVCUpdatePVC(rvc->crvc); }
#endif
}
static bool SecRVCFetchNext(SecRVCRef rvc) {
bool OCSP_fetch_finished = true;
if (SecRVCShouldCheckOCSP(rvc)) {
OCSP_fetch_finished &= SecORVCFetchNext(rvc->orvc);
}
if (OCSP_fetch_finished) {
(void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
}
#if ENABLE_CRLS
bool CRL_fetch_finished = true;
if (SecRVCShouldCheckCRL(rvc)) {
rvc->crvc->distributionPointIX = 0;
CRL_fetch_finished &= SecCRVCFetchNext(rvc->crvc);
}
if (CRL_fetch_finished) {
(void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
}
OCSP_fetch_finished &= CRL_fetch_finished;
#endif
return OCSP_fetch_finished;
}
bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder) {
secdebug("rvc", "checking revocation");
CFIndex certIX, certCount = SecPathBuilderGetCertificateCount(builder);
SecCertificatePathVCRef path = SecPathBuilderGetPath(builder);
bool completed = true;
if (certCount <= 1) {
return completed;
}
if (SecCertificatePathVCIsRevocationDone(path)) {
for (certIX = 0; certIX < certCount; ++certIX) {
SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, certIX);
if (rvc) { SecRVCUpdatePVC(rvc); }
}
secdebug("rvc", "Not rechecking revocation");
return completed;
}
SecCertificatePathVCAllocateRVCs(path, certCount);
#if !ENABLE_CRLS
SecPathBuilderSetAsyncJobCount(builder, (unsigned int)(certCount-1));
#else
SecPathBuilderSetAsyncJobCount(builder, 2 * (unsigned int)(certCount-1));
#endif
for (certIX = 0; certIX < certCount; ++certIX) {
secdebug("rvc", "checking revocation for cert: %ld", certIX);
SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, certIX);
if (!rvc) {
continue;
}
SecRVCInit(rvc, builder, certIX);
if (rvc->done){
continue;
}
#if !TARGET_OS_BRIDGE
if (SecRVCCheckValidInfoDatabase(rvc)) {
SecRVCProcessValidInfoResults(rvc);
}
#endif
if (certIX+1 >= certCount) {
continue;
}
if (SecRVCShouldCheckOCSP(rvc)) {
SecORVCProcessStapledResponses(rvc->orvc);
}
#if TARGET_OS_BRIDGE
rvc->done = true;
return completed;
#endif
SecRVCCheckRevocationCaches(rvc);
if (rvc->orvc->done
#if ENABLE_CRLS
|| rvc->crvc->done
#endif
) {
secdebug("rvc", "found cached response for cert: %ld", certIX);
rvc->done = true;
}
bool old_cached_response = (!rvc->done && rvc->orvc->ocspResponse);
bool allow_fetch = SecPathBuilderCanAccessNetwork(builder) &&
(SecCertificatePathVCIsEV(path) || SecCertificatePathVCIsOptionallyEV(path) ||
SecPathBuilderGetRevocationMethod(builder) || old_cached_response);
bool fetch_done = true;
if (rvc->done || !allow_fetch) {
SecRVCUpdatePVC(rvc);
(void)SecPathBuilderDecrementAsyncJobCount(builder);
#if ENABLE_CRLS
(void)SecPathBuilderDecrementAsyncJobCount(builder);
#endif
} else {
fetch_done = SecRVCFetchNext(rvc);
}
if (!fetch_done) {
secdebug("rvc", "waiting on background fetch for cert %ld", certIX);
completed = false;
}
}
return completed;
}
CFAbsoluteTime SecRVCGetEarliestNextUpdate(SecRVCRef rvc) {
CFAbsoluteTime enu = NULL_TIME;
if (!rvc || !rvc->orvc) { return enu; }
enu = rvc->orvc->nextUpdate;
#if ENABLE_CRLS
CFAbsoluteTime crlNextUpdate = rvc->crvc->nextUpdate;
if (enu == NULL_TIME ||
((crlNextUpdate > NULL_TIME) && (enu > crlNextUpdate))) {
enu = crlNextUpdate;
}
#endif
return enu;
}