]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/lib/SecTrustOSXEntryPoints.cpp
Security-57740.1.18.tar.gz
[apple/security.git] / 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 (file)
index 0000000..55c0993
--- /dev/null
@@ -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 <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>
+
+/*
+ * 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. */
+}