X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/6b200bc335dc93c5516ccb52f14bd896d8c7fad7..07691282a056c4efea71e1e505527601e8cc166b:/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp diff --git a/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp b/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp index c4bb5738..a198ba56 100644 --- a/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp +++ b/OSX/libsecurity_keychain/lib/SecTrustSettings.cpp @@ -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_(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 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); + } +}