X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/ce3c8656732c924baf7e88df75eab50891bdc471..fa7225c82381bac4432a6edf16f53b5370238d85:/OSX/libsecurity_keychain/lib/SecTrustOSXEntryPoints.cpp diff --git a/OSX/libsecurity_keychain/lib/SecTrustOSXEntryPoints.cpp b/OSX/libsecurity_keychain/lib/SecTrustOSXEntryPoints.cpp new file mode 100644 index 00000000..55c09939 --- /dev/null +++ b/OSX/libsecurity_keychain/lib/SecTrustOSXEntryPoints.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2016 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * SecTrustOSXEntryPoints - Interface for unified SecTrust into OS X Security + * Framework. + */ + +#include "SecTrustOSXEntryPoints.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* + * MARK: CFRunloop + */ + +static OSStatus SecLegacySourceChanged(__unused SecKeychainEvent keychainEvent, __unused SecKeychainCallbackInfo *info, __unused void *context) { + // Purge keychain parent cache + SecItemParentCachePurge(); + // Purge unrestricted roots cache + SecTrustSettingsPurgeUserAdminCertsCache(); + return 0; +} + +static void *SecTrustOSXCFRunloop(__unused void *unused) { + CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, (CFTimeInterval) UINT_MAX, 0, 0, 0, ^(__unused CFRunLoopTimerRef _timer) { + /* do nothing */ + }); + + /* add a timer to force the runloop to stay running */ + CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); + /* add keychain callback before we initiate a runloop to avoid it exiting due to no sources */ + + SecKeychainEventMask trustdMask = (kSecAddEventMask | kSecDeleteEventMask | kSecUpdateEventMask | + kSecDefaultChangedEventMask | kSecKeychainListChangedMask | + kSecTrustSettingsChangedEventMask); + SecKeychainAddCallback(SecLegacySourceChanged, trustdMask, NULL); + + try { + CFRunLoopRun(); + } + catch (...) { + /* An exception was rethrown from the runloop. Since we can't reliably + * obtain info about changes to keychains or trust settings anymore, + * just exit and respawn the process when needed. */ + + secerror("Exception occurred in CFRunLoopRun; exiting"); + exit(0); + } + CFRelease(timer); + return NULL; +} + +void SecTrustLegacySourcesEventRunloopCreate(void) { + /* A runloop is currently necessary to receive notifications about changes in the + * legacy keychains and trust settings. */ + 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); + + /* we do this with traditional pthread to avoid impacting our 512 WQ thread limit since this is a parked thread */ + pthread_create(&thread, &attrs, SecTrustOSXCFRunloop, NULL); + }); +} + +/* + * MARK: ocspd CRL Interface + */ +/* lengths of time strings without trailing NULL */ +#define CSSM_TIME_STRLEN 14 /* no trailing 'Z' */ +#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; + } + + /* serialNumber is a CSSM_DATA with the value from the TBS Certificate. */ + CSSM_DATA serialNumber = { 0, NULL }; + serialData = SecCertificateCopySerialNumber(cert, NULL); + if (serialData) { + serialNumber.Data = (uint8_t *)CFDataGetBytePtr(serialData); + serialNumber.Length = CFDataGetLength(serialData); + } + + /* issuers is CSSM_DATA containing pem sequence of all issuers in the chain */ + 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); + } + + /* crlUrl is CSSM_DATA with the CRLDP url*/ + 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; + } + + /* crlUrl is CSSM_DATA with the CRLDP url*/ + CSSM_DATA crlUrl = { 0, NULL }; + crlDP = CFURLCreateData(NULL, currCRLDP, kCFStringEncodingASCII, true); + if (crlDP) { + crlUrl.Data = (uint8_t *)CFDataGetBytePtr(crlDP); + crlUrl.Length = CFDataGetLength(crlDP); + } + + /* determine verification time */ + 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); // don't copy the Z + 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; +} + +/* + * MARK: async_ocspd methods + */ +static void async_ocspd_complete(async_ocspd_t *ocspd) { + if (ocspd->completed) { + ocspd->completed(ocspd); + } +} + +/* Return true, iff we didn't schedule any work, return false if we did. */ +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; /* false -> something was scheduled. */ +}