#include <Security/SecTrustPriv.h>
#include <Security/SecItemPriv.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecCertificatePath.h>
#include <Security/SecFramework.h>
#include <Security/SecPolicyInternal.h>
#include <utilities/SecIOFormat.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFSet.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFPropertyList.h>
#include <AssertMacros.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <Security/SecBasePriv.h>
#include <utilities/SecCFError.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecCertificateTrace.h>
#include "SecRSAKey.h"
#include <libDER/oids.h>
#include <utilities/debugging.h>
#include <Security/SecInternal.h>
#include <ipc/securityd_client.h>
#include <SecuritydXPC.h>
#include <securityd/SecTrustServer.h>
#define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
SEC_CONST_DECL (kSecTrustInfoExtendedValidationKey, "ExtendedValidation");
SEC_CONST_DECL (kSecTrustInfoCompanyNameKey, "CompanyName");
SEC_CONST_DECL (kSecTrustInfoRevocationKey, "Revocation");
SEC_CONST_DECL (kSecTrustInfoRevocationValidUntilKey, "RevocationValidUntil");
SEC_CONST_DECL (kSecTrustEvaluationDate, "TrustEvaluationDate");
SEC_CONST_DECL (kSecTrustExtendedValidation, "TrustExtendedValidation");
SEC_CONST_DECL (kSecTrustOrganizationName, "Organization");
SEC_CONST_DECL (kSecTrustResultValue, "TrustResultValue");
SEC_CONST_DECL (kSecTrustRevocationChecked, "TrustRevocationChecked");
SEC_CONST_DECL (kSecTrustRevocationValidUntilDate, "TrustExpirationDate");
SEC_CONST_DECL (kSecTrustResultDetails, "TrustResultDetails");
#pragma mark -
#pragma mark SecTrust
struct __SecTrust {
CFRuntimeBase _base;
CFArrayRef _certificates;
CFArrayRef _anchors;
CFTypeRef _policies;
CFArrayRef _responses;
CFDateRef _verifyDate;
SecCertificatePathRef _chain;
SecKeyRef _publicKey;
CFArrayRef _details;
CFDictionaryRef _info;
CFArrayRef _exceptions;
SecTrustResultType _trustResult;
bool _anchorsOnly;
SecNetworkPolicy _networkPolicy;
};
static pthread_once_t kSecTrustRegisterClass = PTHREAD_ONCE_INIT;
static CFTypeID kSecTrustTypeID = _kCFRuntimeNotATypeID;
static CFStringRef SecTrustDescribe(CFTypeRef cf);
static void SecTrustDestroy(CFTypeRef cf);
static OSStatus SecTrustEvaluateIfNecessary(SecTrustRef trust);
static CF_RETURNS_RETAINED CFStringRef SecTrustDescribe(CFTypeRef cf) {
SecTrustRef trust = (SecTrustRef)cf;
return CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("<SecTrustRef: %p>"), trust);
}
static void SecTrustDestroy(CFTypeRef cf) {
SecTrustRef trust = (SecTrustRef)cf;
CFReleaseSafe(trust->_certificates);
CFReleaseSafe(trust->_policies);
CFReleaseSafe(trust->_responses);
CFReleaseSafe(trust->_verifyDate);
CFReleaseSafe(trust->_anchors);
CFReleaseSafe(trust->_chain);
CFReleaseSafe(trust->_publicKey);
CFReleaseSafe(trust->_details);
CFReleaseSafe(trust->_info);
CFReleaseSafe(trust->_exceptions);
}
static void SecTrustRegisterClass(void) {
static const CFRuntimeClass kSecTrustClass = {
0,
"SecTrust",
NULL,
NULL,
SecTrustDestroy,
NULL,
NULL,
NULL,
SecTrustDescribe
};
kSecTrustTypeID = _CFRuntimeRegisterClass(&kSecTrustClass);
}
CFTypeID SecTrustGetTypeID(void) {
pthread_once(&kSecTrustRegisterClass, SecTrustRegisterClass);
return kSecTrustTypeID;
}
OSStatus SecTrustCreateWithCertificates(CFTypeRef certificates,
CFTypeRef policies, SecTrustRef *trust) {
OSStatus status = errSecParam;
CFAllocatorRef allocator = kCFAllocatorDefault;
CFArrayRef l_certs = NULL, l_policies = NULL;
SecTrustRef result = NULL;
check(certificates);
check(trust);
CFTypeID certType = CFGetTypeID(certificates);
if (certType == CFArrayGetTypeID()) {
require_quiet(CFArrayGetCount(certificates) > 0, errOut);
l_certs = CFArrayCreateCopy(allocator, certificates);
} else if (certType == SecCertificateGetTypeID()) {
l_certs = CFArrayCreate(allocator, &certificates, 1,
&kCFTypeArrayCallBacks);
} else {
goto errOut;
}
if (!l_certs) {
status = errSecAllocate;
goto errOut;
}
if (!policies) {
CFTypeRef policy = SecPolicyCreateBasicX509();
l_policies = CFArrayCreate(allocator, &policy, 1,
&kCFTypeArrayCallBacks);
CFRelease(policy);
} else if (CFGetTypeID(policies) == CFArrayGetTypeID()) {
l_policies = CFArrayCreateCopy(allocator, policies);
} else if (CFGetTypeID(policies) == SecPolicyGetTypeID()) {
l_policies = CFArrayCreate(allocator, &policies, 1,
&kCFTypeArrayCallBacks);
} else {
goto errOut;
}
if (!l_policies) {
status = errSecAllocate;
goto errOut;
}
CFIndex size = sizeof(struct __SecTrust);
require_quiet(result = (SecTrustRef)_CFRuntimeCreateInstance(allocator,
SecTrustGetTypeID(), size - sizeof(CFRuntimeBase), 0), errOut);
memset((char*)result + sizeof(result->_base), 0,
sizeof(*result) - sizeof(result->_base));
status = errSecSuccess;
errOut:
if (status) {
CFReleaseSafe(result);
CFReleaseSafe(l_certs);
CFReleaseSafe(l_policies);
} else {
result->_certificates = l_certs;
result->_policies = l_policies;
*trust = result;
}
return status;
}
static void SetTrustSetNeedsEvaluation(SecTrustRef trust) {
check(trust);
if (trust) {
trust->_trustResult = kSecTrustResultInvalid;
}
}
OSStatus SecTrustSetAnchorCertificatesOnly(SecTrustRef trust,
Boolean anchorCertificatesOnly) {
if (!trust) {
return errSecParam;
}
SetTrustSetNeedsEvaluation(trust);
trust->_anchorsOnly = anchorCertificatesOnly;
return errSecSuccess;
}
OSStatus SecTrustSetAnchorCertificates(SecTrustRef trust,
CFArrayRef anchorCertificates) {
if (!trust) {
return errSecParam;
}
SetTrustSetNeedsEvaluation(trust);
if (anchorCertificates)
CFRetain(anchorCertificates);
if (trust->_anchors)
CFRelease(trust->_anchors);
trust->_anchors = anchorCertificates;
trust->_anchorsOnly = (anchorCertificates != NULL);
return errSecSuccess;
}
OSStatus SecTrustCopyCustomAnchorCertificates(SecTrustRef trust,
CFArrayRef *anchors) {
if (!trust|| !anchors) {
return errSecParam;
}
CFArrayRef anchorsArray = NULL;
if (trust->_anchors) {
anchorsArray = CFArrayCreateCopy(kCFAllocatorDefault, trust->_anchors);
if (!anchorsArray) {
return errSecAllocate;
}
}
*anchors = anchorsArray;
return errSecSuccess;
}
OSStatus SecTrustSetOCSPResponse(SecTrustRef trust, CFTypeRef responseData) {
if (!trust) {
return errSecParam;
}
SetTrustSetNeedsEvaluation(trust);
CFArrayRef responseArray = NULL;
if (responseData) {
if (CFGetTypeID(responseData) == CFArrayGetTypeID()) {
responseArray = CFArrayCreateCopy(kCFAllocatorDefault, responseData);
} else if (CFGetTypeID(responseData) == CFDataGetTypeID()) {
responseArray = CFArrayCreate(kCFAllocatorDefault, &responseData, 1,
&kCFTypeArrayCallBacks);
} else {
return errSecParam;
}
}
if (trust->_responses)
CFRelease(trust->_responses);
trust->_responses = responseArray;
return errSecSuccess;
}
OSStatus SecTrustSetVerifyDate(SecTrustRef trust, CFDateRef verifyDate) {
if (!trust) {
return errSecParam;
}
SetTrustSetNeedsEvaluation(trust);
check(verifyDate);
CFRetainSafe(verifyDate);
if (trust->_verifyDate)
CFRelease(trust->_verifyDate);
trust->_verifyDate = verifyDate;
return errSecSuccess;
}
OSStatus SecTrustSetPolicies(SecTrustRef trust, CFTypeRef newPolicies) {
if (!trust || !newPolicies) {
return errSecParam;
}
SetTrustSetNeedsEvaluation(trust);
check(newPolicies);
CFArrayRef policyArray = NULL;
if (CFGetTypeID(newPolicies) == CFArrayGetTypeID()) {
policyArray = CFArrayCreateCopy(kCFAllocatorDefault, newPolicies);
} else if (CFGetTypeID(newPolicies) == SecPolicyGetTypeID()) {
policyArray = CFArrayCreate(kCFAllocatorDefault, &newPolicies, 1,
&kCFTypeArrayCallBacks);
} else {
return errSecParam;
}
if (trust->_policies)
CFRelease(trust->_policies);
trust->_policies = policyArray;
return errSecSuccess;
}
OSStatus SecTrustCopyPolicies(SecTrustRef trust, CFArrayRef *policies) {
if (!trust|| !policies) {
return errSecParam;
}
if (!trust->_policies) {
return errSecInternal;
}
CFArrayRef policyArray = CFArrayCreateCopy(kCFAllocatorDefault, trust->_policies);
if (!policyArray) {
return errSecAllocate;
}
*policies = policyArray;
return errSecSuccess;
}
OSStatus SecTrustSetNetworkFetchAllowed(SecTrustRef trust, Boolean allowFetch) {
if (!trust) {
return errSecParam;
}
trust->_networkPolicy = (allowFetch) ? useNetworkEnabled : useNetworkDisabled;
return errSecSuccess;
}
OSStatus SecTrustGetNetworkFetchAllowed(SecTrustRef trust, Boolean *allowFetch) {
if (!trust || !allowFetch) {
return errSecParam;
}
Boolean allowed = false;
SecNetworkPolicy netPolicy = trust->_networkPolicy;
if (netPolicy == useNetworkDefault) {
CFIndex idx, count = (trust->_policies) ? CFArrayGetCount(trust->_policies) : 0;
for (idx=0; idx<count; idx++) {
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(trust->_policies, idx);
if (policy) {
CFDictionaryRef props = SecPolicyCopyProperties(policy);
if (props) {
CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(props, kSecPolicyOid);
if (value) {
if (CFEqual(value, kSecPolicyAppleSSL)) {
allowed = true;
}
}
CFRelease(props);
}
}
}
} else {
allowed = (netPolicy == useNetworkEnabled);
}
*allowFetch = allowed;
return errSecSuccess;
}
CFAbsoluteTime SecTrustGetVerifyTime(SecTrustRef trust) {
CFAbsoluteTime verifyTime;
if (trust && trust->_verifyDate) {
verifyTime = CFDateGetAbsoluteTime(trust->_verifyDate);
} else {
verifyTime = CFAbsoluteTimeGetCurrent();
if (trust) {
trust->_verifyDate = CFDateCreate(CFGetAllocator(trust), verifyTime);
}
}
return verifyTime;
}
CFArrayRef SecTrustGetDetails(SecTrustRef trust) {
if (!trust) {
return NULL;
}
SecTrustEvaluateIfNecessary(trust);
return trust->_details;
}
OSStatus SecTrustGetTrustResult(SecTrustRef trust,
SecTrustResultType *result) {
if (!trust || !result) {
return errSecParam;
}
*result = trust->_trustResult;
return errSecSuccess;
}
static CFStringRef kSecCertificateDetailSHA1Digest = CFSTR("SHA1Digest");
static CFDictionaryRef SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust, CFIndex ix) {
if (!trust->_exceptions || ix >= CFArrayGetCount(trust->_exceptions))
return NULL;
CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(trust->_exceptions, ix);
if (CFGetTypeID(exception) != CFDictionaryGetTypeID())
return NULL;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
if (!certificate)
return NULL;
CFDataRef sha1Digest = SecCertificateGetSHA1Digest(certificate);
CFTypeRef digestValue = CFDictionaryGetValue(exception, kSecCertificateDetailSHA1Digest);
if (!digestValue || !CFEqual(sha1Digest, digestValue))
exception = NULL;
return exception;
}
struct SecTrustCheckExceptionContext {
CFDictionaryRef exception;
bool exceptionNotFound;
};
static void SecTrustCheckException(const void *key, const void *value, void *context) {
struct SecTrustCheckExceptionContext *cec = (struct SecTrustCheckExceptionContext *)context;
if (cec->exception) {
CFTypeRef exceptionValue = CFDictionaryGetValue(cec->exception, key);
if (!exceptionValue || !CFEqual(value, exceptionValue)) {
cec->exceptionNotFound = true;
}
} else {
cec->exceptionNotFound = true;
}
}
OSStatus SecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) {
if (!trust) {
return errSecParam;
}
OSStatus status = SecTrustEvaluateIfNecessary(trust);
if (status || !result)
return status;
SecTrustResultType trustResult = trust->_trustResult;
if (trustResult == kSecTrustResultUnspecified) {
if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
trustResult = kSecTrustResultProceed;
} else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
CFIndex pathLength = CFArrayGetCount(trust->_details);
struct SecTrustCheckExceptionContext context = {};
CFIndex ix;
for (ix = 0; ix < pathLength; ++ix) {
CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(trust->_details, ix);
if ((ix == 0) && CFDictionaryContainsKey(detail, kSecPolicyCheckBlackListedLeaf))
{
trustResult = kSecTrustResultFatalTrustFailure;
goto DoneCheckingTrust;
}
if (CFDictionaryContainsKey(detail, kSecPolicyCheckBlackListedKey))
{
trustResult = kSecTrustResultFatalTrustFailure;
goto DoneCheckingTrust;
}
context.exception = SecTrustGetExceptionForCertificateAtIndex(trust, ix);
CFDictionaryApplyFunction(detail, SecTrustCheckException, &context);
if (context.exceptionNotFound) {
break;
}
}
if (!context.exceptionNotFound)
trustResult = kSecTrustResultProceed;
}
DoneCheckingTrust:
trust->_trustResult = trustResult;
#if DEBUG
if (trustResult != kSecTrustResultProceed &&
trustResult != kSecTrustResultConfirm &&
trustResult != kSecTrustResultUnspecified) {
CFStringRef failureDesc = SecTrustCopyFailureDescription(trust);
secerror("%@", failureDesc);
CFRelease(failureDesc);
}
#endif
*result = trustResult;
return status;
}
OSStatus SecTrustEvaluateAsync(SecTrustRef trust,
dispatch_queue_t queue, SecTrustCallback result)
{
dispatch_async(queue, ^{
SecTrustResultType trustResult;
if (errSecSuccess != SecTrustEvaluate(trust, &trustResult)) {
trustResult = kSecTrustResultInvalid;
}
result(trust, trustResult);
});
return errSecSuccess;
}
static bool SecXPCDictionarySetCertificates(xpc_object_t message, const char *key, CFArrayRef certificates, CFErrorRef *error) {
xpc_object_t xpc_certificates = SecCertificateArrayCopyXPCArray(certificates, error);
if (!xpc_certificates)
return false;
xpc_dictionary_set_value(message, key, xpc_certificates);
xpc_release(xpc_certificates);
return true;
}
static bool SecXPCDictionarySetPolicies(xpc_object_t message, const char *key, CFArrayRef policies, CFErrorRef *error) {
xpc_object_t xpc_policies = SecPolicyArrayCopyXPCArray(policies, error);
if (!xpc_policies)
return false;
xpc_dictionary_set_value(message, key, xpc_policies);
xpc_release(xpc_policies);
return true;
}
static bool SecXPCDictionaryCopyChainOptional(xpc_object_t message, const char *key, SecCertificatePathRef *path, CFErrorRef *error) {
xpc_object_t xpc_path = xpc_dictionary_get_value(message, key);
if (!xpc_path) {
*path = NULL;
return true;
}
*path = SecCertificatePathCreateWithXPCArray(xpc_path, error);
return *path;
}
static int SecXPCDictionaryGetNonZeroInteger(xpc_object_t message, const char *key, CFErrorRef *error) {
int64_t value = xpc_dictionary_get_int64(message, key);
if (!value) {
SecError(errSecInternal, error, CFSTR("object for key %s is 0"), key);
}
return (int)value;
}
static SecTrustResultType certs_anchors_bool_policies_date_ag_to_details_info_chain_int_error_request(enum SecXPCOperation op, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef *details, CFDictionaryRef *info, SecCertificatePathRef *chain, CFErrorRef *error)
{
__block SecTrustResultType tr = kSecTrustResultInvalid;
securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
if (!SecXPCDictionarySetCertificates(message, kSecTrustCertificatesKey, certificates, error))
return false;
if (anchors && !SecXPCDictionarySetCertificates(message, kSecTrustAnchorsKey, anchors, error))
return false;
if (anchorsOnly)
xpc_dictionary_set_bool(message, kSecTrustAnchorsOnlyKey, anchorsOnly);
if (!SecXPCDictionarySetPolicies(message, kSecTrustPoliciesKey, policies, error))
return false;
xpc_dictionary_set_double(message, kSecTrustVerifyDateKey, verifyTime);
return true;
}, ^bool(xpc_object_t response, CFErrorRef *error) {
secdebug("trust", "response: %@", response);
return SecXPCDictionaryCopyArrayOptional(response, kSecTrustDetailsKey, details, error) &&
SecXPCDictionaryCopyDictionaryOptional(response, kSecTrustInfoKey, info, error) &&
SecXPCDictionaryCopyChainOptional(response, kSecTrustChainKey, chain, error) &&
((tr = SecXPCDictionaryGetNonZeroInteger(response, kSecTrustResultKey, error)) != kSecTrustResultInvalid);
});
return tr;
}
static OSStatus SecTrustEvaluateIfNecessary(SecTrustRef trust) {
check(trust);
if (!trust)
return errSecParam;
if (trust->_trustResult != kSecTrustResultInvalid)
return errSecSuccess;
trust->_trustResult = kSecTrustResultOtherError;
CFReleaseNull(trust->_chain);
CFReleaseNull(trust->_details);
CFReleaseNull(trust->_info);
return SecOSStatusWith(^bool (CFErrorRef *error) {
trust->_trustResult = SECURITYD_XPC(sec_trust_evaluate, certs_anchors_bool_policies_date_ag_to_details_info_chain_int_error_request, trust->_certificates, trust->_anchors, trust->_anchorsOnly, trust->_policies, SecTrustGetVerifyTime(trust), SecAccessGroupsGetCurrent(), &trust->_details, &trust->_info, &trust->_chain, error);
if (trust->_trustResult == kSecTrustResultInvalid &&
SecErrorGetOSStatus(*error) == errSecNotAvailable &&
CFArrayGetCount(trust->_certificates)) {
trust->_chain = SecCertificatePathCreate(NULL, (SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0));
if (error)
CFReleaseNull(*error);
return true;
}
return trust->_trustResult != kSecTrustResultInvalid;
});
}
static int compare_strings(const void *a1, const void *a2) {
CFStringRef s1 = *(CFStringRef *)a1;
CFStringRef s2 = *(CFStringRef *)a2;
return (int) CFStringCompare(s1, s2, kCFCompareForcedOrdering);
}
CFStringRef SecTrustCopyFailureDescription(SecTrustRef trust) {
CFMutableStringRef reason = CFStringCreateMutable(NULL, 0);
CFArrayRef details = SecTrustGetDetails(trust);
CFIndex pathLength = details ? CFArrayGetCount(details) : 0;
for (CFIndex ix = 0; ix < pathLength; ++ix) {
CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
CFIndex dCount = CFDictionaryGetCount(detail);
if (dCount) {
if (ix == 0)
CFStringAppend(reason, CFSTR(" [leaf"));
else if (ix == pathLength - 1)
CFStringAppend(reason, CFSTR(" [root"));
else
CFStringAppendFormat(reason, NULL, CFSTR(" [ca%" PRIdCFIndex ), ix);
const void *keys[dCount];
CFDictionaryGetKeysAndValues(detail, &keys[0], NULL);
qsort(&keys[0], dCount, sizeof(keys[0]), compare_strings);
for (CFIndex kix = 0; kix < dCount; ++kix) {
CFStringRef key = keys[kix];
const void *value = CFDictionaryGetValue(detail, key);
CFStringAppendFormat(reason, NULL, CFSTR(" %@%@"), key,
(CFGetTypeID(value) == CFBooleanGetTypeID()
? CFSTR("") : value));
}
CFStringAppend(reason, CFSTR("]"));
}
}
return reason;
}
SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust) {
if (!trust) {
return NULL;
}
if (!trust->_publicKey) {
if (!trust->_chain) {
trust->_publicKey = SecCertificateCopyPublicKey(SecTrustGetCertificateAtIndex(trust, 0));
#if 0
if (!trust->_publicKey) {
SecCertificatePathRef path;
path = SecCertificatePathCreateWithArray(trust->_certificates);
trust->_publicKey = SecCertificatePathCopyPublicKeyAtIndex(path, 0);
CFRelease(path);
}
#endif
if (!trust->_publicKey) {
SecTrustEvaluateIfNecessary(trust);
}
}
if (trust->_chain) {
trust->_publicKey = SecCertificatePathCopyPublicKeyAtIndex(trust->_chain, 0);
}
}
if (trust->_publicKey)
CFRetain(trust->_publicKey);
return trust->_publicKey;
}
CFIndex SecTrustGetCertificateCount(SecTrustRef trust) {
if (!trust) {
return 0;
}
SecTrustEvaluateIfNecessary(trust);
return (trust->_chain) ? SecCertificatePathGetCount(trust->_chain) : 1;
}
SecCertificateRef SecTrustGetCertificateAtIndex(SecTrustRef trust,
CFIndex ix) {
if (!trust) {
return NULL;
}
if (ix == 0) {
return (SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0);
}
SecTrustEvaluateIfNecessary(trust);
return (trust->_chain) ? SecCertificatePathGetCertificateAtIndex(trust->_chain, ix) : NULL;
}
CFDictionaryRef SecTrustCopyInfo(SecTrustRef trust) {
if (!trust) {
return NULL;
}
SecTrustEvaluateIfNecessary(trust);
CFDictionaryRef info = trust->_info;
if (info)
CFRetain(info);
return info;
}
CFDataRef SecTrustCopyExceptions(SecTrustRef trust) {
CFArrayRef details = SecTrustGetDetails(trust);
CFIndex pathLength = details ? CFArrayGetCount(details) : 0;
CFMutableArrayRef exceptions = CFArrayCreateMutable(kCFAllocatorDefault, pathLength, &kCFTypeArrayCallBacks);
CFIndex ix;
for (ix = 0; ix < pathLength; ++ix) {
CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
CFIndex detailCount = CFDictionaryGetCount(detail);
CFMutableDictionaryRef exception;
if (ix == 0 || detailCount > 0) {
exception = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, detailCount + 1, detail);
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
CFDictionaryAddValue(exception, kSecCertificateDetailSHA1Digest, digest);
} else {
exception = (CFMutableDictionaryRef)CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
CFArrayAppendValue(exceptions, exception);
CFRelease(exception);
}
for (ix = pathLength; ix-- > 1;) {
CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(exceptions, ix);
if (CFDictionaryGetCount(exception) == 0) {
CFArrayRemoveValueAtIndex(exceptions, ix);
} else {
break;
}
}
CFDataRef encodedExceptions = CFPropertyListCreateData(kCFAllocatorDefault,
exceptions, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
CFRelease(exceptions);
return encodedExceptions;
}
bool SecTrustSetExceptions(SecTrustRef trust, CFDataRef encodedExceptions) {
if (!trust) {
return false;
}
CFArrayRef exceptions = NULL;
if (NULL != encodedExceptions) {
exceptions = CFPropertyListCreateWithData(kCFAllocatorDefault,
encodedExceptions, kCFPropertyListImmutable, NULL, NULL);
}
if (exceptions && CFGetTypeID(exceptions) != CFArrayGetTypeID()) {
CFRelease(exceptions);
exceptions = NULL;
}
CFReleaseSafe(trust->_exceptions);
trust->_exceptions = exceptions;
if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
return true;
CFReleaseNull(trust->_exceptions);
return false;
}
CFArrayRef SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust, CFIndex ix) {
CFMutableArrayRef summary;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
summary = SecCertificateCopySummaryProperties(certificate,
SecTrustGetVerifyTime(trust));
return summary;
}
CFArrayRef SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust, CFIndex ix) {
CFArrayRef summary;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
summary = SecCertificateCopyProperties(certificate);
return summary;
}
#if 0
CFStringRef kSecPolicyCheckIdLinkage = CFSTR("IdLinkage");
CFStringRef kSecPolicyCheckCriticalExtensions = CFSTR("CriticalExtensions");
CFStringRef kSecPolicyCheckQualifiedCertStatements = CFSTR("QualifiedCertStatements");
CFStringRef kSecPolicyCheckAnchorTrusted = CFSTR("AnchorTrusted");
CFStringRef kSecPolicyCheckAnchorSHA1 = CFSTR("AnchorSHA1");
CFStringRef kSecPolicyCheckSSLHostname = CFSTR("SSLHostname");
CFStringRef kSecPolicyCheckNonEmptySubject = CFSTR("NonEmptySubject");
CFStringRef kSecPolicyCheckBasicContraints = CFSTR("BasicContraints");
CFStringRef kSecPolicyCheckKeyUsage = CFSTR("KeyUsage");
CFStringRef kSecPolicyCheckExtendedKeyUsage = CFSTR("ExtendedKeyUsage");
CFStringRef kSecPolicyCheckIssuerCommonName = CFSTR("IssuerCommonName");
CFStringRef kSecPolicyCheckSubjectCommonNamePrefix = CFSTR("SubjectCommonNamePrefix");
CFStringRef kSecPolicyCheckChainLength = CFSTR("ChainLength");
CFStringRef kSecPolicyCheckNotValidBefore = CFSTR("NotValidBefore");
CFStringRef kSecPolicyCheckValidIntermediates = CFSTR("ValidIntermediates");
CFStringRef kSecPolicyCheckValidLeaf = CFSTR("ValidLeaf");
CFStringRef kSecPolicyCheckValidRoot = CFSTR("ValidRoot");
#endif
struct TrustFailures {
bool badLinkage;
bool unknownCritExtn;
bool untrustedAnchor;
bool hostnameMismatch;
bool policyFail;
bool invalidCert;
};
static void applyDetailProperty(const void *_key, const void *_value,
void *context) {
CFStringRef key = (CFStringRef)_key;
struct TrustFailures *tf = (struct TrustFailures *)context;
if (CFGetTypeID(_value) != CFBooleanGetTypeID()) {
return;
}
CFBooleanRef value = (CFBooleanRef)_value;
if (CFBooleanGetValue(value)) {
return;
}
if (CFEqual(key, kSecPolicyCheckIdLinkage)) {
tf->badLinkage = true;
} else if (CFEqual(key, kSecPolicyCheckCriticalExtensions)
|| CFEqual(key, kSecPolicyCheckQualifiedCertStatements)) {
tf->unknownCritExtn = true;
} else if (CFEqual(key, kSecPolicyCheckAnchorTrusted)
|| CFEqual(key, kSecPolicyCheckAnchorSHA1)) {
tf->untrustedAnchor = true;
} else if (CFEqual(key, kSecPolicyCheckSSLHostname)) {
tf->hostnameMismatch = true;
} else if (CFEqual(key, kSecPolicyCheckValidIntermediates)
|| CFEqual(key, kSecPolicyCheckValidLeaf)
|| CFEqual(key, kSecPolicyCheckValidLeaf)) {
tf->invalidCert = true;
} else
#if 0
if (CFEqual(key, kSecPolicyCheckNonEmptySubject)
|| CFEqual(key, kSecPolicyCheckBasicContraints)
|| CFEqual(key, kSecPolicyCheckKeyUsage)
|| CFEqual(key, kSecPolicyCheckExtendedKeyUsage)
|| CFEqual(key, kSecPolicyCheckIssuerCommonName)
|| CFEqual(key, kSecPolicyCheckSubjectCommonNamePrefix)
|| CFEqual(key, kSecPolicyCheckChainLength)
|| CFEqual(key, kSecPolicyCheckNotValidBefore))
#endif
{
tf->policyFail = true;
}
}
static void appendError(CFMutableArrayRef properties, CFStringRef error) {
CFStringRef localizedError = SecFrameworkCopyLocalizedString(error,
CFSTR("SecCertificate"));
appendProperty(properties, kSecPropertyTypeError, NULL, NULL,
localizedError);
CFReleaseNull(localizedError);
}
CFArrayRef SecTrustCopyProperties(SecTrustRef trust) {
CFArrayRef details = SecTrustGetDetails(trust);
if (!details)
return NULL;
struct TrustFailures tf = {};
CFIndex ix, count = CFArrayGetCount(details);
for (ix = 0; ix < count; ++ix) {
CFDictionaryRef detail = (CFDictionaryRef)
CFArrayGetValueAtIndex(details, ix);
CFDictionaryApplyFunction(detail, applyDetailProperty, &tf);
}
CFMutableArrayRef properties = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (tf.badLinkage) {
appendError(properties, CFSTR("Invalid certificate chain linkage."));
} else if (tf.unknownCritExtn) {
appendError(properties, CFSTR("One or more unsupported critical extensions found."));
} else {
if (tf.untrustedAnchor) {
appendError(properties, CFSTR("Root certificate is not trusted."));
}
if (tf.hostnameMismatch) {
appendError(properties, CFSTR("Hostname mismatch."));
}
if (tf.policyFail) {
appendError(properties, CFSTR("Policy requirements not met."));
}
if (tf.invalidCert) {
appendError(properties, CFSTR("One or more certificates have expired or are not valid yet."));
}
}
if (CFArrayGetCount(properties) == 0) {
CFReleaseNull(properties);
}
return properties;
}
CFDictionaryRef SecTrustCopyResult(SecTrustRef trust) {
if (!trust) {
return NULL;
}
CFMutableDictionaryRef results = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFArrayRef details = SecTrustGetDetails(trust);
if (details) {
CFDictionarySetValue(results, (const void *)kSecTrustResultDetails, (const void *)details);
}
CFNumberRef numValue = CFNumberCreate(NULL, kCFNumberSInt32Type, &trust->_trustResult);
if (numValue) {
CFDictionarySetValue(results, (const void *)kSecTrustResultValue, (const void *)numValue);
CFRelease(numValue);
}
if (trust->_trustResult == kSecTrustResultInvalid || !trust->_info)
return results;
CFDateRef evaluationDate = trust->_verifyDate;
if (evaluationDate) {
CFDictionarySetValue(results, (const void *)kSecTrustEvaluationDate, (const void *)evaluationDate);
}
CFDictionaryRef info = trust->_info;
if (info) {
CFBooleanRef evValue;
if (CFDictionaryGetValueIfPresent(info, kSecTrustInfoExtendedValidationKey, (const void **)&evValue)) {
CFDictionarySetValue(results, (const void *)kSecTrustExtendedValidation, (const void *)evValue);
}
CFStringRef organizationName;
if (CFDictionaryGetValueIfPresent(info, kSecTrustInfoCompanyNameKey, (const void **)&organizationName)) {
CFDictionarySetValue(results, (const void *)kSecTrustOrganizationName, (const void *)organizationName);
}
}
#if 0
CFTypeRef expirationDate;
if (CFDictionaryGetValueIfPresent(mExtendedResult, kSecTrustExpirationDate, (const void **)&expirationDate)) {
CFDictionarySetValue(results, (const void *)kSecTrustRevocationValidUntilDate, (const void *)expirationDate);
CFDictionarySetValue(results, (const void *)kSecTrustRevocationChecked, (const void *)kCFBooleanTrue);
}
#endif
return results;
}
static int to_int_error_request(enum SecXPCOperation op, CFErrorRef *error) {
__block int64_t result = 0;
securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, CFErrorRef *error) {
result = xpc_dictionary_get_int64(response, kSecXPCKeyResult);
if (!result)
return SecError(errSecInternal, error, CFSTR("int64 missing in response"));
return true;
});
return (int)result;
}
OSStatus SecTrustGetOTAPKIAssetVersionNumber(int* versionNumber)
{
return SecOSStatusWith(^bool(CFErrorRef *error) {
if (!versionNumber)
return SecError(errSecParam, error, CFSTR("versionNumber is NULL"));
return (*versionNumber = SECURITYD_XPC(sec_ota_pki_asset_version, to_int_error_request, error)) != 0;
});
}
#define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
static bool xpc_dictionary_entry_is_type(xpc_object_t dictionary, const char *key, xpc_type_t type)
{
xpc_object_t value = xpc_dictionary_get_value(dictionary, key);
return value && (xpc_get_type(value) == type);
}
OSStatus SecTrustOTAPKIGetUpdatedAsset(int* didUpdateAsset)
{
CFErrorRef error = NULL;
do_if_registered(sec_ota_pki_get_new_asset, &error);
int64_t num = 0;
xpc_object_t message = securityd_create_message(kSecXPCOpOTAPKIGetNewAsset, &error);
if (message)
{
xpc_object_t response = securityd_message_with_reply_sync(message, &error);
if (response && xpc_dictionary_entry_is_type(response, kSecXPCKeyResult, XPC_TYPE_INT64))
{
num = (int64_t) xpc_dictionary_get_int64(response, kSecXPCKeyResult);
xpc_release(response);
}
xpc_release(message);
}
if (NULL != didUpdateAsset)
{
*didUpdateAsset = (int)num;
}
return noErr;
}
#if 0
typedef uint8_t SecFetchingState;
enum {
kSecFetchingStatePassedIn = 0,
kSecFetchingStateLocal,
kSecFetchingStateFromURL,
kSecFetchingStateDone,
};
typedef uint8_t SecTrustState;
enum {
kSecTrustStateUnknown = 0,
kSecTrustStateNotSigner,
kSecTrustStateValidSigner,
};
typedef struct __SecTrustNode *SecTrustNodeRef;
struct __SecTrustNode {
SecTrustNodeRef _child;
SecCertificateRef _certificate;
bool _isAnchor;
bool _isSelfSigned;
CFMutableSet _certificates;
CFMutableSet _partials;
CFMutableSet _rejected_partials;
CFMutableSet _candidates;
CFMutableSet _rejected_candidates;
SecFetchingState _fetchingState;
SecTrustState _trustState;
};
typedef struct __SecTrustNode SecTrustNode;
static pthread_once_t kSecTrustNodeRegisterClass = PTHREAD_ONCE_INIT;
static CFTypeID kSecTrustNodeTypeID = _kCFRuntimeNotATypeID;
static CFStringRef SecTrustNodeDescribe(CFTypeRef cf);
static void SecTrustNodeDestroy(CFTypeRef cf);
static CFStringRef SecTrustNodeDescribe(CFTypeRef cf) {
SecTrustNodeRef node = (SecTrustNodeRef)cf;
return CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("<SecTrustNodeRef: %p>"), node);
}
static void SecTrustNodeDestroy(CFTypeRef cf) {
SecTrustNodeRef trust = (SecTrustNodeRef)cf;
if (trust->_child) {
free(trust->_child);
}
if (trust->_certificate) {
free(trust->_certificate);
}
if (trust->_candidates) {
CFRelease(trust->_candidates);
}
}
static void SecTrustNodeRegisterClass(void) {
static const CFRuntimeClass kSecTrustNodeClass = {
0,
"SecTrustNode",
NULL,
NULL,
SecTrustNodeDestroy,
NULL,
NULL,
NULL,
SecTrustNodeDescribe
};
kSecTrustNodeTypeID = _CFRuntimeRegisterClass(&kSecTrustNodeClass);
}
CFTypeID SecTrustNodeGetTypeID(void) {
pthread_once(&kSecTrustNodeRegisterClass, SecTrustNodeRegisterClass);
return kSecTrustNodeTypeID;
}
SecTrustNodeRef SecTrustNodeCreate(SecTrustRef trust,
SecCertificateRef certificate, SecTrustNodeRef child) {
CFAllocatorRef allocator = kCFAllocatorDefault;
check(trust);
check(certificate);
CFIndex size = sizeof(struct __SecTrustNode);
SecTrustNodeRef result = (SecTrustNodeRef)_CFRuntimeCreateInstance(
allocator, SecTrustNodeGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
memset((char*)result + sizeof(result->_base), 0,
sizeof(*result) - sizeof(result->_base));
if (child) {
CFRetain(child);
result->_child = child;
}
CFRetain(certificate);
result->_certificate = certificate;
result->_isAnchor = SecTrustCertificateIsAnchor(certificate);
return result;
}
SecCertificateRef SecTrustGetCertificate(SecTrustNodeRef node) {
check(node);
return node->_certificate;
}
CFArrayRef SecTrustNodeCopyProperties(SecTrustNodeRef node,
SecTrustRef trust) {
check(node);
check(trust);
CFMutableArrayRef summary = SecCertificateCopySummaryProperties(
node->_certificate, SecTrustGetVerifyTime(trust));
return summary;
}
SecTrustState SecTrustNodeVerifySignatureChain(SecTrustNodeRef node) {
return kSecTrustStateUnknown;
}
SecTrustNodeRef SecTrustNodeCopyNextCandidate(SecTrustNodeRef node,
SecTrustRef trust, SecFetchingState fetchingState) {
check(node);
check(trust);
CFAbsoluteTime verifyTime = SecTrustGetVerifyTime(trust);
for (;;) {
while (node->_candidateIndex < CFArrayGetCount(node->_candidates)) {
SecCertificateRef candidate = (SecCertificateRef)
CFArrayGetValueAtIndex(node->_candidates, node->_candidateIndex);
if (node->_fetchingState != kSecFetchingStateDone) {
if (!SecCertificateIsValidOn(candidate, verifyTime)) {
node->_candidateIndex++;
continue;
}
}
SecTrustNodeRef parent = SecTrustNodeCreate(candidate, node);
CFArrayRemoveValueAtIndex(node->_candidates, node->_candidateIndex);
if (SecTrustNodeVerifySignatureChain(parent) ==
kSecTrustStateNotSigner) {
CFRelease(parent);
continue;
}
return parent;
}
SecCertificateRef certificate = node->_certificate;
switch (node->_fetchingState) {
case kSecFetchingStatePassedIn:
CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
if (akid) {
SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates);
} else {
CFDataRef issuer =
SecCertificateGetNormalizedIssuerContent(certificate);
SecTrustAppendCandidatesWithSubject(issuer, node->_candidates);
}
node->_fetchingState = kSecFetchingStateLocal;
break;
case kSecFetchingStateLocal:
node->_fetchingState = kSecFetchingStateFromURL;
break;
case kSecFetchingStateFromURL:
node->_fetchingState = kSecFetchingStateCheckExpired;
break;
case kSecFetchingStateCheckExpired:
node->_candidateIndex = 0;
node->_fetchingState = kSecFetchingStateDone;
break;
case kSecFetchingStateDone;
return NULL;
}
}
CFAllocatorRef allocator = CFGetAllocator(node);
CFMutableArrayRef issuers = CFArrayCreateMutable(allocator, 0,
&kCFTypeArrayCallBacks);
certificate = node->_certificate;
CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
CFTypeRef candidates = NULL;
if (akid) {
candidates = (CFTypeRef)CFDictionaryGetValueForKey(skidDict, akid);
if (candidates) {
addValidIssuersFrom(issuers, certificate, candidates, true);
}
}
if (!candidates) {
CFDataRef issuer =
SecCertificateGetNormalizedIssuerContent(certificate);
candidates = (CFTypeRef)
CFDictionaryGetValueForKey(subjectDict, issuer);
addValidIssuersFrom(issuers, certificate, candidates, false);
}
if (CFArrayGetCount(issuers) == 0) {
}
return errSecSuccess;
}
CFArrayRef SecTrustNodeCopyNextChain(SecTrustNodeRef node,
SecTrustRef trust) {
switch (node->_fetchingState) {
case kSecFetchingStatePassedIn:
CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
if (akid) {
SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates);
} else {
CFDataRef issuer =
SecCertificateGetNormalizedIssuerContent(certificate);
SecTrustAppendCandidatesWithSubject(issuer, node->_candidates);
}
node->_fetchingState = kSecFetchingStateLocal;
break;
case kSecFetchingStateLocal:
node->_fetchingState = kSecFetchingStateFromURL;
break;
case kSecFetchingStateFromURL:
node->_fetchingState = kSecFetchingStateCheckExpired;
break;
case kSecFetchingStateCheckExpired:
node->_candidateIndex = 0;
node->_fetchingState = kSecFetchingStateDone;
break;
case kSecFetchingStateDone;
return NULL;
}
}
class Source {
Iterator parentIterator(Cert);
};
class NodeCache {
Set nodes;
static bool unique(Node node) {
if (nodes.contains(node))
return false;
nodes.add(node);
return true;
}
static bool isAnchor(Cert cert);
};
class Node {
Cert cert;
Node child;
int nextSource;
Iterator parentIterator;
Node(Cert inCert) : child(nil), cert(inCert), nextSource(0) {}
Node(Node inChild, Cert inCert) : child(inChild), cert(inCert),
nextSource(0) {}
CertPath certPath() {
CertPath path;
Node node = this;
while (node) {
path.add(node.cert);
node = node.child;
}
return path;
}
void contains(Cert cert) {
Node node = this;
while (node) {
if (cert == node.cert)
return true;
node = node.child;
}
return false;
}
Node nextParent(Array currentSources) {
for (;;) {
if (!nextSource ||
parentIterator == currentSources[nextSource - 1].end()) {
if (nextSource == currentSources.count) {
return nil;
}
parentIterator = currentSources[nextSource++].begin();
}
Certificate cert = *parentIterator++;
if (!contains(cert)) {
Node node = Node(this, parent);
if (!NodeCache.unique(node))
return node;
}
}
}
};
class PathBuilder {
List nodes;
List rejects;
Array currentSources;
Iterator nit;
Array allSources;
Iterator sourceIT;
CertPath chain;
PathBuilder(Cert cert) {
nodes.append(Node(cert));
nit = nodes.begin();
sourceIT = allSources.begin();
}
nextAnchoredPath() {
if (nit == nodes.end()) {
if (sourceIT == allSources.end()) {
}
currentSources += *sourceIT++;
Nodes.sortBySize();
nit = nodes.begin();
}
while (Node node = *nit) {
Node candidate = node.nextParent(currentSources);
if (!candidate) {
nit++;
continue;
}
if (candidate.isAnchored) {
candidates.append(candidate);
} else
nodes.insert(candidate, nit);
}
}
findValidPath() {
while (Node node = nextAnchoredPath()) {
if (node.isValid()) {
chain = node.certPath;
break;
}
rejects.append(node);
}
}
}
#endif