SecCertificateServer.c [plain text]
#include <CoreFoundation/CoreFoundation.h>
#include <AssertMacros.h>
#include <libDER/libDER.h>
#include <libDER/oids.h>
#include <Security/SecCertificate.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecItem.h>
#include <Security/SecInternal.h>
#include <utilities/SecIOFormat.h>
#include <utilities/SecCFError.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/debugging.h>
#include <securityd/policytree.h>
#include <securityd/SecPolicyServer.h>
#include <securityd/SecCertificateServer.h>
#include <securityd/SecRevocationServer.h>
struct SecCertificateVC {
CFRuntimeBase _base;
SecCertificateRef certificate;
CFArrayRef usageConstraints;
bool optionallyEV;
bool isWeakHash;
bool require_revocation_response;
};
CFGiblisWithHashFor(SecCertificateVC)
static void SecCertificateVCDestroy(CFTypeRef cf) {
SecCertificateVCRef cvc = (SecCertificateVCRef) cf;
CFReleaseNull(cvc->certificate);
CFReleaseNull(cvc->usageConstraints);
}
static Boolean SecCertificateVCCompare(CFTypeRef cf1, CFTypeRef cf2) {
SecCertificateVCRef cv1 = (SecCertificateVCRef) cf1;
SecCertificateVCRef cv2 = (SecCertificateVCRef) cf2;
if (!CFEqual(cv1->certificate, cv2->certificate)) {
return false;
}
if (cv1->usageConstraints && cv2->usageConstraints &&
!CFEqual(cv1->usageConstraints, cv2->usageConstraints)) {
return false;
}
return true;
}
static CFHashCode SecCertificateVCHash(CFTypeRef cf) {
SecCertificateVCRef cvc = (SecCertificateVCRef) cf;
CFHashCode hashCode = 0;
hashCode += CFHash(cvc->certificate);
if (cvc->usageConstraints) {
hashCode += CFHash(cvc->usageConstraints);
}
return hashCode;
}
static CFStringRef SecCertificateVCCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
SecCertificateVCRef cvc = (SecCertificateVCRef)cf;
return CFCopyDescription(cvc->certificate);
}
static bool SecCertificateVCCouldBeEV(SecCertificateRef certificate) {
CFMutableDictionaryRef keySizes = NULL;
CFNumberRef rsaSize = NULL, ecSize = NULL;
bool isEV = false;
const SecCECertificatePolicies *cp;
cp = SecCertificateGetCertificatePolicies(certificate);
require_quiet(cp && cp->numPolicies > 0, notEV);
uint32_t ix = 0;
bool found_ev_anchor_for_leaf_policy = false;
for (ix = 0; ix < cp->numPolicies; ++ix) {
if (SecPolicyIsEVPolicy(&cp->policies[ix].policyIdentifier)) {
found_ev_anchor_for_leaf_policy = true;
}
}
require_quiet(found_ev_anchor_for_leaf_policy, notEV);
const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
if (bc) {
require_action_quiet(bc->isCA == false, notEV,
secnotice("ev", "Leaf has invalid basic constraints"));
}
SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
if (ku) {
require_action_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) == 0, notEV,
secnotice("ev", "Leaf has invalid key usage %u", ku));
}
#if 0
SecCertificateCopyExtendedKeyUsage(certificate);
#endif
CFAbsoluteTime jan2014 = 410227200;
require_quiet(ecSize = CFNumberCreateWithCFIndex(NULL, 256), notEV);
require_quiet(keySizes = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks), notEV);
CFDictionaryAddValue(keySizes, kSecAttrKeyTypeEC, ecSize);
if (SecCertificateNotValidBefore(certificate) < jan2014) {
require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 1024), notEV);
CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize);
require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV,
secnotice("ev", "Leaf's public key is too small for issuance before 2014"));
} else {
require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 2048), notEV);
CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize);
require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV,
secnotice("ev", "Leaf's public key is too small for issuance after 2013"));
}
CFAbsoluteTime jul2016 = 489024000;
CFAbsoluteTime notAfter = SecCertificateNotValidAfter(certificate);
CFAbsoluteTime notBefore = SecCertificateNotValidBefore(certificate);
if (SecCertificateNotValidBefore(certificate) < jul2016) {
CFAbsoluteTime maxPeriod = 60*60*24*(365*5+2);
require_action_quiet(notAfter - notBefore <= maxPeriod, notEV,
secnotice("ev", "Leaf's validity period is more than 60 months"));
} else {
CFAbsoluteTime maxPeriod = 60*60*24*(365*3+2*31+30+1);
require_action_quiet(notAfter - notBefore <= maxPeriod, notEV,
secnotice("ev", "Leaf has validity period longer than 39 months and issued after 30 June 2016"));
}
CFAbsoluteTime jan2016 = 473299200;
if (SecCertificateNotValidBefore(certificate) > jan2016) {
require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate) > kSecSignatureHashAlgorithmSHA1,
notEV, secnotice("ev", "Leaf was issued with SHA-1 after 2015"));
}
isEV = true;
notEV:
CFReleaseNull(rsaSize);
CFReleaseNull(ecSize);
CFReleaseNull(keySizes);
return isEV;
}
SecCertificateVCRef SecCertificateVCCreate(SecCertificateRef certificate, CFArrayRef usageConstraints) {
if (!certificate) { return NULL; }
CFIndex size = sizeof(struct SecCertificateVC);
SecCertificateVCRef result =
(SecCertificateVCRef)_CFRuntimeCreateInstance(kCFAllocatorDefault,
SecCertificateVCGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
result->certificate = CFRetainSafe(certificate);
result->isWeakHash = SecCertificateIsWeakHash(certificate);
result->optionallyEV = SecCertificateVCCouldBeEV(certificate);
CFArrayRef emptyArray = NULL;
if (!usageConstraints) {
require_action_quiet(emptyArray = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks), exit, CFReleaseNull(result));
usageConstraints = emptyArray;
}
result->usageConstraints = CFRetainSafe(usageConstraints);
exit:
CFReleaseNull(emptyArray);
return result;
}
struct SecCertificatePathVC {
CFRuntimeBase _base;
CFIndex count;
CFIndex nextParentSource;
CFIndex lastVerifiedSigner;
CFIndex selfIssued;
bool isSelfSigned;
bool isAnchored;
policy_tree_t policy_tree;
uint8_t policy_tree_verification_result;
bool isEV;
bool isCT;
bool is_allowlisted;
bool hasStrongHashes;
void * rvcs;
CFIndex rvcCount;
CFIndex score;
bool pathValidated;
bool checkedIssuers;
CFIndex unknownCAIndex;
SecPathCTPolicy requiresCT;
CFAbsoluteTime issuanceTime;
SecCertificateVCRef certificates[];
};
CFGiblisWithHashFor(SecCertificatePathVC)
static void SecCertificatePathVCPrunePolicyTree(SecCertificatePathVCRef certificatePath) {
if (certificatePath->policy_tree) {
policy_tree_prune(&certificatePath->policy_tree);
}
}
void SecCertificatePathVCDeleteRVCs(SecCertificatePathVCRef path) {
if (path->rvcs) {
CFIndex certIX, certCount = path->rvcCount;
for (certIX = 0; certIX < certCount; ++certIX) {
SecRVCRef rvc = &((SecRVCRef)path->rvcs)[certIX];
SecRVCDelete(rvc);
}
free(path->rvcs);
path->rvcs = NULL;
}
}
static void SecCertificatePathVCDestroy(CFTypeRef cf) {
SecCertificatePathVCRef certificatePath = (SecCertificatePathVCRef) cf;
CFIndex ix;
SecCertificatePathVCDeleteRVCs(certificatePath);
SecCertificatePathVCPrunePolicyTree(certificatePath);
for (ix = 0; ix < certificatePath->count; ++ix) {
CFReleaseNull(certificatePath->certificates[ix]);
}
}
static Boolean SecCertificatePathVCCompare(CFTypeRef cf1, CFTypeRef cf2) {
SecCertificatePathVCRef cp1 = (SecCertificatePathVCRef) cf1;
SecCertificatePathVCRef cp2 = (SecCertificatePathVCRef) cf2;
if (cp1->count != cp2->count)
return false;
CFIndex ix;
for (ix = 0; ix < cp1->count; ++ix) {
if (!CFEqual(cp1->certificates[ix], cp2->certificates[ix]))
return false;
}
return true;
}
static CFHashCode SecCertificatePathVCHash(CFTypeRef cf) {
SecCertificatePathVCRef certificatePath = (SecCertificatePathVCRef) cf;
CFHashCode hashCode = 0;
CFIndex ix;
for (ix = 0; ix < certificatePath->count; ++ix) {
hashCode += CFHash(certificatePath->certificates[ix]);
}
return hashCode;
}
static CFStringRef SecCertificatePathVCCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
SecCertificatePathVCRef certificatePath = (SecCertificatePathVCRef) cf;
CFMutableStringRef desc = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringRef typeStr = CFCopyTypeIDDescription(CFGetTypeID(cf));
CFStringAppendFormat(desc, NULL,
CFSTR("<%@ certs: "), typeStr);
CFRelease(typeStr);
CFIndex ix;
for (ix = 0; ix < certificatePath->count; ++ix) {
if (ix > 0) {
CFStringAppend(desc, CFSTR(", "));
}
CFStringRef str = CFCopyDescription(certificatePath->certificates[ix]);
CFStringAppend(desc, str);
CFRelease(str);
}
CFStringAppend(desc, CFSTR(" >"));
return desc;
}
SecCertificatePathVCRef SecCertificatePathVCCreate(SecCertificatePathVCRef path,
SecCertificateRef certificate, CFArrayRef usageConstraints) {
CFAllocatorRef allocator = kCFAllocatorDefault;
check(certificate);
CFIndex count;
CFIndex selfIssued, lastVerifiedSigner;
bool isSelfSigned;
if (path) {
count = path->count + 1;
lastVerifiedSigner = path->lastVerifiedSigner;
selfIssued = path->selfIssued;
isSelfSigned = path->isSelfSigned;
} else {
count = 1;
lastVerifiedSigner = 0;
selfIssued = -1;
isSelfSigned = false;
}
CFIndex size = sizeof(struct SecCertificatePathVC) +
count * sizeof(SecCertificateRef);
SecCertificatePathVCRef result =
(SecCertificatePathVCRef)_CFRuntimeCreateInstance(allocator,
SecCertificatePathVCGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
memset((char*)result + sizeof(result->_base), 0,
sizeof(*result) - sizeof(result->_base));
result->count = count;
result->lastVerifiedSigner = lastVerifiedSigner;
result->selfIssued = selfIssued;
result->isSelfSigned = isSelfSigned;
CFIndex ix;
for (ix = 0; ix < count - 1; ++ix) {
result->certificates[ix] = path->certificates[ix];
CFRetain(result->certificates[ix]);
}
SecCertificateVCRef cvc = SecCertificateVCCreate(certificate, usageConstraints);
result->certificates[count - 1] = cvc;
return result;
}
SecCertificatePathVCRef SecCertificatePathVCCopyFromParent(
SecCertificatePathVCRef path, CFIndex skipCount) {
CFAllocatorRef allocator = kCFAllocatorDefault;
CFIndex count;
CFIndex selfIssued, lastVerifiedSigner;
bool isSelfSigned;
if (skipCount < 0 || path->count < 1 + skipCount)
return NULL;
count = path->count - skipCount;
lastVerifiedSigner = path->lastVerifiedSigner > skipCount
? path->lastVerifiedSigner - skipCount : 0;
selfIssued = path->selfIssued >= skipCount
? path->selfIssued - skipCount : -1;
isSelfSigned = path->selfIssued >= 0 ? path->isSelfSigned : false;
CFIndex size = sizeof(struct SecCertificatePathVC) +
count * sizeof(SecCertificateRef);
SecCertificatePathVCRef result =
(SecCertificatePathVCRef)_CFRuntimeCreateInstance(allocator,
SecCertificatePathVCGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
memset((char*)result + sizeof(result->_base), 0,
sizeof(*result) - sizeof(result->_base));
result->count = count;
result->lastVerifiedSigner = lastVerifiedSigner;
result->selfIssued = selfIssued;
result->isSelfSigned = isSelfSigned;
result->isAnchored = path->isAnchored;
CFIndex ix;
for (ix = 0; ix < count; ++ix) {
CFIndex pathIX = ix + skipCount;
result->certificates[ix] = path->certificates[pathIX];
CFRetain(result->certificates[ix]);
}
return result;
}
SecCertificatePathVCRef SecCertificatePathVCCopyAddingLeaf(SecCertificatePathVCRef path,
SecCertificateRef leaf) {
CFAllocatorRef allocator = kCFAllocatorDefault;
CFIndex count;
CFIndex selfIssued, lastVerifiedSigner;
bool isSelfSigned;
SecKeyRef issuerKey = SecCertificatePathVCCopyPublicKeyAtIndex(path, 0);
if (!issuerKey)
return NULL;
OSStatus status = SecCertificateIsSignedBy(leaf, issuerKey);
CFRelease(issuerKey);
if (status)
return NULL;
count = path->count + 1;
lastVerifiedSigner = path->lastVerifiedSigner + 1;
selfIssued = path->selfIssued;
isSelfSigned = path->isSelfSigned;
CFIndex size = sizeof(struct SecCertificatePathVC) +
count * sizeof(SecCertificateRef);
SecCertificatePathVCRef result =
(SecCertificatePathVCRef)_CFRuntimeCreateInstance(allocator,
SecCertificatePathVCGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
memset((char*)result + sizeof(result->_base), 0,
sizeof(*result) - sizeof(result->_base));
result->count = count;
result->lastVerifiedSigner = lastVerifiedSigner;
result->selfIssued = selfIssued;
result->isSelfSigned = isSelfSigned;
result->isAnchored = path->isAnchored;
CFIndex ix;
for (ix = 1; ix < count; ++ix) {
result->certificates[ix] = path->certificates[ix - 1];
CFRetain(result->certificates[ix]);
}
SecCertificateVCRef leafVC = SecCertificateVCCreate(leaf, NULL);
result->certificates[0] = leafVC;
return result;
}
CFArrayRef SecCertificatePathVCCopyCertificates(SecCertificatePathVCRef path) {
CFMutableArrayRef outCerts = NULL;
size_t count = path->count;
require_quiet(outCerts = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks), exit);
SecCertificatePathVCForEachCertificate(path, ^(SecCertificateRef cert, bool * __unused stop) {
CFArrayAppendValue(outCerts, cert);
});
exit:
return outCerts;
}
CFArrayRef SecCertificatePathVCCreateSerialized(SecCertificatePathVCRef path) {
CFMutableArrayRef serializedCerts = NULL;
require_quiet(path, exit);
size_t count = path->count;
require_quiet(serializedCerts = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks), exit);
SecCertificatePathVCForEachCertificate(path, ^(SecCertificateRef cert, bool * __unused stop) {
CFDataRef certData = SecCertificateCopyData(cert);
if (certData) {
CFArrayAppendValue(serializedCerts, certData);
CFRelease(certData);
}
});
exit:
return serializedCerts;
}
void SecCertificatePathVCSetSelfIssued(
SecCertificatePathVCRef certificatePath) {
if (certificatePath->selfIssued >= 0) {
secdebug("trust", "%@ is already issued at %" PRIdCFIndex, certificatePath,
certificatePath->selfIssued);
return;
}
secdebug("trust", "%@ is self issued", certificatePath);
certificatePath->selfIssued = certificatePath->count - 1;
if (certificatePath->selfIssued >= 0 && !certificatePath->isSelfSigned) {
SecCertificateVCRef certVC = certificatePath->certificates[certificatePath->selfIssued];
Boolean isSelfSigned = false;
OSStatus status = SecCertificateIsSelfSigned(certVC->certificate, &isSelfSigned);
if ((status == errSecSuccess) && isSelfSigned) {
certificatePath->isSelfSigned = true;
} else {
certificatePath->selfIssued = -1;
}
}
}
void SecCertificatePathVCSetIsAnchored(
SecCertificatePathVCRef certificatePath) {
secdebug("trust", "%@ is anchored", certificatePath);
certificatePath->isAnchored = true;
if (!certificatePath->isSelfSigned && certificatePath->count > 0) {
SecCertificateVCRef certVC = certificatePath->certificates[certificatePath->count - 1];
Boolean isSelfSigned = false;
OSStatus status = SecCertificateIsSelfSigned(certVC->certificate, &isSelfSigned);
if ((status == errSecSuccess) && isSelfSigned) {
certificatePath->isSelfSigned = true;
if (certificatePath->selfIssued == -1) {
certificatePath->selfIssued = certificatePath->count - 1;
}
}
}
}
CFIndex SecCertificatePathVCSelfSignedIndex(
SecCertificatePathVCRef certificatePath) {
if (certificatePath->isSelfSigned)
return certificatePath->selfIssued;
return -1;
}
Boolean SecCertificatePathVCIsAnchored(
SecCertificatePathVCRef certificatePath) {
return certificatePath->isAnchored;
}
void SecCertificatePathVCSetNextSourceIndex(
SecCertificatePathVCRef certificatePath, CFIndex sourceIndex) {
certificatePath->nextParentSource = sourceIndex;
}
CFIndex SecCertificatePathVCGetNextSourceIndex(
SecCertificatePathVCRef certificatePath) {
return certificatePath->nextParentSource;
}
CFIndex SecCertificatePathVCGetCount(
SecCertificatePathVCRef certificatePath) {
check(certificatePath);
return certificatePath ? certificatePath->count : 0;
}
SecCertificateRef SecCertificatePathVCGetCertificateAtIndex(
SecCertificatePathVCRef certificatePath, CFIndex ix) {
if (!certificatePath || ix < 0 || ix >= certificatePath->count) {
return NULL;
}
SecCertificateVCRef cvc = certificatePath->certificates[ix];
return cvc ? cvc->certificate : NULL;
}
void SecCertificatePathVCForEachCertificate(SecCertificatePathVCRef path, void(^operation)(SecCertificateRef certificate, bool *stop)) {
bool stop = false;
CFIndex ix, count = path->count;
for (ix = 0; ix < count; ++ix) {
SecCertificateVCRef cvc = path->certificates[ix];
operation(cvc->certificate, &stop);
if (stop) { break; }
}
}
CFIndex SecCertificatePathVCGetIndexOfCertificate(SecCertificatePathVCRef path,
SecCertificateRef certificate) {
CFIndex ix, count = path->count;
for (ix = 0; ix < count; ++ix) {
SecCertificateVCRef cvc = path->certificates[ix];
if (CFEqual(cvc->certificate, certificate))
return ix;
}
return kCFNotFound;
}
SecCertificateRef SecCertificatePathVCGetRoot(
SecCertificatePathVCRef certificatePath) {
return SecCertificatePathVCGetCertificateAtIndex(certificatePath,
SecCertificatePathVCGetCount(certificatePath) - 1);
}
SecKeyRef SecCertificatePathVCCopyPublicKeyAtIndex(
SecCertificatePathVCRef certificatePath, CFIndex ix) {
SecCertificateRef certificate =
SecCertificatePathVCGetCertificateAtIndex(certificatePath, ix);
return SecCertificateCopyKey(certificate);
}
CFArrayRef SecCertificatePathVCGetUsageConstraintsAtIndex(
SecCertificatePathVCRef certificatePath, CFIndex ix) {
SecCertificateVCRef cvc = certificatePath->certificates[ix];
return cvc->usageConstraints;
}
void SecCertificatePathVCSetUsageConstraintsAtIndex(SecCertificatePathVCRef certificatePath,
CFArrayRef newConstraints, CFIndex ix) {
CFArrayRef emptyArray = NULL;
if (!newConstraints) {
require_quiet(emptyArray = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks), exit);
newConstraints = emptyArray;
}
SecCertificateVCRef cvc = certificatePath->certificates[ix];
cvc->usageConstraints = CFRetainSafe(newConstraints);
exit:
CFReleaseNull(emptyArray);
return;
}
SecPathVerifyStatus SecCertificatePathVCVerify(SecCertificatePathVCRef certificatePath) {
check(certificatePath);
if (!certificatePath)
return kSecPathVerifyFailed;
for (;
certificatePath->lastVerifiedSigner < certificatePath->count - 1;
++certificatePath->lastVerifiedSigner) {
SecKeyRef issuerKey =
SecCertificatePathVCCopyPublicKeyAtIndex(certificatePath,
certificatePath->lastVerifiedSigner + 1);
if (!issuerKey)
return kSecPathVerifiesUnknown;
SecCertificateVCRef cvc = certificatePath->certificates[certificatePath->lastVerifiedSigner];
OSStatus status = SecCertificateIsSignedBy(cvc->certificate,
issuerKey);
CFRelease(issuerKey);
if (status) {
return kSecPathVerifyFailed;
}
}
return kSecPathVerifySuccess;
}
bool SecCertificatePathVCIsCycleInGraph(SecCertificatePathVCRef path) {
bool isCircle = false;
CFDataRef issuer = SecCertificateGetNormalizedIssuerContent(SecCertificatePathVCGetRoot(path));
if (!issuer) { return isCircle; }
CFIndex ix = path->count - 2;
for (; ix >= 0; ix--) {
SecCertificateVCRef cvc = path->certificates[ix];
CFDataRef subject = SecCertificateGetNormalizedSubjectContent(cvc->certificate);
if (subject && CFEqual(issuer, subject)) {
isCircle = true;
break;
}
}
return isCircle;
}
bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath, CFAbsoluteTime verifyTime) {
__block bool result = true;
SecCertificatePathVCForEachCertificate(certificatePath, ^(SecCertificateRef certificate, bool *stop) {
if (!SecCertificateIsValid(certificate, verifyTime)) {
result = false;
}
});
return result;
}
bool SecCertificatePathVCHasWeakHash(SecCertificatePathVCRef certificatePath) {
CFIndex ix, count = certificatePath->count;
if (certificatePath->hasStrongHashes) {
return false;
}
if (SecCertificatePathVCIsAnchored(certificatePath)) {
count--;
}
for (ix = 0; ix < count; ++ix) {
if (certificatePath->certificates[ix]->isWeakHash) {
return true;
}
}
certificatePath->hasStrongHashes = true;
return false;
}
bool SecCertificatePathVCHasWeakKeySize(SecCertificatePathVCRef certificatePath) {
__block CFDictionaryRef keySizes = NULL;
CFNumberRef rsaSize = NULL, ecSize = NULL;
__block bool result = false;
require(rsaSize = CFNumberCreateWithCFIndex(NULL, 2048), errOut);
require(ecSize = CFNumberCreateWithCFIndex(NULL, 224), errOut);
const void *keys[] = { kSecAttrKeyTypeRSA, kSecAttrKeyTypeEC };
const void *values[] = { rsaSize, ecSize };
require(keySizes = CFDictionaryCreate(NULL, keys, values, 2,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), errOut);
SecCertificatePathVCForEachCertificate(certificatePath, ^(SecCertificateRef certificate, bool *stop) {
if (!SecCertificateIsAtLeastMinKeySize(certificate, keySizes)) {
result = true;
*stop = true;
}
});
errOut:
CFReleaseSafe(keySizes);
CFReleaseSafe(rsaSize);
CFReleaseSafe(ecSize);
return result;
}
CFIndex SecCertificatePathVCScore(SecCertificatePathVCRef certificatePath, CFAbsoluteTime verifyTime) {
CFIndex score = 0;
if (certificatePath->lastVerifiedSigner != certificatePath->count - 1) {
secdebug("trust", "lvs: %" PRIdCFIndex " count: %" PRIdCFIndex,
certificatePath->lastVerifiedSigner, certificatePath->count);
score -= 100000;
}
if (certificatePath->isAnchored) {
score += 10000;
}
if (certificatePath->isSelfSigned && (certificatePath->selfIssued == certificatePath->count - 1)) {
score += 1000;
score -= 1 * certificatePath->count;
} else {
score += 1 * certificatePath->count;
}
if (SecCertificatePathVCIsValid(certificatePath, verifyTime)) {
score += 100;
}
if (!SecCertificatePathVCHasWeakHash(certificatePath)) {
score += 10;
}
if (!SecCertificatePathVCHasWeakKeySize(certificatePath)) {
score += 10;
}
return score;
}
CFIndex SecCertificatePathVCGetScore(SecCertificatePathVCRef certificatePath) {
if (!certificatePath) { return 0; }
return certificatePath->score;
}
void SecCertificatePathVCSetScore(SecCertificatePathVCRef certificatePath, CFIndex score) {
if (score > certificatePath->score) {
certificatePath->score = score;
}
}
void SecCertificatePathVCResetScore(SecCertificatePathVCRef certificatePath) {
certificatePath->score = 0;
}
void *SecCertificatePathVCGetRVCAtIndex(SecCertificatePathVCRef certificatePath, CFIndex ix) {
if (ix >= certificatePath->rvcCount) {
return NULL;
}
return &((SecRVCRef)certificatePath->rvcs)[ix];
}
bool SecCertificatePathVCIsRevocationDone(SecCertificatePathVCRef certificatePath) {
return (bool)certificatePath->rvcs;
}
void SecCertificatePathVCAllocateRVCs(SecCertificatePathVCRef certificatePath, CFIndex certCount) {
certificatePath->rvcs = calloc(sizeof(struct OpaqueSecRVC), certCount);
certificatePath->rvcCount = certCount;
}
CFAbsoluteTime SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef path) {
CFIndex certIX, certCount = path->count;
CFAbsoluteTime enu = NULL_TIME;
if (certCount <= 1 || !path->rvcs) {
return enu;
}
for (certIX = 0; certIX < path->rvcCount; ++certIX) {
SecRVCRef rvc = &((SecRVCRef)path->rvcs)[certIX];
CFAbsoluteTime thisCertNextUpdate = SecRVCGetEarliestNextUpdate(rvc);
if (thisCertNextUpdate == 0) {
if (certIX > 0) {
SecCertificateRef cert = SecCertificatePathVCGetCertificateAtIndex(path, rvc->certIX);
CFArrayRef ocspResponders = NULL;
ocspResponders = SecCertificateGetOCSPResponders(cert);
#if ENABLE_CRLS
CFArrayRef crlDPs = NULL;
crlDPs = SecCertificateGetCRLDistributionPoints(cert);
#endif
if ((!ocspResponders || CFArrayGetCount(ocspResponders) == 0)
#if ENABLE_CRLS
&& (!crlDPs || CFArrayGetCount(crlDPs) == 0)
#endif
) {
continue;
}
}
if (certIX == certCount - 1) {
continue;
}
secdebug("rvc", "revocation checking soft failure for cert: %ld",
certIX);
enu = thisCertNextUpdate;
break;
}
if (enu == 0 || thisCertNextUpdate < enu) {
enu = thisCertNextUpdate;
}
}
secdebug("rvc", "revocation valid until: %lg", enu);
return enu;
}
bool SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
CFIndex ix) {
if (ix > certificatePath->count - 1) { return false; }
SecCertificateVCRef cvc = certificatePath->certificates[ix];
return cvc->require_revocation_response;
}
void SecCertificatePathVCSetRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
CFIndex ix) {
if (ix > certificatePath->count - 1) { return; }
SecCertificateVCRef cvc = certificatePath->certificates[ix];
cvc->require_revocation_response = true;
}
bool SecCertificatePathVCCheckedIssuers(SecCertificatePathVCRef certificatePath) {
return certificatePath->checkedIssuers;
}
void SecCertificatePathVCSetCheckedIssuers(SecCertificatePathVCRef certificatePath, bool checked) {
certificatePath->checkedIssuers = checked;
}
CFIndex SecCertificatePathVCUnknownCAIndex(SecCertificatePathVCRef certificatePath) {
return certificatePath->unknownCAIndex;
}
void SecCertificatePathVCSetUnknownCAIndex(SecCertificatePathVCRef certificatePath, CFIndex index) {
certificatePath->unknownCAIndex = index;
}
bool SecCertificatePathVCIsPathValidated(SecCertificatePathVCRef certificatePath) {
if (!certificatePath) { return false; }
return certificatePath->pathValidated;
}
void SecCertificatePathVCSetPathValidated(SecCertificatePathVCRef certificatePath) {
certificatePath->pathValidated = true;
}
bool SecCertificatePathVCIsEV(SecCertificatePathVCRef certificatePath) {
if (!certificatePath) { return false; }
return certificatePath->isEV;
}
void SecCertificatePathVCSetIsEV(SecCertificatePathVCRef certificatePath, bool isEV) {
certificatePath->isEV = isEV;
}
bool SecCertificatePathVCIsOptionallyEV(SecCertificatePathVCRef certificatePath) {
if (!certificatePath) { return false; }
return certificatePath->certificates[0]->optionallyEV;
}
bool SecCertificatePathVCIsCT(SecCertificatePathVCRef certificatePath) {
if (!certificatePath) { return false; }
return certificatePath->isCT;
}
void SecCertificatePathVCSetIsCT(SecCertificatePathVCRef certificatePath, bool isCT) {
certificatePath->isCT = isCT;
}
SecPathCTPolicy SecCertificatePathVCRequiresCT(SecCertificatePathVCRef certificatePath) {
if (!certificatePath) { return kSecPathCTNotRequired; }
return certificatePath->requiresCT;
}
void SecCertificatePathVCSetRequiresCT(SecCertificatePathVCRef certificatePath, SecPathCTPolicy requiresCT) {
if (certificatePath->requiresCT > requiresCT) {
return;
}
certificatePath->requiresCT = requiresCT;
}
CFAbsoluteTime SecCertificatePathVCIssuanceTime(SecCertificatePathVCRef certificatePath) {
if (!certificatePath) { return 0; }
return certificatePath->issuanceTime;
}
void SecCertificatePathVCSetIssuanceTime(SecCertificatePathVCRef certificatePath, CFAbsoluteTime issuanceTime) {
certificatePath->issuanceTime = issuanceTime;
}
bool SecCertificatePathVCIsAllowlisted(SecCertificatePathVCRef certificatePath) {
if (!certificatePath) { return false; }
return certificatePath->is_allowlisted;
}
void SecCertificatePathVCSetIsAllowlisted(SecCertificatePathVCRef certificatePath, bool isAllowlisted) {
certificatePath->is_allowlisted = isAllowlisted;
}
struct policy_tree_add_ctx {
oid_t p_oid;
policy_qualifier_t p_q;
};
static bool policy_tree_add_if_match(policy_tree_t node, void *ctx) {
struct policy_tree_add_ctx *info = (struct policy_tree_add_ctx *)ctx;
policy_set_t policy_set;
for (policy_set = node->expected_policy_set;
policy_set;
policy_set = policy_set->oid_next) {
if (oid_equal(policy_set->oid, info->p_oid)) {
policy_tree_add_child(node, &info->p_oid, info->p_q);
return true;
}
}
return false;
}
static bool policy_tree_add_if_any(policy_tree_t node, void *ctx) {
struct policy_tree_add_ctx *info = (struct policy_tree_add_ctx *)ctx;
if (oid_equal(node->valid_policy, oidAnyPolicy)) {
policy_tree_add_child(node, &info->p_oid, info->p_q);
return true;
}
return false;
}
static bool policy_tree_has_child_with_oid(policy_tree_t node,
const oid_t *oid) {
policy_tree_t child;
for (child = node->children; child; child = child->siblings) {
if (oid_equal(child->valid_policy, (*oid))) {
return true;
}
}
return false;
}
static bool policy_tree_add_expected(policy_tree_t node, void *ctx) {
policy_qualifier_t p_q = (policy_qualifier_t)ctx;
policy_set_t policy_set;
bool added_node = false;
for (policy_set = node->expected_policy_set;
policy_set;
policy_set = policy_set->oid_next) {
if (!policy_tree_has_child_with_oid(node, &policy_set->oid)) {
policy_tree_add_child(node, &policy_set->oid, p_q);
added_node = true;
}
}
return added_node;
}
static bool policy_tree_map_if_match(policy_tree_t node, void *ctx) {
if (oid_equal(node->valid_policy, oidAnyPolicy))
return false;
const SecCEPolicyMappings *pm = (const SecCEPolicyMappings *)ctx;
size_t mapping_ix, mapping_count = pm->numMappings;
policy_set_t policy_set = NULL;
for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) {
const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix];
if (oid_equal(node->valid_policy, mapping->issuerDomainPolicy)) {
policy_set_t p_node = (policy_set_t)malloc(sizeof(*policy_set));
p_node->oid = mapping->subjectDomainPolicy;
p_node->oid_next = policy_set ? policy_set : NULL;
policy_set = p_node;
}
}
if (policy_set) {
policy_tree_set_expected_policy(node, policy_set);
return true;
}
return false;
}
static bool policy_tree_map_if_any(policy_tree_t node, void *ctx) {
if (!oid_equal(node->valid_policy, oidAnyPolicy)) {
return false;
}
const SecCEPolicyMappings *pm = (const SecCEPolicyMappings *)ctx;
size_t mapping_ix, mapping_count = pm->numMappings;
CFMutableDictionaryRef mappings = NULL;
CFDataRef idp = NULL;
CFDataRef sdp = NULL;
require_quiet(mappings = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks),
errOut);
for (mapping_ix = 0; mapping_ix < mapping_count; mapping_ix++) {
oid_t issuerDomainPolicy = pm->mappings[mapping_ix].issuerDomainPolicy;
oid_t subjectDomainPolicy = pm->mappings[mapping_ix].subjectDomainPolicy;
idp = CFDataCreateWithBytesNoCopy(NULL, issuerDomainPolicy.data, issuerDomainPolicy.length, kCFAllocatorNull);
sdp = CFDataCreateWithBytesNoCopy(NULL, subjectDomainPolicy.data, subjectDomainPolicy.length, kCFAllocatorNull);
CFMutableArrayRef sdps = (CFMutableArrayRef)CFDictionaryGetValue(mappings, idp);
if (sdps) {
CFArrayAppendValue(sdps, sdp);
} else {
require_quiet(sdps = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks), errOut);
CFArrayAppendValue(sdps, sdp);
CFDictionarySetValue(mappings, idp, sdps);
CFRelease(sdps);
}
CFReleaseNull(idp);
CFReleaseNull(sdp);
}
CFDictionaryForEach(mappings, ^(const void *key, const void *value) {
CFDataRef idp = key;
CFArrayRef sdps = value;
oid_t p_oid;
p_oid.data = (uint8_t *)CFDataGetBytePtr(idp);
p_oid.length = CFDataGetLength(idp);
policy_qualifier_t p_q = node->qualifier_set;
__block policy_set_t p_expected = NULL;
CFArrayForEach(sdps, ^(const void *value) {
policy_set_t p_node = (policy_set_t)malloc(sizeof(*p_expected));
p_node->oid.data = (void *)CFDataGetBytePtr(value);
p_node->oid.length = CFDataGetLength(value);
p_node->oid_next = p_expected ? p_expected : NULL;
p_expected = p_node;
});
policy_tree_add_sibling(node, &p_oid, p_q, p_expected);
});
CFReleaseNull(mappings);
return true;
errOut:
CFReleaseNull(mappings);
CFReleaseNull(idp);
CFReleaseNull(sdp);
return false;
}
static bool policy_tree_map_delete_if_match(policy_tree_t node, void *ctx) {
if (oid_equal(node->valid_policy, oidAnyPolicy))
return false;
const SecCEPolicyMappings *pm = (const SecCEPolicyMappings *)ctx;
size_t mapping_ix, mapping_count = pm->numMappings;
for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) {
const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix];
if (oid_equal(node->valid_policy, mapping->issuerDomainPolicy)) {
policy_tree_remove_node(&node);
break;
}
}
return true;
}
bool SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecCertificatePathVCRef path, CFIndex ix) {
bool result = false;
SecCertificateRef cert = SecCertificatePathVCGetCertificateAtIndex(path, ix);
CFDataRef issuer = SecCertificateCopyNormalizedIssuerSequence(cert);
CFDataRef subject = SecCertificateCopyNormalizedSubjectSequence(cert);
if (issuer && subject && CFEqual(issuer, subject)) {
result = true;
}
CFReleaseNull(issuer);
CFReleaseNull(subject);
return result;
}
enum {
kSecPolicyTreeVerificationUnknown = 0,
kSecPolicyTreeVerificationFalse,
kSecPolicyTreeVerificationTrue,
};
bool SecCertificatePathVCVerifyPolicyTree(SecCertificatePathVCRef path, bool anchor_trusted) {
if (!path) { return false; }
if (path->policy_tree_verification_result != kSecPolicyTreeVerificationUnknown) {
return (path->policy_tree_verification_result == kSecPolicyTreeVerificationTrue);
}
bool result = false;
path->policy_tree_verification_result = kSecPolicyTreeVerificationFalse;
bool initial_policy_mapping_inhibit = false;
bool initial_explicit_policy = false;
bool initial_any_policy_inhibit = false;
SecCertificatePathVCPrunePolicyTree(path);
path->policy_tree = policy_tree_create(&oidAnyPolicy, NULL);
assert((unsigned long)path->count<=UINT32_MAX);
uint32_t n = (uint32_t)path->count;
if (anchor_trusted) {
n--;
}
uint32_t explicit_policy = initial_explicit_policy ? 0 : n + 1;
uint32_t inhibit_any_policy = initial_any_policy_inhibit ? 0 : n + 1;
uint32_t policy_mapping = initial_policy_mapping_inhibit ? 0 : n + 1;
SecCertificateRef cert = NULL;
uint32_t i;
for (i = 1; i <= n; ++i) {
cert = SecCertificatePathVCGetCertificateAtIndex(path, n - i);
bool is_self_issued = SecCertificatePathVCIsCertificateAtIndexSelfIssued(path, n - i);
if (path->policy_tree) {
const SecCECertificatePolicies *cp =
SecCertificateGetCertificatePolicies(cert);
size_t policy_ix, policy_count = cp ? cp->numPolicies : 0;
for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
const SecCEPolicyInformation *policy = &cp->policies[policy_ix];
oid_t p_oid = policy->policyIdentifier;
policy_qualifier_t p_q = &policy->policyQualifiers;
struct policy_tree_add_ctx ctx = { p_oid, p_q };
if (!oid_equal(p_oid, oidAnyPolicy)) {
if (!policy_tree_walk_depth(path->policy_tree, i - 1,
policy_tree_add_if_match, &ctx)) {
policy_tree_walk_depth(path->policy_tree, i - 1,
policy_tree_add_if_any, &ctx);
}
}
}
if (inhibit_any_policy > 0 || (i < n && is_self_issued)) {
for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
const SecCEPolicyInformation *policy = &cp->policies[policy_ix];
oid_t p_oid = policy->policyIdentifier;
policy_qualifier_t p_q = &policy->policyQualifiers;
if (oid_equal(p_oid, oidAnyPolicy)) {
policy_tree_walk_depth(path->policy_tree, i - 1,
policy_tree_add_expected, (void *)p_q);
}
}
}
policy_tree_prune_childless(&path->policy_tree, i - 1);
if (!cp) {
SecCertificatePathVCPrunePolicyTree(path);
}
}
if (!path->policy_tree && explicit_policy == 0) {
secnotice("policy", "policy tree failure on cert %u", n - i);
goto errOut;
}
if (i == n)
break;
const SecCEPolicyMappings *pm = SecCertificateGetPolicyMappings(cert);
if (pm && pm->present) {
size_t mapping_ix, mapping_count = pm->numMappings;
for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) {
const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix];
if (oid_equal(mapping->issuerDomainPolicy, oidAnyPolicy)
|| oid_equal(mapping->subjectDomainPolicy, oidAnyPolicy)) {
secnotice("policy", "policy mapping anyPolicy failure %u", n - i);
goto errOut;
}
}
if (policy_mapping > 0 && path->policy_tree) {
if (!policy_tree_walk_depth(path->policy_tree, i,
policy_tree_map_if_match, (void *)pm)) {
policy_tree_walk_depth(path->policy_tree, i, policy_tree_map_if_any, (void *)pm);
}
} else if (path->policy_tree) {
policy_tree_walk_depth(path->policy_tree, i,
policy_tree_map_delete_if_match, (void *)pm);
policy_tree_prune_childless(&path->policy_tree, i - 1);
}
}
if (!is_self_issued) {
if (explicit_policy)
explicit_policy--;
if (policy_mapping)
policy_mapping--;
if (inhibit_any_policy)
inhibit_any_policy--;
}
const SecCEPolicyConstraints *pc =
SecCertificateGetPolicyConstraints(cert);
if (pc) {
if (pc->requireExplicitPolicyPresent
&& pc->requireExplicitPolicy < explicit_policy) {
explicit_policy = pc->requireExplicitPolicy;
}
if (pc->inhibitPolicyMappingPresent
&& pc->inhibitPolicyMapping < policy_mapping) {
policy_mapping = pc->inhibitPolicyMapping;
}
}
const SecCEInhibitAnyPolicy *iap = SecCertificateGetInhibitAnyPolicySkipCerts(cert);
if (iap && iap->skipCerts < inhibit_any_policy) {
inhibit_any_policy = iap->skipCerts;
}
}
cert = SecCertificatePathVCGetCertificateAtIndex(path, 0);
if (explicit_policy)
explicit_policy--;
const SecCEPolicyConstraints *pc = SecCertificateGetPolicyConstraints(cert);
if (pc) {
if (pc->requireExplicitPolicyPresent
&& pc->requireExplicitPolicy == 0) {
explicit_policy = 0;
}
}
if (path->policy_tree) {
#if !defined(NDEBUG)
policy_tree_dump(path->policy_tree);
#endif
}
if (!path->policy_tree && explicit_policy == 0) {
secnotice("policy", "policy tree failure on leaf");
goto errOut;
}
path->policy_tree_verification_result = kSecPolicyTreeVerificationTrue;
result = true;
errOut:
return result;
}