#include <securityd/SecTrustServer.h>
#include <securityd/SecPolicyServer.h>
#include <securityd/SecTrustStoreServer.h>
#include <securityd/SecCAIssuerRequest.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecItem.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecCertificatePath.h>
#include <Security/SecFramework.h>
#include <Security/SecPolicyInternal.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 <MacErrors.h>
#include "SecRSAKey.h"
#include <libDER/oids.h>
#include <security_utilities/debugging.h>
#include <Security/SecInternal.h>
#include "securityd_client.h"
#include "securityd_server.h"
const struct digest_to_ix_t *
digest_to_anchor_ix (register const char *str, register unsigned int len);
const struct subject_to_ix_t *
subject_to_anchor_ix (register const char *str, register unsigned int len);
const struct ev_oids *
ev_oid (register const char *str, register unsigned int len);
#ifndef SECITEM_SHIM_OSX
#include "evroots.h"
#endif
#define MAX_CHAIN_LENGTH 15
static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents);
#pragma mark -
#pragma mark SecCertificateSource
typedef struct SecCertificateSource *SecCertificateSourceRef;
typedef void(*SecCertificateSourceParents)(void *, CFArrayRef);
typedef bool(*CopyParents)(SecCertificateSourceRef source,
SecCertificateRef certificate, void *context, SecCertificateSourceParents);
typedef bool(*Contains)(SecCertificateSourceRef source,
SecCertificateRef certificate);
struct SecCertificateSource {
CopyParents copyParents;
Contains contains;
};
static bool SecCertificateSourceCopyParents(SecCertificateSourceRef source,
SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
return source->copyParents(source, certificate, context, callback);
}
static bool SecCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
return source->contains(source, certificate);
}
#pragma mark -
#pragma mark SecItemCertificateSource
static bool SecItemCertificateSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
CFDataRef normalizedIssuer =
SecCertificateGetNormalizedIssuerContent(certificate);
const void *keys[] = {
kSecClass,
kSecReturnRef,
kSecMatchLimit,
kSecAttrSubject
},
*values[] = {
kSecClassCertificate,
kCFBooleanTrue,
kSecMatchLimitAll,
normalizedIssuer
};
CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 4,
NULL, NULL);
CFTypeRef results = NULL;
OSStatus status = SecItemCopyMatching(query, &results);
CFRelease(query);
if (status) {
secdebug("trust", "SecItemCopyMatching status: %lu", status);
}
callback(context, results);
CFReleaseSafe(results);
return true;
}
static bool SecItemCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
CFDataRef normalizedSubject =
SecCertificateGetNormalizedSubjectContent(certificate);
CFDataRef serialNumber =
SecCertificateCopySerialNumber(certificate);
const void *keys[] = {
kSecClass,
kSecReturnRef,
kSecMatchLimit,
kSecAttrIssuer,
kSecAttrSerialNumber
},
*values[] = {
kSecClassCertificate,
kCFBooleanTrue,
kSecMatchLimitOne,
normalizedSubject,
serialNumber
};
CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 5,
NULL, NULL);
OSStatus status = SecItemCopyMatching(query, NULL);
CFRelease(query);
CFRelease(serialNumber);
if (status) {
if (status != errSecItemNotFound) {
secdebug("trust", "SecItemCopyMatching returned %d", status);
}
return false;
}
return true;
}
struct SecCertificateSource kSecItemCertificateSource = {
SecItemCertificateSourceCopyParents,
SecItemCertificateSourceContains
};
#if 0
#pragma mark -
#pragma mark SecSystemAnchorSource
struct SecSystemAnchorSource {
struct SecCertificateSource base;
CFSetRef digests;
};
typedef struct SecSystemAnchorSource *SecSystemAnchorSourceRef;
static pthread_once_t kSecSystemAnchorSourceInit = PTHREAD_ONCE_INIT;
static SecCertificateSourceRef kSecSystemAnchorSource = NULL;
static bool SecSystemAnchorSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
callback(context, NULL);
return true;
}
static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
SecSystemAnchorSourceRef sasource = (SecSystemAnchorSourceRef)source;
CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
return CFSetContainsValue(sasource->digests, digest);
}
static void SecSystemAnchorSourceInit(void) {
SecSystemAnchorSourceRef result = (SecSystemAnchorSourceRef)
malloc(sizeof(*result));
result->base.copyParents = SecSystemAnchorSourceCopyParents;
result->base.contains = SecSystemAnchorSourceContains;
CFDataRef xmlData = SecFrameworkCopyResourceContents(
CFSTR("SystemAnchors"), CFSTR("plist"), NULL);
CFPropertyListRef plist = CFPropertyListCreateFromXMLData(
kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL);
if (plist) {
if (CFGetTypeID(plist) == CFDictionaryGetTypeID()) {
result->digests = (CFSetRef)plist;
} else {
secwarning("SystemAnchors plist is wrong type.");
CFRelease(plist);
}
}
if (!result->digests) {
result->digests = CFSetCreate(kCFAllocatorDefault, NULL, 0,
&kCFTypeSetCallBacks);
}
kSecSystemAnchorSource = (SecCertificateSourceRef)result;
}
static SecCertificateSourceRef SecSystemAnchorSourceGetDefault(void) {
pthread_once(&kSecSystemAnchorSourceInit, SecSystemAnchorSourceInit);
return kSecSystemAnchorSource;
}
#else
#pragma mark -
#pragma mark SecSystemAnchorSource
static bool SecSystemAnchorSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
#ifndef SECITEM_SHIM_OSX
CFMutableArrayRef parents = NULL;
CFDataRef nic = SecCertificateGetNormalizedIssuerContent(certificate);
assert((unsigned long)CFDataGetLength(nic)<UINT_MAX);
const struct subject_to_ix_t *i2x =
subject_to_anchor_ix((const char *)CFDataGetBytePtr(nic),
(unsigned int)CFDataGetLength(nic));
require_quiet(i2x, errOut);
int anchor_ix = i2x->anchor_ix;
CFIndex capacity = 0;
do {
++capacity;
} while ((anchor_ix = anchorslist[anchor_ix].next_same_subject));
parents = CFArrayCreateMutable(kCFAllocatorDefault, capacity,
&kCFTypeArrayCallBacks);
anchor_ix = i2x->anchor_ix;
do {
const void *anchor = NULL;
CFDataRef anchor_data = NULL;
require_quiet(anchor_data = CFDataCreateWithBytesNoCopy(
kCFAllocatorDefault, (const UInt8 *)anchorslist[anchor_ix].data,
anchorslist[anchor_ix].length, kCFAllocatorNull), errOut);
anchor = SecCertificateCreateWithData(kCFAllocatorDefault,
anchor_data);
CFRelease(anchor_data);
if (anchor) {
CFArrayAppendValue(parents, anchor);
CFRelease(anchor);
}
} while ((anchor_ix = anchorslist[anchor_ix].next_same_subject));
errOut:
callback(context, parents);
CFReleaseSafe(parents);
#endif
return true;
}
static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
#ifndef SECITEM_SHIM_OSX
CFDataRef nic = SecCertificateGetNormalizedSubjectContent(certificate);
assert((unsigned long)CFDataGetLength(nic)<UINT_MAX);
const struct subject_to_ix_t *i2x =
subject_to_anchor_ix((const char *)CFDataGetBytePtr(nic),
(unsigned int)CFDataGetLength(nic));
require_quiet(i2x, errOut);
CFIndex cert_length = SecCertificateGetLength(certificate);
const UInt8 *cert_data = SecCertificateGetBytePtr(certificate);
int anchor_ix = i2x->anchor_ix;
do {
if (cert_length == anchorslist[anchor_ix].length &&
!memcmp(anchorslist[anchor_ix].data, cert_data, cert_length))
return true;
} while ((anchor_ix = anchorslist[anchor_ix].next_same_subject));
errOut:
#endif
return false;
}
struct SecCertificateSource kSecSystemAnchorSource = {
SecSystemAnchorSourceCopyParents,
SecSystemAnchorSourceContains
};
#pragma mark -
#pragma mark SecUserAnchorSource
static bool SecUserAnchorSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
CFArrayRef parents = SecTrustStoreCopyParents(
SecTrustStoreForDomain(kSecTrustStoreDomainUser), certificate);
callback(context, parents);
CFReleaseSafe(parents);
return true;
}
static bool SecUserAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
return SecTrustStoreContains(
SecTrustStoreForDomain(kSecTrustStoreDomainUser), certificate);
}
struct SecCertificateSource kSecUserAnchorSource = {
SecUserAnchorSourceCopyParents,
SecUserAnchorSourceContains
};
#endif
#pragma mark -
#pragma mark SecMemoryCertificateSource
struct SecMemoryCertificateSource {
struct SecCertificateSource base;
CFMutableSetRef certificates;
CFMutableDictionaryRef subjects;
};
typedef struct SecMemoryCertificateSource *SecMemoryCertificateSourceRef;
static bool SecMemoryCertificateSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
SecMemoryCertificateSourceRef msource =
(SecMemoryCertificateSourceRef)source;
CFDataRef normalizedIssuer =
SecCertificateGetNormalizedIssuerContent(certificate);
CFArrayRef parents = CFDictionaryGetValue(msource->subjects,
normalizedIssuer);
secdebug("trust", "%@ parents -> %@", certificate, parents);
callback(context, parents);
return true;
}
static bool SecMemoryCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
SecMemoryCertificateSourceRef msource =
(SecMemoryCertificateSourceRef)source;
return CFSetContainsValue(msource->certificates, certificate);
}
static void dictAddValueToArrayForKey(CFMutableDictionaryRef dict,
const void *key, const void *value) {
if (!key)
return;
CFMutableArrayRef values =
(CFMutableArrayRef)CFDictionaryGetValue(dict, key);
if (!values) {
values = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
CFDictionaryAddValue(dict, key, values);
CFRelease(values);
}
if (values)
CFArrayAppendValue(values, value);
}
static void SecMemoryCertificateSourceApplierFunction(const void *value,
void *context) {
SecMemoryCertificateSourceRef msource =
(SecMemoryCertificateSourceRef)context;
SecCertificateRef certificate = (SecCertificateRef)value;
if (CFSetContainsValue(msource->certificates, certificate))
return;
CFSetAddValue(msource->certificates, certificate);
CFDataRef key = SecCertificateGetNormalizedSubjectContent(certificate);
dictAddValueToArrayForKey(msource->subjects, key, value);
}
static SecCertificateSourceRef SecMemoryCertificateSourceCreate(
CFArrayRef certificates) {
SecMemoryCertificateSourceRef result = (SecMemoryCertificateSourceRef)
malloc(sizeof(*result));
result->base.copyParents = SecMemoryCertificateSourceCopyParents;
result->base.contains = SecMemoryCertificateSourceContains;
CFIndex count = CFArrayGetCount(certificates);
result->certificates = CFSetCreateMutable(kCFAllocatorDefault, count,
&kCFTypeSetCallBacks);
result->subjects = CFDictionaryCreateMutable(kCFAllocatorDefault,
count, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRange range = { 0, count };
CFArrayApplyFunction(certificates, range,
SecMemoryCertificateSourceApplierFunction, result);
return (SecCertificateSourceRef)result;
}
static void SecMemoryCertificateSourceDestroy(
SecCertificateSourceRef source) {
SecMemoryCertificateSourceRef msource =
(SecMemoryCertificateSourceRef)source;
CFRelease(msource->certificates);
CFRelease(msource->subjects);
free(msource);
}
#pragma mark -
#pragma mark SecCAIssuerCertificateSource
static bool SecCAIssuerCertificateSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
return SecCAIssuerCopyParents(certificate, context, callback);
}
static bool SecCAIssuerCertificateSourceContains(
SecCertificateSourceRef source, SecCertificateRef certificate) {
return false;
}
struct SecCertificateSource kSecCAIssuerSource = {
SecCAIssuerCertificateSourceCopyParents,
SecCAIssuerCertificateSourceContains
};
#pragma mark -
#pragma mark SecPathBuilder
struct SecPathBuilder {
SecCertificateSourceRef certificateSource;
SecCertificateSourceRef anchorSource;
CFMutableArrayRef anchorSources;
CFIndex nextParentSource;
CFMutableArrayRef parentSources;
CFMutableSetRef allPaths;
CFMutableArrayRef partialPaths;
CFMutableArrayRef rejectedPaths;
CFMutableArrayRef candidatePaths;
CFIndex partialIX;
CFArrayRef leafDetails;
CFIndex rejectScore;
bool considerRejected;
bool considerPartials;
bool canAccessNetwork;
struct OpaqueSecPVC path;
SecCertificatePathRef bestPath;
bool bestPathIsEV;
CFIndex activations;
bool (*state)(SecPathBuilderRef);
SecPathBuilderCompleted completed;
const void *context;
};
static bool SecPathBuilderGetNext(SecPathBuilderRef builder);
static bool SecPathBuilderValidatePath(SecPathBuilderRef builder);
static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder);
static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder);
static bool SecPathBuilderReportResult(SecPathBuilderRef builder);
static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
SecCertificateRef certificate);
static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder,
SecCertificatePathRef path) {
CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
builder->leafDetails = CFArrayCreate(kCFAllocatorDefault,
(const void **)&certDetail, 1, &kCFTypeArrayCallBacks);
CFRelease(certDetail);
SecPVCRef pvc = &builder->path;
SecPVCSetPath(pvc, path, builder->leafDetails);
builder->considerRejected = !SecPVCLeafChecks(pvc);
}
static void SecPathBuilderInit(SecPathBuilderRef builder,
CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly,
CFArrayRef policies, CFAbsoluteTime verifyTime,
SecPathBuilderCompleted completed, const void *context) {
secdebug("alloc", "%p", builder);
CFAllocatorRef allocator = kCFAllocatorDefault;
builder->nextParentSource = 1;
builder->considerPartials = false;
builder->canAccessNetwork = true;
builder->anchorSources = CFArrayCreateMutable(allocator, 0, NULL);
builder->parentSources = CFArrayCreateMutable(allocator, 0, NULL);
builder->allPaths = CFSetCreateMutable(allocator, 0,
&kCFTypeSetCallBacks);
builder->partialPaths = CFArrayCreateMutable(allocator, 0, NULL);
builder->rejectedPaths = CFArrayCreateMutable(allocator, 0, NULL);
builder->candidatePaths = CFArrayCreateMutable(allocator, 0, NULL);
builder->partialIX = 0;
SecPVCInit(&builder->path, builder, policies, verifyTime);
builder->bestPath = NULL;
builder->bestPathIsEV = false;
builder->rejectScore = 0;
builder->certificateSource =
SecMemoryCertificateSourceCreate(certificates);
if (anchors)
builder->anchorSource = SecMemoryCertificateSourceCreate(anchors);
else
builder->anchorSource = NULL;
CFArrayAppendValue(builder->parentSources, builder->certificateSource);
if (builder->anchorSource) {
CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
}
CFArrayAppendValue(builder->parentSources, &kSecItemCertificateSource);
if (anchorsOnly) {
CFArrayAppendValue(builder->parentSources, &kSecSystemAnchorSource);
CFArrayAppendValue(builder->parentSources, &kSecUserAnchorSource);
} else {
CFArrayAppendValue(builder->anchorSources, &kSecSystemAnchorSource);
CFArrayAppendValue(builder->anchorSources, &kSecUserAnchorSource);
}
CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
SecCertificateRef leaf =
(SecCertificateRef)CFArrayGetValueAtIndex(certificates, 0);
SecCertificatePathRef path = SecCertificatePathCreate(NULL, leaf);
CFSetAddValue(builder->allPaths, path);
CFArrayAppendValue(builder->partialPaths, path);
if (SecPathBuilderIsAnchor(builder, leaf)) {
SecCertificatePathSetIsAnchored(path);
CFArrayAppendValue(builder->candidatePaths, path);
}
SecPathBuilderLeafCertificateChecks(builder, path);
CFRelease(path);
builder->activations = 0;
builder->state = SecPathBuilderGetNext;
builder->completed = completed;
builder->context = context;
}
SecPathBuilderRef SecPathBuilderCreate(CFArrayRef certificates,
CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies,
CFAbsoluteTime verifyTime,
SecPathBuilderCompleted completed, const void *context) {
SecPathBuilderRef builder = malloc(sizeof(*builder));
SecPathBuilderInit(builder, certificates, anchors, anchorsOnly,
policies, verifyTime, completed, context);
return builder;
}
static void SecPathBuilderDestroy(SecPathBuilderRef builder) {
secdebug("alloc", "%p", builder);
if (builder->anchorSource)
SecMemoryCertificateSourceDestroy(builder->anchorSource);
if (builder->certificateSource)
SecMemoryCertificateSourceDestroy(builder->certificateSource);
CFReleaseSafe(builder->anchorSources);
CFReleaseSafe(builder->parentSources);
CFReleaseSafe(builder->allPaths);
CFReleaseSafe(builder->partialPaths);
CFReleaseSafe(builder->rejectedPaths);
CFReleaseSafe(builder->candidatePaths);
CFReleaseSafe(builder->leafDetails);
SecPVCDelete(&builder->path);
}
bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder) {
return builder->canAccessNetwork;
}
void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder, bool allow) {
if (builder->canAccessNetwork != allow) {
builder->canAccessNetwork = allow;
if (allow) {
secdebug("http", "network access re-enabled by policy");
CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
} else {
secdebug("http", "network access disabled by policy");
CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
CFRangeMake(0, CFArrayGetCount(builder->parentSources)),
&kSecCAIssuerSource);
if (ix >= 0)
CFArrayRemoveValueAtIndex(builder->parentSources, ix);
}
}
}
static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
SecCertificateRef certificate) {
CFIndex count = CFArrayGetCount(builder->anchorSources);
CFIndex ix;
for (ix = 0; ix < count; ++ix) {
SecCertificateSourceRef source = (SecCertificateSourceRef)
CFArrayGetValueAtIndex(builder->anchorSources, ix);
if (SecCertificateSourceContains(source, certificate)) {
return true;
}
}
return false;
}
static bool SecPathBuilderIsPartial(SecPathBuilderRef builder,
SecCertificatePathRef path) {
SecPVCRef pvc = &builder->path;
SecPVCSetPath(pvc, path, NULL);
if (!builder->considerRejected && !SecPVCParentCertificateChecks(pvc,
SecPVCGetCertificateCount(pvc) - 1)) {
secdebug("trust", "Found rejected path %@", path);
CFArrayAppendValue(builder->rejectedPaths, path);
return false;
}
SecPathVerifyStatus vstatus = SecCertificatePathVerify(path);
if (vstatus == kSecPathVerifyFailed) {
secdebug("trust", "Verify failed for path %@", path);
return false;
}
if (vstatus == kSecPathVerifySuccess) {
if (SecCertificatePathIsAnchored(path)) {
secdebug("trust", "Adding candidate %@", path);
CFArrayAppendValue(builder->candidatePaths, path);
return false;
}
}
return true;
}
static void SecPathBuilderProccessParents(SecPathBuilderRef builder,
SecCertificatePathRef partial, CFArrayRef parents) {
CFIndex rootIX = SecCertificatePathGetCount(partial) - 1;
CFIndex num_parents = parents ? CFArrayGetCount(parents) : 0;
CFIndex parentIX;
bool is_anchor = SecCertificatePathGetNextSourceIndex(partial) <=
CFArrayGetCount(builder->anchorSources);
secdebug("trust", "found %d candidate %s", num_parents,
(is_anchor ? "anchors" : "parents"));
for (parentIX = 0; parentIX < num_parents; ++parentIX) {
SecCertificateRef parent = (SecCertificateRef)
CFArrayGetValueAtIndex(parents, parentIX);
CFIndex ixOfParent = SecCertificatePathGetIndexOfCertificate(partial,
parent);
if (ixOfParent != kCFNotFound) {
if (ixOfParent == rootIX) {
SecCertificatePathSetSelfIssued(partial);
}
continue;
}
SecCertificatePathRef path = SecCertificatePathCreate(partial, parent);
if (!path)
continue;
if (!CFSetContainsValue(builder->allPaths, path)) {
CFSetAddValue(builder->allPaths, path);
if (is_anchor)
SecCertificatePathSetIsAnchored(path);
if (SecPathBuilderIsPartial(builder, path)) {
CFArrayInsertValueAtIndex(builder->partialPaths,
++builder->partialIX, path);
secdebug("trust", "Adding partial for parent %d/%d %@",
parentIX + 1, num_parents, path);
}
secdebug("trust", "found new path %@", path);
}
CFRelease(path);
}
}
static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents) {
SecPathBuilderRef builder = (SecPathBuilderRef)context;
SecCertificatePathRef partial = (SecCertificatePathRef)
CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
secdebug("async", "%@ parents %@", partial, parents);
SecPathBuilderProccessParents(builder, partial, parents);
builder->state = SecPathBuilderGetNext;
SecPathBuilderStep(builder);
}
static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
if (CFArrayGetCount(builder->candidatePaths)) {
SecCertificatePathRef path = (SecCertificatePathRef)
CFArrayGetValueAtIndex(builder->candidatePaths, 0);
CFArrayRemoveValueAtIndex(builder->candidatePaths, 0);
secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
path);
SecPVCSetPath(&builder->path, path, NULL);
builder->state = SecPathBuilderValidatePath;
return true;
}
if (builder->considerRejected) {
CFIndex rejectedIX = CFArrayGetCount(builder->rejectedPaths);
if (rejectedIX) {
rejectedIX--;
SecCertificatePathRef path = (SecCertificatePathRef)
CFArrayGetValueAtIndex(builder->rejectedPaths, rejectedIX);
if (SecPathBuilderIsPartial(builder, path)) {
CFArrayInsertValueAtIndex(builder->partialPaths,
++builder->partialIX, path);
}
CFArrayRemoveValueAtIndex(builder->rejectedPaths, rejectedIX);
return true;
}
}
if (builder->partialIX < 0) {
CFIndex num_sources = CFArrayGetCount(builder->parentSources);
if (builder->nextParentSource < num_sources) {
builder->nextParentSource++;
secdebug("trust", "broading search to %d/%d sources",
builder->nextParentSource, num_sources);
} else {
if (!builder->considerRejected) {
builder->considerRejected = true;
secdebug("trust", "considering rejected paths");
} else if (!builder->considerPartials) {
builder->considerPartials = true;
secdebug("trust", "considering partials");
} else {
builder->state = SecPathBuilderComputeDetails;
return true;
}
}
builder->partialIX = CFArrayGetCount(builder->partialPaths) - 1;
secdebug("trust", "re-checking %d partials", builder->partialIX + 1);
return true;
}
SecCertificatePathRef partial = (SecCertificatePathRef)
CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
if (builder->considerPartials) {
--builder->partialIX;
SecPVCSetPath(&builder->path, partial, NULL);
builder->state = SecPathBuilderValidatePath;
return true;
}
secdebug("trust", "looking for parents of partial %d/%d: %@",
builder->partialIX + 1, CFArrayGetCount(builder->partialPaths),
partial);
CFIndex sourceIX = SecCertificatePathGetNextSourceIndex(partial);
CFIndex num_anchor_sources = CFArrayGetCount(builder->anchorSources);
if (sourceIX < num_anchor_sources + builder->nextParentSource) {
SecCertificateSourceRef source;
if (sourceIX < num_anchor_sources) {
source = (SecCertificateSourceRef)
CFArrayGetValueAtIndex(builder->anchorSources, sourceIX);
secdebug("trust", "searching anchor source %d/%d", sourceIX + 1,
num_anchor_sources);
} else {
CFIndex parentIX = sourceIX - num_anchor_sources;
source = (SecCertificateSourceRef)
CFArrayGetValueAtIndex(builder->parentSources, parentIX);
secdebug("trust", "searching parent source %d/%d", parentIX + 1,
builder->nextParentSource);
}
SecCertificatePathSetNextSourceIndex(partial, sourceIX + 1);
SecCertificateRef root = SecCertificatePathGetRoot(partial);
return SecCertificateSourceCopyParents(source, root,
builder, SecPathBuilderExtendPaths);
} else {
--builder->partialIX;
}
return true;
}
static void SecPathBuilderReject(SecPathBuilderRef builder) {
check(builder);
SecPVCRef pvc = &builder->path;
builder->state = SecPathBuilderGetNext;
if (builder->bestPathIsEV && !pvc->is_ev) {
return;
}
CFIndex rejectScore = builder->rejectScore;
CFIndex score = SecCertificatePathScore(builder->path.path,
SecPVCGetVerifyTime(&builder->path));
if (pvc->is_ev && !builder->bestPathIsEV) {
rejectScore = 0;
}
#if 0
if (pvc->is_ev) {
builder->state = SecPathBuilderComputeDetails;
}
#endif
if (!builder->bestPath || score > rejectScore) {
if (builder->bestPath) {
secdebug("reject",
"replacing %sev %s score: %ld with %sev reject score: %d %@",
(builder->bestPathIsEV ? "" : "non "),
(builder->rejectScore == INTPTR_MAX ? "accept" : "reject"),
builder->rejectScore,
(pvc->is_ev ? "" : "non "), score, builder->path.path);
} else {
secdebug("reject", "%sev reject score: %d %@",
(pvc->is_ev ? "" : "non "), score, builder->path.path);
}
builder->rejectScore = score;
builder->bestPath = pvc->path;
builder->bestPathIsEV = pvc->is_ev;
} else {
secdebug("reject", "%sev reject score: %d lower than %d %@",
(pvc->is_ev ? "" : "non "), score, rejectScore, builder->path.path);
}
}
static void SecPathBuilderAccept(SecPathBuilderRef builder) {
check(builder);
SecPVCRef pvc = &builder->path;
if (pvc->is_ev || !builder->bestPathIsEV) {
secdebug("accept", "replacing %sev accept with %sev %@",
(builder->bestPathIsEV ? "" : "non "),
(pvc->is_ev ? "" : "non "), builder->path.path);
builder->rejectScore = INTPTR_MAX;
builder->bestPathIsEV = pvc->is_ev;
builder->bestPath = pvc->path;
}
if (pvc->is_ev || !pvc->optionally_ev)
builder->state = SecPathBuilderComputeDetails;
else
builder->state = SecPathBuilderGetNext;
}
static bool SecPathBuilderValidatePath(SecPathBuilderRef builder) {
SecPVCRef pvc = &builder->path;
if (builder->considerRejected) {
SecPathBuilderReject(builder);
return true;
}
builder->state = SecPathBuilderDidValidatePath;
return SecPVCPathChecks(pvc);
}
static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder) {
SecPVCRef pvc = &builder->path;
if (pvc->result) {
SecPathBuilderAccept(builder);
} else {
SecPathBuilderReject(builder);
}
assert(builder->state != SecPathBuilderDidValidatePath);
return true;
}
static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder) {
SecPVCRef pvc = &builder->path;
#if 0
if (!builder->caller_wants_details) {
SecPVCSetPath(pvc, builder->bestPath, NULL);
pvc->result = builder->rejectScore == INTPTR_MAX;
builder->state = SecPathBuilderReportResult;
return true;
}
#endif
CFIndex ix, pathLength = SecCertificatePathGetCount(builder->bestPath);
CFMutableArrayRef details = CFArrayCreateMutableCopy(kCFAllocatorDefault,
pathLength, builder->leafDetails);
SecPVCSetPath(pvc, builder->bestPath, details);
pvc->optionally_ev = builder->bestPathIsEV;
pvc->info = CFDictionaryCreateMutable(kCFAllocatorDefault,
0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for (ix = 1; ix < pathLength; ++ix) {
CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFArrayAppendValue(details, certDetail);
CFRelease(certDetail);
SecPVCParentCertificateChecks(pvc, ix);
SecPVCBlackListedKeyChecks(pvc, ix);
}
builder->state = SecPathBuilderReportResult;
bool completed = SecPVCPathChecks(pvc);
if (builder->rejectScore == INTPTR_MAX && !pvc->result) {
builder->rejectScore = 0;
}
return completed;
}
static bool SecPathBuilderReportResult(SecPathBuilderRef builder) {
SecPVCRef pvc = &builder->path;
if (pvc->info && pvc->is_ev && pvc->result) {
CFDictionarySetValue(pvc->info, kSecTrustInfoExtendedValidationKey,
kCFBooleanTrue);
SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
CFStringRef leafCompanyName = SecCertificateCopyCompanyName(leaf);
if (leafCompanyName) {
CFDictionarySetValue(pvc->info, kSecTrustInfoCompanyNameKey,
leafCompanyName);
CFRelease(leafCompanyName);
}
if (pvc->rvcs) {
CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
if (nextUpdate == 0) {
CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
kCFBooleanFalse);
} else {
CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationValidUntilKey,
validUntil);
CFRelease(validUntil);
CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
kCFBooleanTrue);
}
}
}
builder->state = NULL;
return false;
}
bool SecPathBuilderStep(SecPathBuilderRef builder) {
if (builder->activations) {
secdebug("async", "activations: %lu returning true",
builder->activations);
return true;
}
secdebug("async", "activations: %lu", builder->activations);
builder->activations++;
while (builder->state && builder->state(builder));
--builder->activations;
if (builder->state) {
secdebug("async", "waiting for async reply, exiting");
return true;
}
if (builder->activations) {
return false;
}
SecTrustResultType result = (builder->rejectScore == INTPTR_MAX
? kSecTrustResultUnspecified : kSecTrustResultRecoverableTrustFailure);
secdebug("trust", "completed: %@ details: %@ result: %d",
builder->bestPath, builder->path.details, result);
if (builder->completed) {
builder->completed(builder->context, builder->bestPath,
builder->path.details, builder->path.info, result);
}
SecPathBuilderDestroy(builder);
free(builder);
return false;
}
#pragma mark -
#pragma mark SecTrustServer
OSStatus
SecTrustServerEvaluateAsync(CFDictionaryRef args_in,
SecPathBuilderCompleted completed, const void *userData) {
OSStatus status = paramErr;
CFArrayRef certificates = NULL, anchors = NULL, policies = NULL;
CFArrayRef certificatesData = (CFArrayRef)CFDictionaryGetValue(args_in, kSecTrustCertificatesKey);
require_quiet(certificatesData && CFGetTypeID(certificatesData) == CFArrayGetTypeID(), errOut);
certificates = SecCertificateDataArrayCopyArray(certificatesData);
require_quiet(certificates
&& CFGetTypeID(certificates) == CFArrayGetTypeID()
&& CFArrayGetCount(certificates) > 0, errOut);
CFArrayRef anchorsData = (CFArrayRef)CFDictionaryGetValue(args_in, kSecTrustAnchorsKey);
if (anchorsData) {
require_quiet(CFGetTypeID(anchorsData) == CFArrayGetTypeID(), errOut);
anchors = SecCertificateDataArrayCopyArray(anchorsData);
}
bool anchorsOnly = CFDictionaryContainsKey(args_in, kSecTrustAnchorsOnlyKey);
CFArrayRef serializedPolicies = (CFArrayRef)CFDictionaryGetValue(args_in, kSecTrustPoliciesKey);
if (serializedPolicies) {
require_quiet(CFGetTypeID(serializedPolicies) == CFArrayGetTypeID(), errOut);
policies = SecPolicyArrayDeserialize(serializedPolicies);
}
CFDateRef verifyDate = (CFDateRef)CFDictionaryGetValue(args_in, kSecTrustVerifyDateKey);
require_quiet(verifyDate && CFGetTypeID(verifyDate) == CFDateGetTypeID(), errOut);
CFAbsoluteTime verifyTime = CFDateGetAbsoluteTime(verifyDate);
SecPathBuilderRef builder = SecPathBuilderCreate(certificates, anchors,
anchorsOnly, policies, verifyTime, completed, userData);
status = SecPathBuilderStep(builder) ? errSecWaitForCallback : noErr;
errOut:
CFReleaseSafe(policies);
CFReleaseSafe(anchors);
CFReleaseSafe(certificates);
return status;
}
struct SecTrustEvaluationContext {
CFTypeRef args_out;
bool running;
};
static void
SecTrustServerEvaluateDone(const void *userData,
SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
SecTrustResultType result) {
struct SecTrustEvaluationContext *tec =
(struct SecTrustEvaluationContext *)userData;
CFDictionaryRef args_out;
CFNumberRef resultNumber = NULL;
CFArrayRef chain_certs = NULL;
resultNumber = CFNumberCreate(NULL, kCFNumberSInt32Type, &result);
chain_certs = SecCertificatePathCopyArray(chain);
const void *out_keys[] = { kSecTrustChainKey, kSecTrustDetailsKey,
kSecTrustInfoKey, kSecTrustResultKey };
const void *out_values[] = { chain_certs, details, info, resultNumber };
args_out = (CFTypeRef)CFDictionaryCreate(kCFAllocatorDefault, out_keys,
out_values, sizeof(out_keys) / sizeof(*out_keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFReleaseSafe(chain_certs);
CFReleaseSafe(resultNumber);
tec->args_out = args_out;
if (tec->running) {
CFRunLoopStop(CFRunLoopGetCurrent());
}
}
OSStatus
SecTrustServerEvaluate(CFDictionaryRef args_in, CFTypeRef *args_out) {
OSStatus status;
struct SecTrustEvaluationContext tec;
tec.args_out = NULL;
tec.running = false;
status = SecTrustServerEvaluateAsync(args_in, SecTrustServerEvaluateDone,
&tec);
if (status == noErr || status == errSecWaitForCallback) {
if (status == errSecWaitForCallback) {
status = noErr;
tec.running = true;
CFRunLoopRun();
}
*args_out = tec.args_out;
}
return status;
}