#include <Security/SecTrustPriv.h>
#include <Security/SecItem.h>
#include <Security/SecCertificateInternal.h>
-#include <Security/SecCertificatePath.h>
#include <Security/SecFramework.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecPolicyInternal.h>
#include "OTATrustUtilities.h"
#include "personalization.h"
#include <utilities/SecInternalReleasePriv.h>
+#include <mach/mach_time.h>
#if TARGET_OS_OSX
#include <Security/SecTaskPriv.h>
#endif
#define MAX_CHAIN_LENGTH 15
+#define MAX_NUM_CHAINS 100
#define ACCEPT_PATH_SCORE 10000000
/* Forward declaration for use in SecCertificateSource. */
********************************************************/
struct SecPathBuilder {
dispatch_queue_t queue;
+ uint64_t startTime;
CFDataRef clientAuditToken;
SecCertificateSourceRef certificateSource;
SecCertificateSourceRef itemCertificateSource;
Note that this is the only container in which certificatePath
objects are retained.
Every certificatePath being considered is always in allPaths and in at
- most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
+ least one of partialPaths, rejectedPaths, or candidatePath,
all of which don't retain their values. */
CFMutableSetRef allPaths;
SecCertificatePathVCRef path;
unsigned int asyncJobCount;
bool online_revocation;
+ bool trusted_revocation;
CFStringRef revocation_check_method;
SecCertificatePathVCRef bestPath;
bool (*state)(SecPathBuilderRef);
SecPathBuilderCompleted completed;
const void *context;
+ TrustAnalyticsBuilder * analyticsData;
};
/* State functions. Return false if a async job was scheduled, return
secdebug("alloc", "%p", builder);
CFAllocatorRef allocator = kCFAllocatorDefault;
+ builder->analyticsData = calloc(1, sizeof(TrustAnalyticsBuilder));
+ builder->analyticsData->start_time = mach_absolute_time();
+
builder->clientAuditToken = (CFDataRef)
((clientAuditToken) ? CFRetain(clientAuditToken) : NULL);
builder->queue = dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL);
builder->nextParentSource = 1;
#if !TARGET_OS_WATCH
+ /* <rdar://32728029> */
builder->canAccessNetwork = true;
#endif
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->allPaths = CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks);
+ builder->partialPaths = CFArrayCreateMutable(allocator, 0, NULL); // Does not retain, allPaths retains members. See declaration.
+ builder->rejectedPaths = CFArrayCreateMutable(allocator, 0, NULL); // Does not retain, allPaths retains members. See declaration.
+ builder->candidatePaths = CFArrayCreateMutable(allocator, 0, NULL); // Does not retain, allPaths retains members. See declaration.
/* Init the policy verification context. */
builder->pvcs = malloc(sizeof(SecPVCRef));
SecPathBuilderSetPath(builder, path);
CFRelease(path);
- /* Set the revocation context */
-
/* Next step is to process the leaf. We do that work on the builder queue
* to avoid blocking the main thread with database lookups. */
builder->state = SecPathBuilderProcessLeaf;
CFReleaseNull(builder->info);
CFReleaseNull(builder->exceptions);
+ free(builder->analyticsData);
+ builder->analyticsData = NULL;
+
if (builder->pvcs) {
CFIndex ix;
for (ix = 0; ix < builder->pvcCount; ix++) {
a parent source. */
CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
#else
+ /* <rdar://32728029> */
secnotice("http", "network access not allowed on WatchOS");
builder->canAccessNetwork = false;
#endif
return CFRetainSafe(builder->trustedLogs);
}
+SecCertificateSourceRef SecPathBuilderGetAppAnchorSource(SecPathBuilderRef builder)
+{
+ return builder->anchorSource;
+}
+
+CFSetRef SecPathBuilderGetAllPaths(SecPathBuilderRef builder)
+{
+ return builder->allPaths;
+}
+
+TrustAnalyticsBuilder *SecPathBuilderGetAnalyticsData(SecPathBuilderRef builder)
+{
+ return builder->analyticsData;
+}
+
SecCertificatePathVCRef SecPathBuilderGetBestPath(SecPathBuilderRef builder)
{
return builder->bestPath;
return builder->verifyTime;
}
+bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder) {
+ __block bool validIntermediates = false;
+ SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool *stop) {
+ CFArrayForEach(pvc->policies, ^(const void *value) {
+ SecPolicyRef policy = (SecPolicyRef)value;
+ if (CFDictionaryContainsKey(policy->_options, kSecPolicyCheckTemporalValidity)) {
+ validIntermediates = true;
+ *stop = true;
+ }
+ });
+ });
+ return validIntermediates;
+}
+
CFIndex SecPathBuilderGetCertificateCount(SecPathBuilderRef builder) {
return SecCertificatePathVCGetCount(builder->path);
}
secdebug("rvc", "revocation force online check");
}
+bool SecPathBuilderGetCheckRevocationIfTrusted(SecPathBuilderRef builder) {
+ return builder->trusted_revocation;
+}
+
+void SecPathBuilderSetCheckRevocationIfTrusted(SecPathBuilderRef builder) {
+ builder->trusted_revocation = true;
+ secdebug("rvc", "revocation check only if trusted");
+}
+
CFArrayRef SecPathBuilderGetExceptions(SecPathBuilderRef builder) {
return builder->exceptions;
}
}
void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder, CFStringRef key,
- CFIndex ix, CFTypeRef result, bool force,
- SecTrustResultType resultType) {
+ CFIndex ix, CFTypeRef result, bool force) {
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
SecPVCSetResultForced(pvc, key, ix, result, force);
- pvc->result = resultType;
});
}
return acceptPath;
}
-static SecPVCRef SecPathBuilderGetResultPVC(SecPathBuilderRef builder) {
+SecPVCRef SecPathBuilderGetResultPVC(SecPathBuilderRef builder) {
/* Return the first PVC that passed */
__block SecPVCRef resultPVC = NULL;
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool *stop) {
secdebug("trust", "Adding candidate %@", path);
CFArrayAppendValue(builder->candidatePaths, path);
}
- /* The path is not partial if the last cert is self-signed. */
- if ((SecCertificatePathVCSelfSignedIndex(path) >= 0) &&
- (SecCertificatePathVCSelfSignedIndex(path) == SecCertificatePathVCGetCount(path)-1)) {
+ /* The path is not partial if the last cert is self-signed.
+ * The path is also not partial if the issuer of the last cert was the subject
+ * of a previous cert in the chain, indicating a cycle in the graph. See <rdar://33136765>. */
+ if (((SecCertificatePathVCSelfSignedIndex(path) >= 0) &&
+ (SecCertificatePathVCSelfSignedIndex(path) == SecCertificatePathVCGetCount(path)-1)) ||
+ SecCertificatePathVCIsCycleInGraph(path)) {
+ if (!builder->considerRejected) {
+ secdebug("trust", "Adding non-partial non-anchored reject %@", path);
+ CFArrayAppendValue(builder->rejectedPaths, path);
+ } else {
+ /* This path was previously rejected as unanchored non-partial, but now that
+ * we're considering rejected paths, this is a candidate. */
+ secdebug("trust", "Adding non-partial non-anchored candidate %@", path);
+ CFArrayAppendValue(builder->candidatePaths, path);
+ }
return false;
}
}
/* Found pinning policies. Apply them to the path builder. */
CFArrayRef newRules = CFDictionaryGetValue(results, kSecPinningDbKeyRules);
CFStringRef dbPolicyName = CFDictionaryGetValue(results, kSecPinningDbKeyPolicyName);
- secinfo("trust", "found pinning %lu %@ policies for hostname %@, policyName %@",
+ secinfo("SecPinningDb", "found pinning %lu %@ policies for hostname %@, policyName %@",
(unsigned long)CFArrayGetCount(newRules), dbPolicyName, hostname, policyName);
CFIndex newRulesIX;
for (newRulesIX = 0; newRulesIX < CFArrayGetCount(newRules); newRulesIX++) {
directly.
FIXME we might not want to consider partial paths that
are subsets of other partial paths, or not consider them
- at all if we already have an anchored reject. */
+ at all if we already have an (unpreferred) accept or anchored reject */
if (!builder->considerRejected) {
builder->considerRejected = true;
secdebug("trust", "considering rejected paths");
return true;
}
+ /* Don't try to extend partials anymore if we already have too many chains. */
+ if (CFSetGetCount(builder->allPaths) > MAX_NUM_CHAINS) {
+ secnotice("trust", "not building any more paths, already have %" PRIdCFIndex,
+ CFSetGetCount(builder->allPaths));
+ builder->partialIX = -1;
+ return true;
+ }
+
/* Attempt to extend this partial path with another certificate. This
should give us a list of potential parents to consider. */
secdebug("trust", "looking for parents of partial %" PRIdCFIndex "/%" PRIdCFIndex ": %@",
builder->state = SecPathBuilderDidValidatePath;
/* Revocation checking is now done before path checks, to ensure that
- isAllowlisted will be set correctly for the subsequent path checks. */
+ we have OCSP responses for CT checking and that isAllowlisted is
+ appropriately set for other checks. */
bool completed = SecPathBuilderCheckRevocation(builder);
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
* this is the state we call back into once all the asynchronous
* revocation check calls are done. */
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
- SecPVCPathCheckRevocationRequired(pvc);
+ SecPVCPathCheckRevocationResponsesReceived(pvc);
});
if (SecPathBuilderIsOkResult(builder)) {
__block CFIndex ix, pathLength = SecCertificatePathVCGetCount(builder->bestPath);
__block bool completed = true;
-
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
SecPVCComputeDetails(pvc, builder->bestPath);
completed &= SecPathBuilderCheckRevocation(builder);
builder->state = SecPathBuilderReportResult;
+ /* Check revocation responses. */
SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
- SecPVCPathCheckRevocationRequired(pvc);
+ SecPVCPathCheckRevocationResponsesReceived(pvc);
});
/* Reject the certificate if it was accepted before but we failed it now. (Should not happen anymore.) */
CFAbsoluteTime nextUpdate = SecCertificatePathVCGetEarliestNextUpdate(builder->bestPath);
if (nextUpdate != 0) {
#else
- /* We don't do networking on watchOS, so we can't require OCSP for EV */
+ /* <rdar://32728029> We don't do networking on watchOS, so we can't require OCSP for EV */
{
{
#endif
builder->bestPath, pvc->details, result);
if (builder->completed) {
- SecCertificatePathRef resultPath = SecCertificatePathVCCopyCertificatePath(builder->bestPath);
+ CFArrayRef resultPath = SecCertificatePathVCCopyCertificates(builder->bestPath);
builder->completed(builder->context, resultPath,
pvc->details, builder->info, result);
CFReleaseNull(resultPath);
****************** SecTrustServer **********************
********************************************************/
-typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error);
+typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, CFArrayRef chain, CFErrorRef error);
static void
SecTrustServerEvaluateCompleted(const void *userData,
- SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
+ CFArrayRef chain, CFArrayRef details, CFDictionaryRef info,
SecTrustResultType result) {
SecTrustServerEvaluationCompleted evaluated = (SecTrustServerEvaluationCompleted)userData;
+ TrustdHealthAnalyticsLogEvaluationCompleted();
evaluated(result, details, info, chain, NULL);
Block_release(evaluated);
}
void
-SecTrustServerEvaluateBlock(CFDataRef clientAuditToken, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, CFArrayRef accessGroups, CFArrayRef exceptions, void (^evaluated)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error)) {
+SecTrustServerEvaluateBlock(CFDataRef clientAuditToken, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, CFArrayRef accessGroups, CFArrayRef exceptions, void (^evaluated)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, CFArrayRef chain, CFErrorRef error)) {
/* We need an array containing at least one certificate to proceed. */
if (!isArray(certificates) || !(CFArrayGetCount(certificates) > 0)) {
CFErrorRef certError = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificate, NULL);
// NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
-SecTrustResultType SecTrustServerEvaluate(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef exceptions, CFArrayRef *pdetails, CFDictionaryRef *pinfo, SecCertificatePathRef *pchain, CFErrorRef *perror) {
+SecTrustResultType SecTrustServerEvaluate(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef exceptions, CFArrayRef *pdetails, CFDictionaryRef *pinfo, CFArrayRef *pchain, CFErrorRef *perror) {
dispatch_semaphore_t done = dispatch_semaphore_create(0);
__block SecTrustResultType result = kSecTrustResultInvalid;
- SecTrustServerEvaluateBlock(NULL, certificates, anchors, anchorsOnly, keychainsAllowed, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, exceptions, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error) {
+ SecTrustServerEvaluateBlock(NULL, certificates, anchors, anchorsOnly, keychainsAllowed, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, exceptions, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, CFArrayRef chain, CFErrorRef error) {
result = tr;
if (tr == kSecTrustResultInvalid) {
if (perror) {