SecTrustOSXEntryPoints.cpp [plain text]
#include "SecTrustOSXEntryPoints.h"
#include <Security/Security.h>
#include <Security/cssmtype.h>
#include <Security/SecKeychain.h>
#include <Security/SecItemPriv.h>
#include <Security/SecTrustSettingsPriv.h>
#include <Security/SecCertificate.h>
#include <Security/SecImportExport.h>
#include <security_keychain/SecImportExportPem.h>
#include <security_utilities/debugging.h>
#include <security_ocspd/ocspdClient.h>
#include <security_ocspd/ocspdUtils.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFRunLoop.h>
#include <dispatch/dispatch.h>
#include <AssertMacros.h>
#include <pthread.h>
static OSStatus SecLegacySourceChanged(__unused SecKeychainEvent keychainEvent, __unused SecKeychainCallbackInfo *info, __unused void *context) {
SecItemParentCachePurge();
SecTrustSettingsPurgeUserAdminCertsCache();
return 0;
}
static void *SecTrustOSXCFRunloop(__unused void *unused) {
CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, (CFTimeInterval) UINT_MAX, 0, 0, 0, ^(__unused CFRunLoopTimerRef _timer) {
});
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
SecKeychainEventMask trustdMask = (kSecAddEventMask | kSecDeleteEventMask | kSecUpdateEventMask |
kSecDefaultChangedEventMask | kSecKeychainListChangedMask |
kSecTrustSettingsChangedEventMask);
SecKeychainAddCallback(SecLegacySourceChanged, trustdMask, NULL);
try {
CFRunLoopRun();
}
catch (...) {
secerror("Exception occurred in CFRunLoopRun; exiting");
exit(0);
}
CFRelease(timer);
return NULL;
}
void SecTrustLegacySourcesEventRunloopCreate(void) {
static dispatch_once_t once;
dispatch_once(&once, ^{
pthread_attr_t attrs;
pthread_t thread;
pthread_attr_init(&attrs);
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
pthread_create(&thread, &attrs, SecTrustOSXCFRunloop, NULL);
});
}
#define CSSM_TIME_STRLEN 14
#define GENERALIZED_TIME_STRLEN 15
OSStatus SecTrustLegacyCRLStatus(SecCertificateRef cert, CFArrayRef chain, CFURLRef currCRLDP);
OSStatus SecTrustLegacyCRLFetch(CFURLRef currCRLDP, CFAbsoluteTime verifyTime);
static OSStatus cssmReturnToOSStatus(CSSM_RETURN crtn) {
OSStatus status = errSecInternalComponent;
switch (crtn) {
case CSSM_OK:
status = errSecSuccess;
break;
case CSSMERR_TP_CERT_REVOKED:
status = errSecCertificateRevoked;
break;
case CSSMERR_APPLETP_NETWORK_FAILURE:
status = errSecNetworkFailure;
break;
case CSSMERR_APPLETP_CRL_NOT_FOUND:
status = errSecCRLNotFound;
break;
default:
status = errSecInternalComponent;
}
return status;
}
#define PEM_STRING_X509 "CERTIFICATE"
static CFDataRef serializedPathToPemSequences(CFArrayRef certs) {
CFMutableDataRef result = NULL;
CFIndex certIX, certCount;
require_quiet(certs, out);
certCount = CFArrayGetCount(certs);
require_quiet(certCount > 0, out);
require_quiet(result = CFDataCreateMutable(NULL, 0), out);
for (certIX = 0; certIX < certCount; certIX++) {
CFDataRef certData = (CFDataRef)CFArrayGetValueAtIndex(certs, certIX);
require_noerr_quiet(impExpPemEncodeExportRep(certData, PEM_STRING_X509,
NULL, result), out);
}
out:
return result;
}
OSStatus SecTrustLegacyCRLStatus(SecCertificateRef cert, CFArrayRef chain, CFURLRef currCRLDP) {
OSStatus result = errSecParam;
CSSM_RETURN crtn = CSSMERR_TP_INTERNAL_ERROR;
CFDataRef serialData = NULL, pemIssuers = NULL, crlDP = NULL;
CFMutableArrayRef issuersArray = NULL;
if (!cert || !chain) {
return result;
}
CSSM_DATA serialNumber = { 0, NULL };
serialData = SecCertificateCopySerialNumber(cert, NULL);
if (serialData) {
serialNumber.Data = (uint8_t *)CFDataGetBytePtr(serialData);
serialNumber.Length = CFDataGetLength(serialData);
}
CSSM_DATA issuers = { 0, NULL };
issuersArray = CFArrayCreateMutableCopy(NULL, 0, chain);
if (issuersArray) {
CFArrayRemoveValueAtIndex(issuersArray, 0);
pemIssuers = serializedPathToPemSequences(issuersArray);
}
if (pemIssuers) {
issuers.Data = (uint8_t *)CFDataGetBytePtr(pemIssuers);
issuers.Length = CFDataGetLength(pemIssuers);
}
CSSM_DATA crlUrl = { 0, NULL };
crlDP = CFURLCreateData(NULL, currCRLDP, kCFStringEncodingASCII, true);
if (crlDP) {
crlUrl.Data = (uint8_t *)CFDataGetBytePtr(crlDP);
crlUrl.Length = CFDataGetLength(crlDP);
}
if (serialNumber.Data && issuers.Data && crlUrl.Data) {
crtn = ocspdCRLStatus(serialNumber, issuers, NULL, &crlUrl);
}
result = cssmReturnToOSStatus(crtn);
if (serialData) { CFRelease(serialData); }
if (issuersArray) { CFRelease(issuersArray); }
if (pemIssuers) { CFRelease(pemIssuers); }
if (crlDP) { CFRelease(crlDP); }
return result;
}
static CSSM_RETURN ocspdCRLFetchToCache(const CSSM_DATA &crlURL,
CSSM_TIMESTRING verifyTime) {
Allocator &alloc(Allocator::standard(Allocator::normal));
CSSM_DATA crlData = { 0, NULL };
CSSM_RETURN crtn;
crtn = ocspdCRLFetch(alloc, crlURL, NULL, true, true, verifyTime, crlData);
if (crlData.Data) { alloc.free(crlData.Data); }
return crtn;
}
static OSStatus fetchCRL(CFURLRef currCRLDP, CFAbsoluteTime verifyTime) {
OSStatus result = errSecParam;
CSSM_RETURN crtn = CSSMERR_TP_INTERNAL_ERROR;
CFDataRef crlDP = NULL;
char *cssmTime = NULL, *genTime = NULL;
if (!currCRLDP) {
return result;
}
CSSM_DATA crlUrl = { 0, NULL };
crlDP = CFURLCreateData(NULL, currCRLDP, kCFStringEncodingASCII, true);
if (crlDP) {
crlUrl.Data = (uint8_t *)CFDataGetBytePtr(crlDP);
crlUrl.Length = CFDataGetLength(crlDP);
}
cssmTime = (char *)malloc(CSSM_TIME_STRLEN + 1);
genTime = (char *)malloc(GENERAL_TIME_STRLEN + 1);
if (cssmTime && genTime) {
if (verifyTime != 0.0) {
cfAbsTimeToGgenTime(verifyTime, genTime);
} else {
cfAbsTimeToGgenTime(CFAbsoluteTimeGetCurrent(), genTime);
}
memmove(cssmTime, genTime, GENERAL_TIME_STRLEN - 1); cssmTime[CSSM_TIME_STRLEN] = '\0';
}
if (crlUrl.Data && cssmTime) {
crtn = ocspdCRLFetchToCache(crlUrl, (CSSM_TIMESTRING)cssmTime);
}
result = cssmReturnToOSStatus(crtn);
if (crlDP) { CFRelease(crlDP); }
if (cssmTime) { free(cssmTime); }
if (genTime) { free(genTime); }
return result;
}
static void async_ocspd_complete(async_ocspd_t *ocspd) {
if (ocspd->completed) {
ocspd->completed(ocspd);
}
}
bool SecTrustLegacyCRLFetch(async_ocspd_t *ocspd,
CFURLRef currCRLDP, CFAbsoluteTime verifyTime,
SecCertificateRef cert, CFArrayRef chain) {
dispatch_async(ocspd->queue, ^ {
OSStatus status = fetchCRL(currCRLDP, verifyTime);
switch (status) {
case errSecSuccess:
ocspd->response= SecTrustLegacyCRLStatus(cert, chain, currCRLDP);
break;
default:
ocspd->response = status;
break;
}
async_ocspd_complete(ocspd);
if (chain) { CFRelease(chain); }
});
return false;
}