X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/822b670c6f91d089ccb51b77e24b6ac80406b337..07691282a056c4efea71e1e505527601e8cc166b:/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp?ds=inline diff --git a/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp b/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp index 5e292c98..a198ba56 100644 --- a/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp +++ b/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005,2011-2015 Apple Inc. All Rights Reserved. + * Copyright (c) 2005,2011-2016 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -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" @@ -54,9 +55,9 @@ #include #include #include -#include /* for _CSCheckFix */ +#include -#define trustSettingsDbg(args...) secdebug("trustSettings", ## args) +#define trustSettingsDbg(args...) secinfo("trustSettings", ## args) /* * Ideally we'd like to implement our own lock to protect the state of the cert stores @@ -244,7 +245,7 @@ static void tsPurgeCache() StLock _(sutCacheLock()); trustSettingsDbg("tsPurgeCache"); for(domain=0; domain_(ts); CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - + /* - * Keychains to search: user's search list, System.keychain, system root store, - * system intermediates, as appropriate + * Keychains to search: user's search list, System.keychain, system root store */ StorageManager::KeychainList keychains; Keychain adminKc; - Keychain sysCertKc; Keychain sysRootKc; switch(domain) { case kSecTrustSettingsDomainUser: @@ -904,9 +872,6 @@ OSStatus SecTrustSettingsCopyCertificates( /* admin certs in system keychain */ adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false); keychains.push_back(adminKc); - /* system-wide intermediate certs */ - sysCertKc = globals().storageManager.make(SYSTEM_CERT_STORE_PATH, false); - keychains.push_back(sysCertKc); /* drop thru to next case */ case kSecTrustSettingsDomainSystem: /* and, for all cases, immutable system root store */ @@ -917,15 +882,126 @@ 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; } - tsAddConditionalCerts(outArray); - *certArray = outArray; + /* Go through outArray and do a SecTrustEvaluate only for DomainSystem */ + if (kSecTrustSettingsDomainSystem == domain) { + 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 ModuleNexus gUserAdminCertsLock; + +void SecTrustSettingsPurgeUserAdminCertsCache(void) { + StReadWriteLock _(gUserAdminCertsLock(), StReadWriteLock::Write); + CFReleaseNull(gUserAdminCerts); + gUserAdminCertsCacheBuilt = false; +} + +OSStatus SecTrustSettingsCopyCertificatesForUserAdminDomains( + CFArrayRef *certArray) +{ + TS_REQUIRED(certArray); + OSStatus result = errSecSuccess; + + { /* Hold the read lock for the check */ + StReadWriteLock _(gUserAdminCertsLock(), StReadWriteLock::Read); + if (gUserAdminCertsCacheBuilt) { + if (gUserAdminCerts) { + *certArray = (CFArrayRef)CFRetain(gUserAdminCerts); + return errSecSuccess; + } else { + return errSecNoTrustSettings; + } + } + } + + /* There were no cached results. We'll have to recreate them. */ + CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!outArray) { + return errSecAllocate; + } + + CFArrayRef userTrusted = NULL, adminTrusted = NULL; + OSStatus userStatus = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainUser, &userTrusted); + if ((userStatus == errSecSuccess) && (userTrusted != NULL)) { + CFArrayAppendArray(outArray, userTrusted, CFRangeMake(0, CFArrayGetCount(userTrusted))); + CFRelease(userTrusted); + } + + OSStatus adminStatus = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainAdmin, &adminTrusted); + if ((adminStatus == errSecSuccess) && (adminTrusted != NULL)) { + CFArrayAppendArray(outArray, adminTrusted, CFRangeMake(0, CFArrayGetCount(adminTrusted))); + CFRelease(adminTrusted); + } + + /* Lack of trust settings for a domain results in an error above. Only fail + * if we weren't able to get trust settings for both domains. */ + if (userStatus != errSecSuccess && adminStatus != errSecSuccess) { + result = userStatus; + } + + if (result != errSecSuccess && outArray) { + CFRelease(outArray); + outArray = NULL; + } + + *certArray = outArray; + + /* For valid results, update the global cache */ + if (result == errSecSuccess || result == errSecNoTrustSettings) { + StReadWriteLock _(gUserAdminCertsLock(), StReadWriteLock::Write); + CFReleaseNull(gUserAdminCerts); + gUserAdminCerts = (CFArrayRef)CFRetainSafe(outArray); + gUserAdminCertsCacheBuilt = true; + } + + return result; +} + /* * Obtain an external, portable representation of the specified * domain's TrustSettings. Caller must CFRelease the returned data. @@ -985,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); + } +}