]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/lib/SecTrustSettings.cpp
Security-58286.260.20.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecTrustSettings.cpp
index c4bb57389c06302864f177c0ffd640531ef1aac6..a198ba569aeb55c26de6e57a83036d3167ad031a 100644 (file)
@@ -31,6 +31,7 @@
 #include "SecTrustSettings.h"
 #include "SecTrustSettingsPriv.h"
 #include "SecTrustSettingsCertificates.h"
+#include "SecCFRelease.h"
 #include "TrustSettingsUtils.h"
 #include "TrustSettings.h"
 #include "TrustSettingsSchema.h"
@@ -424,7 +425,7 @@ static OSStatus tsCopyCertsCommon(
                tsAddConditionalCerts(outArray);
        }
        *certArray = outArray;
-       CFRetain(*certArray);
+       CFRetainSafe(*certArray);
        trustSettingsDbg("tsCopyCertsCommon: %ld certs found",
                CFArrayGetCount(outArray));
        return errSecSuccess;
@@ -432,7 +433,7 @@ static OSStatus tsCopyCertsCommon(
 
 static void tsAddConditionalCerts(CFMutableArrayRef certArray)
 {
-#if TARGET_OS_MAC && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_IPHONE && !TARGET_OS_NANO
+#if TARGET_OS_OSX
        struct certmap_entry_s {
                CFStringRef bundleId;
                const UInt8* data;
@@ -842,18 +843,20 @@ OSStatus SecTrustSettingsCopyCertificates(
 
        TS_REQUIRED(certArray)
 
-       OSStatus result;
+       OSStatus status;
        TrustSettings* ts;
+       CFMutableArrayRef trustedCertArray = NULL;
+    SecTrustRef trust = NULL;
 
-       result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
-       if (result != errSecSuccess) {
-               return result;
+       status = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
+       if (status != errSecSuccess) {
+               return status;
        }
 
        auto_ptr<TrustSettings>_(ts);
 
        CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
-
+    
        /*
         * Keychains to search: user's search list, System.keychain, system root store
         */
@@ -879,23 +882,61 @@ OSStatus SecTrustSettingsCopyCertificates(
                        break;
        }
        ts->findCerts(keychains, outArray);
-       if(CFArrayGetCount(outArray) == 0) {
-               CFRelease(outArray);
+    CFIndex count = outArray ? CFArrayGetCount(outArray) : 0;
+       if(count == 0) {
+               CFReleaseSafe(outArray);
                return errSecNoTrustSettings;
        }
+ /* Go through outArray and do a SecTrustEvaluate only for DomainSystem */
        if (kSecTrustSettingsDomainSystem == domain) {
-               tsAddConditionalCerts(outArray);
-       }
-       *certArray = outArray;
+        CFIndex i;
+        SecPolicyRef policy = SecPolicyCreateBasicX509();
+           trustedCertArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+        for (i = 0; i < count ; i++) {
+            SecTrustResultType result;
+            SecCertificateRef certificate = (SecCertificateRef) CFArrayGetValueAtIndex(outArray, i);
+            status = SecTrustCreateWithCertificates(certificate, policy, &trust);
+               if (status != errSecSuccess) {
+               CFReleaseSafe(policy);
+              goto out;
+            }
+            status = SecTrustEvaluate(trust, &result);
+            if (status != errSecSuccess) {
+               CFReleaseSafe(policy);
+               goto out;
+            }
+            if (result != kSecTrustResultFatalTrustFailure) {
+                CFArrayAppendValue(trustedCertArray, certificate);
+            }
+            CFReleaseNull(trust);
+        }
+               tsAddConditionalCerts(trustedCertArray);
+        if (CFArrayGetCount(trustedCertArray) == 0) {
+                       status = errSecNoTrustSettings;
+               } else {
+                       *certArray = trustedCertArray;
+                       CFReleaseSafe(outArray);
+               }
+               CFReleaseSafe(policy);
+       } else {
+               *certArray = outArray;
+    }
+out:
+    if (status != errSecSuccess) {
+        CFReleaseSafe(outArray);
+               CFReleaseSafe(trustedCertArray);
+     }
+    CFReleaseNull(trust);
+    return status;
        END_RCSAPI
 }
 
 static CFArrayRef gUserAdminCerts = NULL;
 static bool gUserAdminCertsCacheBuilt = false;
-static ReadWriteLock gUserAdminCertsLock;
+static ModuleNexus<ReadWriteLock> gUserAdminCertsLock;
 
 void SecTrustSettingsPurgeUserAdminCertsCache(void) {
-    StReadWriteLock _(gUserAdminCertsLock, StReadWriteLock::Write);
+    StReadWriteLock _(gUserAdminCertsLock(), StReadWriteLock::Write);
     CFReleaseNull(gUserAdminCerts);
     gUserAdminCertsCacheBuilt = false;
 }
@@ -907,7 +948,7 @@ OSStatus SecTrustSettingsCopyCertificatesForUserAdminDomains(
     OSStatus result = errSecSuccess;
 
     { /* Hold the read lock for the check */
-        StReadWriteLock _(gUserAdminCertsLock, StReadWriteLock::Read);
+        StReadWriteLock _(gUserAdminCertsLock(), StReadWriteLock::Read);
         if (gUserAdminCertsCacheBuilt) {
             if (gUserAdminCerts) {
                 *certArray = (CFArrayRef)CFRetain(gUserAdminCerts);
@@ -952,7 +993,7 @@ OSStatus SecTrustSettingsCopyCertificatesForUserAdminDomains(
 
     /* For valid results, update the global cache */
     if (result == errSecSuccess || result == errSecNoTrustSettings) {
-        StReadWriteLock _(gUserAdminCertsLock, StReadWriteLock::Write);
+        StReadWriteLock _(gUserAdminCertsLock(), StReadWriteLock::Write);
         CFReleaseNull(gUserAdminCerts);
         gUserAdminCerts = (CFArrayRef)CFRetainSafe(outArray);
         gUserAdminCertsCacheBuilt = true;
@@ -1020,3 +1061,217 @@ OSStatus SecTrustSettingsImportExternalRepresentation(
        END_RCSAPI
 }
 
+/*
+ * SecTrustSettingsSetTrustSettings convenience wrapper function.
+ */
+void SecTrustSettingsSetTrustedCertificateForSSLHost(
+    SecCertificateRef certificate,
+    CFStringRef hostname,
+    void (^result)(SecTrustSettingsResult trustResult, CFErrorRef error))
+{
+       __block CFMutableArrayRef trustSettings = NULL;
+       __block CFNumberRef trustSettingsResult = NULL;
+       __block SecTrustSettingsDomain domain = kSecTrustSettingsDomainUser;
+
+       CFDictionaryRef policyProperties = NULL;
+       CFStringRef policyOid = NULL;
+       SecPolicyRef policy = NULL;
+
+       Boolean isSelfSigned = false;
+       Boolean hasPolicyConstraint = false;
+       Boolean hasPolicyValue = false;
+       Boolean policyConstraintChanged = false;
+       CFIndex indexOfEntryWithAllowedErrorForExpiredCert = kCFNotFound;
+       CFIndex indexOfEntryWithAllowedErrorForHostnameMismatch = kCFNotFound;
+       CFIndex i, count;
+       int32_t trustSettingsResultCode = kSecTrustSettingsResultTrustAsRoot;
+       OSStatus status = errSecSuccess;
+
+       CFRetainSafe(certificate);
+       CFRetainSafe(hostname);
+       if (!certificate || !hostname) {
+               status = errSecParam;
+       } else {
+               status = SecCertificateIsSelfSigned(certificate, &isSelfSigned);
+       }
+       if (status != errSecSuccess) {
+               goto reportErr;
+       }
+       if (isSelfSigned) {
+               trustSettingsResultCode = kSecTrustSettingsResultTrustRoot;
+       }
+       trustSettingsResult = CFNumberCreate(NULL, kCFNumberSInt32Type, &trustSettingsResultCode);
+
+       /* start with the existing trust settings for this certificate, if any */
+       {
+               CFArrayRef curTrustSettings = NULL;
+               (void)SecTrustSettingsCopyTrustSettings(certificate, domain, &curTrustSettings);
+               if (curTrustSettings) {
+                       trustSettings = CFArrayCreateMutableCopy(NULL, 0, curTrustSettings);
+                       CFReleaseNull(curTrustSettings);
+               } else {
+                       trustSettings = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               }
+       }
+       if (!trustSettings || !trustSettingsResult) {
+               status = errSecAllocate;
+               goto reportErr;
+       }
+
+       /* set up policy and value instances to trust the certificate for SSL for a given hostname */
+       policy = SecPolicyCreateSSL(true, hostname);
+       if (!policy) {
+               status = errSecInternal;
+               goto reportErr;
+       }
+       policyProperties = SecPolicyCopyProperties(policy);
+       if (!policyProperties) {
+               status = errSecInternal;
+               goto reportErr;
+       }
+       policyOid = (CFStringRef)CFDictionaryGetValue(policyProperties, kSecPolicyOid);
+       CFRetainSafe(policyOid);
+       if (!policyOid) {
+               status = errSecInternal;
+               goto reportErr;
+       }
+
+       /* look for dictionaries in the trust settings array for this policy and value */
+       count = CFArrayGetCount(trustSettings);
+       for (i=0; i < count; i++) {
+               CFDictionaryRef constraints = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, i);
+               if (!constraints) { continue; }
+               SecPolicyRef aPolicy = (SecPolicyRef)CFDictionaryGetValue(constraints, kSecTrustSettingsPolicy);
+               if (!aPolicy) { continue; }
+               CFDictionaryRef properties = SecPolicyCopyProperties(aPolicy);
+               if (!properties) { continue; }
+               CFStringRef aPolicyOid = (CFStringRef)CFDictionaryGetValue(properties, kSecPolicyOid);
+               if (aPolicyOid && kCFCompareEqualTo == CFStringCompare(aPolicyOid, policyOid, 0)) {
+                       CFStringRef aPolicyString = (CFStringRef)CFDictionaryGetValue(constraints, kSecTrustSettingsPolicyString);
+                       if (aPolicyString && kCFCompareEqualTo == CFStringCompare(aPolicyString, hostname, kCFCompareCaseInsensitive)) {
+                               /* found existing entry */
+                               CFNumberRef allowedErr = (CFNumberRef)CFDictionaryGetValue(constraints, kSecTrustSettingsAllowedError);
+                               int32_t eOld = 0;
+                               if (!allowedErr || !CFNumberGetValue(allowedErr, kCFNumberSInt32Type, &eOld)) {
+                                       eOld = CSSM_OK;
+                               }
+                               CFNumberRef tsResult = (CFNumberRef)CFDictionaryGetValue(constraints, kSecTrustSettingsResult);
+                               int32_t rOld = 0;
+                               if (!tsResult || !CFNumberGetValue(allowedErr, kCFNumberSInt32Type, &rOld)) {
+                                       rOld = kSecTrustSettingsResultTrustRoot;
+                               }
+                               if (!hasPolicyValue) { hasPolicyValue = (aPolicyString != NULL); }
+                               if (!hasPolicyConstraint) { hasPolicyConstraint = true; }
+                               if (eOld == CSSMERR_TP_CERT_EXPIRED) {
+                                       indexOfEntryWithAllowedErrorForExpiredCert = i;
+                               } else if (eOld == CSSMERR_APPLETP_HOSTNAME_MISMATCH) {
+                                       indexOfEntryWithAllowedErrorForHostnameMismatch = i;
+                               }
+                               if (trustSettingsResultCode != rOld) {
+                                       policyConstraintChanged = true;  // we are changing existing policy constraint's result
+                               }
+                       }
+               }
+               CFReleaseSafe(properties);
+       }
+
+       if (!hasPolicyConstraint) {
+               policyConstraintChanged = true; // we are adding a new policy constraint
+       } else if (hostname && !hasPolicyValue) {
+               policyConstraintChanged = true; // we need to add the hostname to an existing policy constraint
+       } else if ((indexOfEntryWithAllowedErrorForExpiredCert == kCFNotFound) ||
+                          (indexOfEntryWithAllowedErrorForHostnameMismatch == kCFNotFound)) {
+               policyConstraintChanged = true; // we are missing one of the expected allowed-error entries for this policy
+       }
+
+       if (policyConstraintChanged) {
+               CFMutableDictionaryRef policyDict[2] = { NULL, NULL };
+               policyDict[0] = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+               policyDict[1] = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+               int32_t certExpiredCode = (int32_t)CSSMERR_TP_CERT_EXPIRED;
+               CFNumberRef certExpired = CFNumberCreate(NULL, kCFNumberSInt32Type, &certExpiredCode);
+               int32_t hostnameMismatchCode = (int32_t)CSSMERR_APPLETP_HOSTNAME_MISMATCH;
+               CFNumberRef hostnameMismatch = CFNumberCreate(NULL, kCFNumberSInt32Type, &hostnameMismatchCode);
+               if (!policyDict[0] || !policyDict[1] || !certExpired || !hostnameMismatch) {
+                       status = errSecInternal;
+               } else {
+                       /* set up entry for policy, hostname, expired cert error, and result */
+                       CFDictionarySetValue(policyDict[0], kSecTrustSettingsPolicy, policy);
+                       CFDictionarySetValue(policyDict[0], kSecTrustSettingsPolicyString, hostname);
+                       CFDictionarySetValue(policyDict[0], kSecTrustSettingsAllowedError, certExpired);
+                       CFDictionarySetValue(policyDict[0], kSecTrustSettingsResult, trustSettingsResult);
+                       if (indexOfEntryWithAllowedErrorForExpiredCert != kCFNotFound) {
+                               /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
+                               CFArraySetValueAtIndex(trustSettings, indexOfEntryWithAllowedErrorForExpiredCert, policyDict[0]);
+                       } else if (!(hasPolicyValue)) {
+                               /* add a new policy constraint */
+                               CFArrayAppendValue(trustSettings, policyDict[0]);
+                       }
+                       /* set up additional entry for policy, hostname, hostname mismatch error, and result */
+                       CFDictionarySetValue(policyDict[1], kSecTrustSettingsPolicy, policy);
+                       CFDictionarySetValue(policyDict[1], kSecTrustSettingsPolicyString, hostname);
+                       CFDictionarySetValue(policyDict[1], kSecTrustSettingsAllowedError, hostnameMismatch);
+                       CFDictionarySetValue(policyDict[1], kSecTrustSettingsResult, trustSettingsResult);
+                       if (indexOfEntryWithAllowedErrorForHostnameMismatch != kCFNotFound) {
+                               /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
+                               CFArraySetValueAtIndex(trustSettings, indexOfEntryWithAllowedErrorForHostnameMismatch, policyDict[1]);
+                       } else if (!(hasPolicyValue)) {
+                               /* add a new policy constraint */
+                               CFArrayAppendValue(trustSettings, policyDict[1]);
+                       }
+               }
+               CFReleaseSafe(policyDict[0]);
+               CFReleaseSafe(policyDict[1]);
+               CFReleaseSafe(certExpired);
+               CFReleaseSafe(hostnameMismatch);
+       }
+
+       if (status != errSecSuccess) {
+               goto reportErr;
+       }
+       CFReleaseSafe(policyOid);
+       CFReleaseSafe(policyProperties);
+       CFReleaseSafe(policy);
+
+       dispatch_async(dispatch_get_main_queue(), ^{
+               /* add certificate to keychain first */
+               OSStatus status = SecCertificateAddToKeychain(certificate, NULL);
+               if (status == errSecSuccess || status == errSecDuplicateItem) {
+                       /* this will block on authorization UI... */
+                       status = SecTrustSettingsSetTrustSettings(certificate,
+                               domain, trustSettings);
+               }
+               if (result) {
+                       CFErrorRef error = NULL;
+                       if (status) {
+                               error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, status, NULL);
+                       }
+                       int32_t tsrc;
+                       if (!CFNumberGetValue(trustSettingsResult, kCFNumberSInt32Type, (int32_t*)&tsrc)) {
+                               tsrc = (int32_t)kSecTrustSettingsResultUnspecified;
+                       }
+                       result((SecTrustSettingsResult)tsrc, error);
+                       CFReleaseSafe(error);
+               }
+               CFRelease(trustSettingsResult);
+               CFRelease(trustSettings);
+               CFRelease(certificate);
+               CFRelease(hostname);
+       });
+
+       return;
+
+reportErr:
+       CFReleaseSafe(policyOid);
+       CFReleaseSafe(policyProperties);
+       CFReleaseSafe(policy);
+       CFReleaseSafe(trustSettingsResult);
+       CFReleaseSafe(trustSettings);
+       CFReleaseSafe(certificate);
+       CFReleaseSafe(hostname);
+       if (result) {
+               CFErrorRef error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, status, NULL);
+               result(kSecTrustSettingsResultInvalid, error);
+               CFReleaseSafe(error);
+       }
+}