/*
- * Copyright (c) 2006-2010,2012-2015 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2006-2010,2012-2016 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <Security/SecCertificatePath.h>
#include <Security/SecFramework.h>
#include <Security/SecPolicyInternal.h>
+#include <Security/SecTrustSettingsPriv.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFSet.h>
#include <CoreFoundation/CFString.h>
********************************************************/
#define MAX_CHAIN_LENGTH 15
+#define ACCEPT_PATH_SCORE 10000000
/* Forward declaration for use in SecCertificateSource. */
static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents);
typedef void(*SecCertificateSourceParents)(void *, CFArrayRef);
typedef bool(*CopyParents)(SecCertificateSourceRef source,
SecCertificateRef certificate, void *context, SecCertificateSourceParents);
+typedef CFArrayRef(*CopyConstraints)(SecCertificateSourceRef source,
+ SecCertificateRef certificate);
typedef bool(*Contains)(SecCertificateSourceRef source,
SecCertificateRef certificate);
struct SecCertificateSource {
CopyParents copyParents;
+ CopyConstraints copyUsageConstraints;
Contains contains;
};
return source->copyParents(source, certificate, context, callback);
}
+static CFArrayRef SecCertificateSourceCopyUsageConstraints(
+ SecCertificateSourceRef source, SecCertificateRef certificate) {
+ if (source->copyUsageConstraints) {
+ return source->copyUsageConstraints(source, certificate);
+ } else {
+ return NULL;
+ }
+}
+
static bool SecCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
return source->contains(source, certificate);
/* We can make this async or run this on a queue now easily. */
CFErrorRef localError = NULL;
if (!_SecItemCopyMatching(query, &client, &results, &localError)) {
- if (CFErrorGetCode(localError) != errSecItemNotFound) {
+ if (localError && (CFErrorGetCode(localError) != errSecItemNotFound)) {
secdebug("trust", "_SecItemCopyMatching: %@", localError);
}
- CFRelease(localError);
+ CFReleaseSafe(localError);
}
CFRelease(query);
CFTypeRef certs = SecItemCertificateSourceResultsPost(results);
static bool SecItemCertificateSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
- /* Lookup a certificate by issuer and serial number. */
- CFDataRef normalizedSubject =
- SecCertificateGetNormalizedSubjectContent(certificate);
+ /* Look up a certificate by issuer and serial number. */
+ CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate);
+ CFRetainSafe(normalizedIssuer);
CFDataRef serialNumber =
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
SecCertificateCopySerialNumber(certificate, NULL);
*values[] = {
kSecClassCertificate,
kSecMatchLimitOne,
- normalizedSubject,
+ normalizedIssuer,
serialNumber
};
SecurityClient client = {
.allowSyncBubbleKeychain = false,
.isNetworkExtension = false,
};
- CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 5,
- NULL, NULL);
+ CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 4, NULL, NULL);
CFErrorRef localError = NULL;
CFTypeRef results = NULL;
bool ok = _SecItemCopyMatching(query, &client, &results, &localError);
- CFRelease(query);
- CFRelease(serialNumber);
+ CFReleaseSafe(query);
+ CFReleaseSafe(serialNumber);
+ CFReleaseSafe(normalizedIssuer);
CFReleaseSafe(results);
if (!ok) {
if (CFErrorGetCode(localError) != errSecItemNotFound) {
secdebug("trust", "_SecItemCopyMatching: %@", localError);
}
- CFRelease(localError);
+ CFReleaseSafe(localError);
return false;
}
return true;
static SecCertificateSourceRef SecItemCertificateSourceCreate(CFArrayRef accessGroups) {
SecItemCertificateSourceRef result = (SecItemCertificateSourceRef)malloc(sizeof(*result));
result->base.copyParents = SecItemCertificateSourceCopyParents;
+ result->base.copyUsageConstraints = NULL;
result->base.contains = SecItemCertificateSourceContains;
result->accessGroups = accessGroups;
CFRetainSafe(accessGroups);
static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
bool result = false;
-//#ifndef SECITEM_SHIM_OSX
CFArrayRef anchors = NULL;
SecOTAPKIRef otapkiref = NULL;
CFArrayRef cert_datas = NULL;
errOut:
CFReleaseSafe(cert_datas);
CFReleaseSafe(otapkiref);
-//#endif // SECITEM_SHIM_OSX
return result;
}
struct SecCertificateSource kSecSystemAnchorSource = {
SecSystemAnchorSourceCopyParents,
+ NULL,
SecSystemAnchorSourceContains
};
+#if TARGET_OS_IPHONE
// MARK: -
// MARK: SecUserAnchorSource
/********************************************************
- *********** SecUserAnchorSource object ************
+ ************* SecUserAnchorSource object ***************
********************************************************/
static bool SecUserAnchorSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
return true;
}
+static CFArrayRef SecUserAnchorSourceCopyUsageConstraints(
+ SecCertificateSourceRef source, SecCertificateRef certificate) {
+ CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
+ if (!digest)
+ return NULL;
+ CFArrayRef usageConstraints = NULL;
+ bool ok = _SecTrustStoreCopyUsageConstraints(
+ SecTrustStoreForDomain(kSecTrustStoreDomainUser), digest, &usageConstraints, NULL);
+ return (ok) ? usageConstraints : NULL;
+}
+
static bool SecUserAnchorSourceContains(SecCertificateSourceRef source,
SecCertificateRef certificate) {
return SecTrustStoreContains(
struct SecCertificateSource kSecUserAnchorSource = {
SecUserAnchorSourceCopyParents,
+ SecUserAnchorSourceCopyUsageConstraints,
SecUserAnchorSourceContains
};
+#endif
// MARK: -
// MARK: SecMemoryCertificateSource
/********************************************************
- *********** SecMemoryCertificateSource object ************
+ ********** SecMemoryCertificateSource object ***********
********************************************************/
struct SecMemoryCertificateSource {
struct SecCertificateSource base;
SecMemoryCertificateSourceRef result = (SecMemoryCertificateSourceRef)
malloc(sizeof(*result));
result->base.copyParents = SecMemoryCertificateSourceCopyParents;
+ result->base.copyUsageConstraints = NULL;
result->base.contains = SecMemoryCertificateSourceContains;
CFIndex count = CFArrayGetCount(certificates);
result->certificates = CFSetCreateMutable(kCFAllocatorDefault, count,
struct SecCertificateSource kSecCAIssuerSource = {
SecCAIssuerCertificateSourceCopyParents,
+ NULL,
SecCAIssuerCertificateSourceContains
};
+#if (SECTRUST_OSX && TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
+#include <Security/SecItemPriv.h>
+// MARK: -
+// MARK: SecLegacyCertificateSource
+/********************************************************
+ ********** SecLegacyCertificateSource object ***********
+ ********************************************************/
+
+static bool SecLegacyCertificateSourceCopyParents(
+ SecCertificateSourceRef source, SecCertificateRef certificate,
+ void *context, SecCertificateSourceParents callback) {
+ CFArrayRef parents = SecItemCopyParentCertificates(certificate, NULL);
+ callback(context, parents);
+ CFReleaseSafe(parents);
+ return true;
+}
+
+static bool SecLegacyCertificateSourceContains(
+ SecCertificateSourceRef source, SecCertificateRef certificate) {
+ SecCertificateRef cert = SecItemCopyStoredCertificate(certificate, NULL);
+ bool result = (cert) ? true : false;
+ CFReleaseSafe(cert);
+ return result;
+}
+
+struct SecCertificateSource kSecLegacyCertificateSource = {
+ SecLegacyCertificateSourceCopyParents,
+ NULL,
+ SecLegacyCertificateSourceContains
+};
+#endif /* SecLegacyCertificateSource */
+
+#if (SECTRUST_OSX && TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
+// MARK: -
+// MARK: SecLegacyAnchorSource
+/********************************************************
+ ************ SecLegacyAnchorSource object **************
+ ********************************************************/
+
+static bool SecLegacyAnchorSourceCopyParents(
+ SecCertificateSourceRef source, SecCertificateRef certificate,
+ void *context, SecCertificateSourceParents callback) {
+ CFMutableArrayRef anchors = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ CFArrayRef parents = SecItemCopyParentCertificates(certificate, NULL);
+ CFArrayRef trusted = NULL;
+ if (parents == NULL) {
+ goto finish;
+ }
+ /* Get the custom anchors which have been trusted in the user and admin domains.
+ * We don't need system domain roots here, since SecSystemAnchorSource provides those.
+ */
+ OSStatus status = SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted);
+ if (status == errSecSuccess && trusted) {
+ CFIndex index, count = CFArrayGetCount(parents);
+ for (index = 0; index < count; index++) {
+ SecCertificateRef parent = (SecCertificateRef)CFArrayGetValueAtIndex(parents, index);
+ if (parent && CFArrayContainsValue(trusted, CFRangeMake(0, CFArrayGetCount(trusted)), parent)) {
+ CFArrayAppendValue(anchors, parent);
+ }
+ }
+ }
+
+finish:
+ callback(context, anchors);
+ CFReleaseSafe(anchors);
+ CFReleaseSafe(parents);
+ CFReleaseSafe(trusted);
+ return true;
+}
+
+static CFArrayRef SecLegacyAnchorSourceCopyUsageConstraints(
+ SecCertificateSourceRef source, SecCertificateRef certificate) {
+
+ CFArrayRef result = NULL;
+ CFArrayRef userTrustSettings = NULL, adminTrustSettings = NULL;
+
+ OSStatus status = SecTrustSettingsCopyTrustSettings(certificate,
+ kSecTrustSettingsDomainUser,
+ &userTrustSettings);
+ if ((status == errSecSuccess) && (userTrustSettings != NULL)) {
+ result = CFRetain(userTrustSettings);
+ }
+
+ status = SecTrustSettingsCopyTrustSettings(certificate,
+ kSecTrustSettingsDomainAdmin,
+ &adminTrustSettings);
+ /* user trust settings overrule admin trust settings */
+ if ((status == errSecSuccess) && (adminTrustSettings != NULL) && (result == NULL)) {
+ result = CFRetain(adminTrustSettings);
+ }
+
+ CFReleaseNull(userTrustSettings);
+ CFReleaseNull(adminTrustSettings);
+ return result;
+}
+
+static bool SecLegacyAnchorSourceContains(
+ SecCertificateSourceRef source, SecCertificateRef certificate) {
+ if (certificate == NULL) {
+ return false;
+ }
+ CFArrayRef trusted = NULL;
+ bool result = false;
+ OSStatus status = SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted);
+ if ((status == errSecSuccess) && (trusted != NULL)) {
+ CFIndex index, count = CFArrayGetCount(trusted);
+ for (index = 0; index < count; index++) {
+ SecCertificateRef anchor = (SecCertificateRef)CFArrayGetValueAtIndex(trusted, index);
+ if (anchor && CFEqual(anchor, certificate)) {
+ result = true;
+ break;
+ }
+ }
+ }
+ CFReleaseSafe(trusted);
+ return result;
+}
+
+struct SecCertificateSource kSecLegacyAnchorSource = {
+ SecLegacyAnchorSourceCopyParents,
+ SecLegacyAnchorSourceCopyUsageConstraints,
+ SecLegacyAnchorSourceContains
+};
+#endif /* SecLegacyAnchorSource */
+
// MARK: -
// MARK: SecPathBuilder
/********************************************************
CFArrayRef leafDetails;
- CFIndex rejectScore;
+ CFIndex bestPathScore;
bool considerRejected;
bool considerPartials;
struct OpaqueSecPVC path;
SecCertificatePathRef bestPath;
bool bestPathIsEV;
+ bool bestPathIsSHA2;
+ bool denyBestPath;
CFIndex activations;
bool (*state)(SecPathBuilderRef);
/* Forward declarations. */
static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
- SecCertificateRef certificate);
+ SecCertificateRef certificate, SecCertificateSourceRef *foundInSource);
-/* IDEA: policies could be made cabable of replacing incoming anchors and
- anchorsOnly argument values. For example some policies require the
+/* IDEA: policies could be made capable of replacing incoming anchors and
+ anchorsOnly argument values. For example, some policies require the
Apple Inc. CA and not any other anchor. This can be done in
SecPathBuilderLeafCertificateChecks since this only runs once. */
static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder,
static void SecPathBuilderInit(SecPathBuilderRef builder,
CFDataRef clientAuditToken, CFArrayRef certificates,
- CFArrayRef anchors, bool anchorsOnly,
+ CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed,
CFArrayRef policies, CFArrayRef ocspResponses,
CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
SecPVCInit(&builder->path, builder, policies, verifyTime);
builder->bestPath = NULL;
builder->bestPathIsEV = false;
- builder->rejectScore = 0;
+ builder->bestPathIsSHA2 = false;
+ builder->denyBestPath = false;
+ builder->bestPathScore = 0;
/* Let's create all the certificate sources we might want to use. */
builder->certificateSource =
else
builder->anchorSource = NULL;
- /* We always search certificateSource for parents since it includes the
- leaf itself and it might be self signed. */
- CFArrayAppendValue(builder->parentSources, builder->certificateSource);
- if (builder->anchorSource) {
- CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
- }
+ /** Parent Sources
+ ** The order here avoids the most expensive methods if the cheaper methods
+ ** produce an acceptable chain: client-provided, keychains, network-fetched.
+ **/
+ CFArrayAppendValue(builder->parentSources, builder->certificateSource);
builder->itemCertificateSource = SecItemCertificateSourceCreate(accessGroups);
- CFArrayAppendValue(builder->parentSources, builder->itemCertificateSource);
+ if (keychainsAllowed) {
+ CFArrayAppendValue(builder->parentSources, builder->itemCertificateSource);
+#if !TARGET_OS_IPHONE
+ /* On OS X, need additional parent source to search legacy keychain files. */
+ if (kSecLegacyCertificateSource.contains && kSecLegacyCertificateSource.copyParents) {
+ CFArrayAppendValue(builder->parentSources, &kSecLegacyCertificateSource);
+ }
+#endif
+ }
if (anchorsOnly) {
/* Add the system and user anchor certificate db to the search list
- if we don't explicitly trust them. */
+ if we don't explicitly trust them. */
CFArrayAppendValue(builder->parentSources, &kSecSystemAnchorSource);
+#if TARGET_OS_IPHONE
CFArrayAppendValue(builder->parentSources, &kSecUserAnchorSource);
- } else {
- /* Only add the system and user anchor certificate db to the
- anchorSources if we are supposed to trust them. */
- CFArrayAppendValue(builder->anchorSources, &kSecSystemAnchorSource);
- CFArrayAppendValue(builder->anchorSources, &kSecUserAnchorSource);
+#endif
}
- if (builder->canAccessNetwork) {
+ if (keychainsAllowed && builder->canAccessNetwork) {
CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
}
+ /** Anchor Sources
+ ** The order here allows a client-provided anchor to overrule
+ ** a user or admin trust setting which can overrule the system anchors.
+ **/
+ if (builder->anchorSource) {
+ CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
+ }
+ if (!anchorsOnly) {
+ /* Only add the system and user anchor certificate db to the
+ anchorSources if we are supposed to trust them. */
+#if TARGET_OS_IPHONE
+ CFArrayAppendValue(builder->anchorSources, &kSecUserAnchorSource);
+#else
+ if (keychainsAllowed && kSecLegacyAnchorSource.contains && kSecLegacyAnchorSource.copyParents) {
+ CFArrayAppendValue(builder->anchorSources, &kSecLegacyAnchorSource);
+ }
+#endif
+ CFArrayAppendValue(builder->anchorSources, &kSecSystemAnchorSource);
+ }
+
/* Now let's get the leaf cert and turn it into a path. */
SecCertificateRef leaf =
(SecCertificateRef)CFArrayGetValueAtIndex(certificates, 0);
- SecCertificatePathRef path = SecCertificatePathCreate(NULL, leaf);
- CFSetAddValue(builder->allPaths, path);
+ SecCertificateSourceRef source = NULL;
+ bool isAnchor = false;
+ CFArrayRef constraints = NULL;
+ if (SecPathBuilderIsAnchor(builder, leaf, &source)) {
+ isAnchor = true;
+ }
+ if (source) {
+ constraints = SecCertificateSourceCopyUsageConstraints(source, leaf);
+ }
+ SecCertificatePathRef path = SecCertificatePathCreate(NULL, leaf, constraints);
+ CFReleaseSafe(constraints);
+ CFSetAddValue(builder->allPaths, path);
CFArrayAppendValue(builder->partialPaths, path);
- if (SecPathBuilderIsAnchor(builder, leaf)) {
+ if (isAnchor) {
SecCertificatePathSetIsAnchored(path);
CFArrayAppendValue(builder->candidatePaths, path);
}
SecPathBuilderRef SecPathBuilderCreate(CFDataRef clientAuditToken,
CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly,
- CFArrayRef policies, CFArrayRef ocspResponses,
+ bool keychainsAllowed, CFArrayRef policies, CFArrayRef ocspResponses,
CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
SecPathBuilderCompleted completed, const void *context) {
SecPathBuilderRef builder = malloc(sizeof(*builder));
SecPathBuilderInit(builder, clientAuditToken, certificates,
- anchors, anchorsOnly, policies, ocspResponses,
+ anchors, anchorsOnly, keychainsAllowed, policies, ocspResponses,
signedCertificateTimestamps, trustedLogs, verifyTime,
accessGroups, completed, context);
return builder;
builder->canAccessNetwork = allow;
if (allow) {
#if !TARGET_OS_WATCH
- secdebug("http", "network access re-enabled by policy");
+ secinfo("http", "network access re-enabled by policy");
/* re-enabling network_access re-adds kSecCAIssuerSource as
a parent source. */
CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
#else
- secdebug("http", "network access not allowed on WatchOS");
+ secnotice("http", "network access not allowed on WatchOS");
builder->canAccessNetwork = false;
#endif
} else {
- secdebug("http", "network access disabled by policy");
+ secinfo("http", "network access disabled by policy");
/* disabling network_access removes kSecCAIssuerSource from
the list of parent sources. */
CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
return CFRetainSafe(builder->trustedLogs);
}
+/* This function assumes that the input source is an anchor source */
+static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder, SecCertificateSourceRef source,
+ SecCertificateRef certificate) {
+ bool result = false;
+ CFArrayRef constraints = NULL;
+ constraints = SecCertificateSourceCopyUsageConstraints(source, certificate);
+
+ /* Unrestricted certificates:
+ * -those that come from anchor sources with no constraints
+ * -self-signed certificates with empty contraints arrays
+ */
+ Boolean selfSigned = false;
+ require(errSecSuccess == SecCertificateIsSelfSigned(certificate, &selfSigned), out);
+ if ((NULL == source->copyUsageConstraints) ||
+ (constraints && (CFArrayGetCount(constraints) == 0) && selfSigned)) {
+ secinfo("trust", "unrestricted anchor%s",
+ (NULL == source->copyUsageConstraints) ? " source" : "");
+ result = true;
+ goto out;
+ }
+
+ /* Get the trust settings result for the PVC */
+ require(constraints, out);
+ SecTrustSettingsResult settingsResult = kSecTrustSettingsResultInvalid;
+ settingsResult = SecPVCGetTrustSettingsResult(&builder->path,
+ certificate,
+ constraints);
+ if ((selfSigned && settingsResult == kSecTrustSettingsResultTrustRoot) ||
+ (!selfSigned && settingsResult == kSecTrustSettingsResultTrustAsRoot)) {
+ // For our purposes, this is an anchor.
+ secinfo("trust", "complex trust settings anchor");
+ result = true;
+ }
+
+ if (settingsResult == kSecTrustSettingsResultDeny) {
+ /* We consider denied certs "anchors" because the trust decision
+ is set regardless of building the chain further. The policy
+ validation will handle rejecting this chain. */
+ secinfo("trust", "complex trust settings denied anchor");
+ result = true;
+ }
+
+out:
+ CFReleaseNull(constraints);
+ return result;
+}
+
+/* Source returned in foundInSource has the same lifetime as the builder. */
static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
- SecCertificateRef certificate) {
- /* We always look through all anchor sources. */
- 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;
+ SecCertificateRef certificate, SecCertificateSourceRef *foundInSource) {
+ /* We look through the anchor sources in order. They are ordered in
+ SecPathBuilderInit so that process anchors override user anchors which
+ override system anchors. */
+ CFIndex count = CFArrayGetCount(builder->anchorSources);
+ CFIndex ix;
+ for (ix = 0; ix < count; ++ix) {
+ SecCertificateSourceRef source = (SecCertificateSourceRef)
+ CFArrayGetValueAtIndex(builder->anchorSources, ix);
+ if (SecCertificateSourceContains(source, certificate)) {
+ if (foundInSource)
+ *foundInSource = source;
+ if (SecPathBuilderIsAnchorPerConstraints(builder, source, certificate)) {
+ return true;
+ }
+ }
+ }
+ return false;
}
/* Return false if path is not a partial, if path was a valid candidate it
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 %" PRIdCFIndex " candidate %s", num_parents,
- (is_anchor ? "anchors" : "parents"));
for (parentIX = 0; parentIX < num_parents; ++parentIX) {
SecCertificateRef parent = (SecCertificateRef)
CFArrayGetValueAtIndex(parents, parentIX);
/* FIXME Add more sanity checks to see that parent really can be
a parent of partial_root. subjectKeyID == authorityKeyID,
signature algorithm matches public key algorithm, etc. */
- SecCertificatePathRef path = SecCertificatePathCreate(partial, parent);
+ SecCertificateSourceRef source = NULL;
+ bool is_anchor = SecPathBuilderIsAnchor(builder, parent, &source);
+ CFArrayRef constraints = (source) ? SecCertificateSourceCopyUsageConstraints(source, parent) : NULL;
+ SecCertificatePathRef path = SecCertificatePathCreate(partial, parent, constraints);
+ CFReleaseSafe(constraints);
if (!path)
continue;
if (!CFSetContainsValue(builder->allPaths, path)) {
return;
}
- CFIndex rejectScore = builder->rejectScore;
+ CFIndex bestPathScore = builder->bestPathScore;
CFIndex score = SecCertificatePathScore(builder->path.path,
SecPVCGetVerifyTime(&builder->path));
replace any previously accepted or rejected non EV chains with the
current one. */
if (pvc->is_ev && !builder->bestPathIsEV) {
- rejectScore = 0;
+ bestPathScore = 0;
}
#if 0
}
#endif
- /* Do this last so that changes to rejectScore above will take affect. */
- if (!builder->bestPath || score > rejectScore) {
+ /* Do this last so that changes to bestPathScore above will take effect. */
+ if (!builder->bestPath || score > bestPathScore) {
if (builder->bestPath) {
- secdebug("reject",
- "replacing %sev %s score: %ld with %sev reject score: %" PRIdCFIndex " %@",
+ secinfo("reject",
+ "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
(builder->bestPathIsEV ? "" : "non "),
- (builder->rejectScore == INTPTR_MAX ? "accept" : "reject"),
- builder->rejectScore,
+ (builder->bestPathScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
+ builder->bestPathScore,
(pvc->is_ev ? "" : "non "), (long)score, builder->path.path);
} else {
- secdebug("reject", "%sev reject score: %" PRIdCFIndex " %@",
+ secinfo("reject", "%sev score: %" PRIdCFIndex " %@",
(pvc->is_ev ? "" : "non "), score, builder->path.path);
}
- builder->rejectScore = score;
+ builder->bestPathScore = score;
builder->bestPath = pvc->path;
builder->bestPathIsEV = pvc->is_ev;
+ builder->denyBestPath = SecPVCCheckUsageConstraints(pvc);
} else {
- secdebug("reject", "%sev reject score: %" PRIdCFIndex " lower than %" PRIdCFIndex " %@",
- (pvc->is_ev ? "" : "non "), score, rejectScore, builder->path.path);
+ secinfo("reject", "%sev score: %" PRIdCFIndex " lower than %" PRIdCFIndex " %@",
+ (pvc->is_ev ? "" : "non "), score, bestPathScore, 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; /* CFIndex is signed long which is INTPTR_T */
- builder->bestPathIsEV = pvc->is_ev;
+ bool isSHA2 = !SecCertificatePathHasWeakHash(pvc->path);
+ CFIndex bestScore = builder->bestPathScore;
+ /* Score this path. Note that all points awarded or deducted in
+ * SecCertificatePathScore are < 100,000 */
+ CFIndex currScore = (SecCertificatePathScore(pvc->path, pvc->verifyTime) +
+ ACCEPT_PATH_SCORE + // 10,000,000 points for accepting
+ ((pvc->is_ev) ? 1000000 : 0)); //1,000,000 points for EV
+ if (currScore > bestScore) {
+ // current path is better than existing best path
+ secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
+ (builder->bestPathIsEV ? "" : "non "),
+ (builder->bestPathScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
+ builder->bestPathScore,
+ (pvc->is_ev ? "" : "non "), (long)currScore, builder->path.path);
+
+ builder->bestPathScore = currScore;
+ builder->bestPathIsEV = pvc->is_ev;
+ builder->bestPathIsSHA2 = isSHA2;
builder->bestPath = pvc->path;
+ builder->denyBestPath = SecPVCCheckUsageConstraints(pvc); /* should always be false */
}
- /* If we found the best accept we can we want to switch directly to the
+ /* If we found the best accept we can, we want to switch directly to the
SecPathBuilderComputeDetails state here, since we're done. */
- if (pvc->is_ev || !pvc->optionally_ev)
+ if ((pvc->is_ev || !pvc->optionally_ev) && isSHA2)
builder->state = SecPathBuilderComputeDetails;
else
builder->state = SecPathBuilderGetNext;
#if 0
if (!builder->caller_wants_details) {
SecPVCSetPath(pvc, builder->bestPath, NULL);
- pvc->result = builder->rejectScore == INTPTR_MAX;
+ pvc->result = builder->bestPathScore > ACCEPT_PATH_SCORE;
builder->state = SecPathBuilderReportResult;
return true;
}
bool completed = SecPVCPathChecks(pvc);
/* Reject the certificate if it was accepted before but we failed it now. */
- if (builder->rejectScore == INTPTR_MAX && !pvc->result) {
- builder->rejectScore = 0;
+ if (builder->bestPathScore > ACCEPT_PATH_SCORE && !pvc->result) {
+ builder->bestPathScore = 0;
}
CFReleaseSafe(details);
if (pvc->rvcs) {
CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
if (nextUpdate == 0) {
+ /* populate revocation info for failed revocation check */
CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
kCFBooleanFalse); /* iOS key */
CFDictionarySetValue(pvc->info, kSecTrustRevocationChecked,
kCFBooleanFalse); /* unified API key */
- } else {
- haveRevocationResponse = true;
- CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
- CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationValidUntilKey,
- validUntil); /* iOS key */
- CFDictionarySetValue(pvc->info, kSecTrustRevocationValidUntilDate,
- validUntil); /* unified API key */
- CFRelease(validUntil);
- CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
- kCFBooleanTrue); /* iOS key */
- CFDictionarySetValue(pvc->info, kSecTrustRevocationChecked,
- kCFBooleanTrue); /* unified API key */
}
}
}
+ if (pvc->info && pvc->result && pvc->rvcs) {
+ CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
+ if (nextUpdate != 0) {
+ /* always populate revocation info for successful revocation check */
+ haveRevocationResponse = true;
+ CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
+ CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationValidUntilKey,
+ validUntil); /* iOS key */
+ CFDictionarySetValue(pvc->info, kSecTrustRevocationValidUntilDate,
+ validUntil); /* unified API key */
+ CFRelease(validUntil);
+ CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
+ kCFBooleanTrue); /* iOS key */
+ CFDictionarySetValue(pvc->info, kSecTrustRevocationChecked,
+ kCFBooleanTrue); /* unified API key */
+ }
+ }
+
if (pvc->info && pvc->result && pvc->response_required && !haveRevocationResponse) {
- builder->rejectScore = 0;
+ builder->bestPathScore = 0;
SecPVCSetResultForced(pvc, kSecPolicyCheckRevocationResponseRequired,
0, kCFBooleanFalse, true);
}
kCFBooleanTrue);
}
+ if (pvc->info && pvc->is_ct_whitelisted && pvc->result) {
+ CFDictionarySetValue(pvc->info, kSecTrustInfoCertificateTransparencyWhiteListKey,
+ kCFBooleanTrue);
+ }
+
/* This will trigger the outer step function to call the completion
function. */
bool SecPathBuilderStep(SecPathBuilderRef builder) {
if (builder->activations) {
secdebug("async", "activations: %lu returning true",
- builder->activations);
+ builder->activations);
return true;
}
if (builder->state) {
secdebug("async", "waiting for async reply, exiting");
/* A state returned false, it's waiting for network traffic. Let's
- return. */
+ return. */
return true;
}
if (builder->activations) {
/* There is still at least one other running instance of this builder
- somewhere on the stack, we let that instance take care of sending
- the client a response. */
+ somewhere on the stack, we let that instance take care of sending
+ the client a response. */
return false;
}
- SecTrustResultType result = (builder->rejectScore == INTPTR_MAX
- ? kSecTrustResultUnspecified : kSecTrustResultRecoverableTrustFailure);
+ SecTrustResultType result = kSecTrustResultInvalid;
+ if (builder->bestPathScore > ACCEPT_PATH_SCORE) {
+ result = kSecTrustResultUnspecified;
+ } else if (builder->denyBestPath) {
+ result = kSecTrustResultDeny;
+ } else {
+ result = kSecTrustResultRecoverableTrustFailure;
+ }
- secdebug("trust", "completed: %@ details: %@ result: %d",
+ secinfo("trust", "completed: %@ details: %@ result: %d",
builder->bestPath, builder->path.details, result);
if (builder->completed) {
}
void
-SecTrustServerEvaluateBlock(CFDataRef clientAuditToken, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, 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, __unused CFArrayRef accessGroups, void (^evaluated)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error)) {
SecTrustServerEvaluationCompleted userData = Block_copy(evaluated);
/* Call the actual evaluator function. */
SecPathBuilderRef builder = SecPathBuilderCreate(clientAuditToken,
certificates, anchors,
- anchorsOnly, policies,
+ anchorsOnly, keychainsAllowed, policies,
responses, SCTs, trustedLogs,
verifyTime, accessGroups,
SecTrustServerEvaluateCompleted, userData);
// NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
-SecTrustResultType SecTrustServerEvaluate(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, 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 *pdetails, CFDictionaryRef *pinfo, SecCertificatePathRef *pchain, CFErrorRef *perror) {
dispatch_semaphore_t done = dispatch_semaphore_create(0);
__block SecTrustResultType result = kSecTrustResultInvalid;
- SecTrustServerEvaluateBlock(NULL, certificates, anchors, anchorsOnly, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error) {
+ SecTrustServerEvaluateBlock(NULL, certificates, anchors, anchorsOnly, keychainsAllowed, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error) {
result = tr;
if (tr == kSecTrustResultInvalid) {
if (perror) {