SecCertificatePath.c [plain text]
#include "SecCertificatePath.h"
#include <Security/SecTrust.h>
#include <Security/SecTrustStore.h>
#include <Security/SecItem.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecFramework.h>
#include <utilities/SecIOFormat.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFSet.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFPropertyList.h>
#include <AssertMacros.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <Security/SecBase.h>
#include "SecRSAKey.h"
#include <libDER/oidsPriv.h>
#include <utilities/debugging.h>
#include <Security/SecInternal.h>
#include <AssertMacros.h>
#include <utilities/SecCFError.h>
#include <utilities/SecCFWrappers.h>
struct SecCertificatePath {
CFRuntimeBase _base;
CFIndex count;
CFIndex nextParentSource;
CFIndex lastVerifiedSigner;
CFIndex selfIssued;
bool isSelfSigned;
bool isAnchored;
SecCertificateRef certificates[];
};
CFGiblisWithHashFor(SecCertificatePath)
static void SecCertificatePathDestroy(CFTypeRef cf) {
SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf;
CFIndex ix;
for (ix = 0; ix < certificatePath->count; ++ix) {
CFRelease(certificatePath->certificates[ix]);
}
}
static Boolean SecCertificatePathCompare(CFTypeRef cf1, CFTypeRef cf2) {
SecCertificatePathRef cp1 = (SecCertificatePathRef) cf1;
SecCertificatePathRef cp2 = (SecCertificatePathRef) 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 SecCertificatePathHash(CFTypeRef cf) {
SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf;
CFHashCode hashCode = 0;
CFIndex ix;
for (ix = 0; ix < certificatePath->count; ++ix) {
hashCode += CFHash(certificatePath->certificates[ix]);
}
return hashCode;
}
static CFStringRef SecCertificatePathCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf;
CFMutableStringRef desc = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringRef typeStr = CFCopyTypeIDDescription(CFGetTypeID(cf));
CFStringAppendFormat(desc, NULL,
CFSTR("<%@ lvs: %" PRIdCFIndex " certs: "), typeStr,
certificatePath->lastVerifiedSigner);
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;
}
SecCertificatePathRef SecCertificatePathCreate(SecCertificatePathRef path,
SecCertificateRef certificate) {
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 SecCertificatePath) +
count * sizeof(SecCertificateRef);
SecCertificatePathRef result =
(SecCertificatePathRef)_CFRuntimeCreateInstance(allocator,
SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
result->count = count;
result->nextParentSource = 0;
result->lastVerifiedSigner = lastVerifiedSigner;
result->selfIssued = selfIssued;
result->isSelfSigned = isSelfSigned;
result->isAnchored = false;
CFIndex ix;
for (ix = 0; ix < count - 1; ++ix) {
result->certificates[ix] = path->certificates[ix];
CFRetain(result->certificates[ix]);
}
result->certificates[count - 1] = certificate;
CFRetainSafe(certificate);
return result;
}
SecCertificatePathRef SecCertificatePathCreateWithXPCArray(xpc_object_t xpc_path, CFErrorRef *error) {
SecCertificatePathRef result = NULL;
require_action_quiet(xpc_path, exit, SecError(errSecParam, error, CFSTR("xpc_path is NULL")));
require_action_quiet(xpc_get_type(xpc_path) == XPC_TYPE_ARRAY, exit, SecError(errSecDecode, error, CFSTR("xpc_path value is not an array")));
size_t count;
require_action_quiet(count = xpc_array_get_count(xpc_path), exit, SecError(errSecDecode, error, CFSTR("xpc_path array count == 0")));
size_t size = sizeof(struct SecCertificatePath) + count * sizeof(SecCertificateRef);
require_action_quiet(result = (SecCertificatePathRef)_CFRuntimeCreateInstance(kCFAllocatorDefault, SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0), exit, SecError(errSecDecode, error, CFSTR("_CFRuntimeCreateInstance returned NULL")));
result->count = count;
result->nextParentSource = 0;
result->lastVerifiedSigner = count;
result->selfIssued = -1;
result->isSelfSigned = false;
result->isAnchored = false;
size_t ix;
for (ix = 0; ix < count; ++ix) {
SecCertificateRef certificate = SecCertificateCreateWithXPCArrayAtIndex(xpc_path, ix, error);
if (certificate) {
result->certificates[ix] = certificate;
} else {
result->count = ix; CFReleaseNull(result);
break;
}
}
exit:
return result;
}
SecCertificatePathRef SecCertificatePathCopyFromParent(
SecCertificatePathRef 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 SecCertificatePath) +
count * sizeof(SecCertificateRef);
SecCertificatePathRef result =
(SecCertificatePathRef)_CFRuntimeCreateInstance(allocator,
SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
result->count = count;
result->nextParentSource = 0;
result->lastVerifiedSigner = lastVerifiedSigner;
result->selfIssued = selfIssued;
result->isSelfSigned = isSelfSigned;
result->isAnchored = path->isAnchored;
CFIndex ix;
for (ix = 0; ix < count; ++ix) {
result->certificates[ix] = path->certificates[ix + skipCount];
CFRetain(result->certificates[ix]);
}
return result;
}
SecCertificatePathRef SecCertificatePathCopyAddingLeaf(SecCertificatePathRef path,
SecCertificateRef leaf) {
CFAllocatorRef allocator = kCFAllocatorDefault;
CFIndex count;
CFIndex selfIssued, lastVerifiedSigner;
bool isSelfSigned;
SecKeyRef issuerKey = SecCertificatePathCopyPublicKeyAtIndex(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 SecCertificatePath) +
count * sizeof(SecCertificateRef);
SecCertificatePathRef result =
(SecCertificatePathRef)_CFRuntimeCreateInstance(allocator,
SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
result->count = count;
result->nextParentSource = 0;
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]);
}
result->certificates[0] = leaf;
CFRetain(leaf);
return result;
}
xpc_object_t SecCertificatePathCopyXPCArray(SecCertificatePathRef path, CFErrorRef *error) {
xpc_object_t xpc_chain = NULL;
size_t ix, count = path->count;
require_action_quiet(xpc_chain = xpc_array_create(NULL, 0), exit, SecError(errSecParam, error, CFSTR("xpc_array_create failed")));
for (ix = 0; ix < count; ++ix) {
SecCertificateRef cert = SecCertificatePathGetCertificateAtIndex(path, ix);
if (!SecCertificateAppendToXPCArray(cert, xpc_chain, error)) {
xpc_release(xpc_chain);
return NULL;
}
}
exit:
return xpc_chain;
}
void SecCertificatePathSetSelfIssued(
SecCertificatePathRef 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;
}
void SecCertificatePathSetIsAnchored(
SecCertificatePathRef certificatePath) {
secdebug("trust", "%@ is anchored", certificatePath);
certificatePath->isAnchored = true;
}
CFIndex SecCertificatePathSelfSignedIndex(
SecCertificatePathRef certificatePath) {
if (certificatePath->isSelfSigned)
return certificatePath->selfIssued;
return -1;
}
Boolean SecCertificatePathIsAnchored(
SecCertificatePathRef certificatePath) {
return certificatePath->isAnchored;
}
void SecCertificatePathSetNextSourceIndex(
SecCertificatePathRef certificatePath, CFIndex sourceIndex) {
certificatePath->nextParentSource = sourceIndex;
}
CFIndex SecCertificatePathGetNextSourceIndex(
SecCertificatePathRef certificatePath) {
return certificatePath->nextParentSource;
}
CFIndex SecCertificatePathGetCount(
SecCertificatePathRef certificatePath) {
check(certificatePath);
return certificatePath ? certificatePath->count : 0;
}
SecCertificateRef SecCertificatePathGetCertificateAtIndex(
SecCertificatePathRef certificatePath, CFIndex ix) {
check(certificatePath && ix >= 0 && ix < certificatePath->count);
return certificatePath->certificates[ix];
}
CFIndex SecCertificatePathGetIndexOfCertificate(SecCertificatePathRef path,
SecCertificateRef certificate) {
CFIndex ix, count = path->count;
for (ix = 0; ix < count; ++ix) {
if (CFEqual(path->certificates[ix], certificate))
return ix;
}
return kCFNotFound;
}
#if 0
SecCertificateRef SecCertificatePathGetLeaf(
SecCertificatePathRef certificatePath) {
return SecCertificatePathGetCertificateAtIndex(certificatePath, 0);
}
#endif
SecCertificateRef SecCertificatePathGetRoot(
SecCertificatePathRef certificatePath) {
return SecCertificatePathGetCertificateAtIndex(certificatePath,
SecCertificatePathGetCount(certificatePath) - 1);
}
SecKeyRef SecCertificatePathCopyPublicKeyAtIndex(
SecCertificatePathRef certificatePath, CFIndex ix) {
SecCertificateRef certificate =
SecCertificatePathGetCertificateAtIndex(certificatePath, ix);
const DERAlgorithmId *algId =
SecCertificateGetPublicKeyAlgorithm(certificate);
const DERItem *params = NULL;
if (algId->params.length != 0) {
params = &algId->params;
} else {
CFIndex count = certificatePath->count;
for (++ix; ix < count; ++ix) {
certificate = certificatePath->certificates[ix];
const DERAlgorithmId *chain_algId =
SecCertificateGetPublicKeyAlgorithm(certificate);
if (!DEROidCompare(&algId->oid, &chain_algId->oid)) {
break;
}
if (chain_algId->params.length != 0) {
params = &chain_algId->params;
break;
}
}
}
const DERItem *keyData = SecCertificateGetPublicKeyData(certificate);
SecAsn1Oid oid1 = { .Data = algId->oid.data, .Length = algId->oid.length };
SecAsn1Item params1 = {
.Data = params ? params->data : NULL,
.Length = params ? params->length : 0
};
SecAsn1Item keyData1 = {
.Data = keyData ? keyData->data : NULL,
.Length = keyData ? keyData->length : 0
};
return SecKeyCreatePublicFromDER(kCFAllocatorDefault, &oid1, ¶ms1,
&keyData1);
}
SecPathVerifyStatus SecCertificatePathVerify(
SecCertificatePathRef certificatePath) {
check(certificatePath);
if (!certificatePath)
return kSecPathVerifyFailed;
for (;
certificatePath->lastVerifiedSigner < certificatePath->count - 1;
++certificatePath->lastVerifiedSigner) {
SecKeyRef issuerKey =
SecCertificatePathCopyPublicKeyAtIndex(certificatePath,
certificatePath->lastVerifiedSigner + 1);
if (!issuerKey)
return kSecPathVerifiesUnknown;
OSStatus status = SecCertificateIsSignedBy(
certificatePath->certificates[certificatePath->lastVerifiedSigner],
issuerKey);
CFRelease(issuerKey);
if (status) {
return kSecPathVerifyFailed;
}
}
if (certificatePath->selfIssued >= 0 && !certificatePath->isSelfSigned) {
SecKeyRef issuerKey =
SecCertificatePathCopyPublicKeyAtIndex(certificatePath,
certificatePath->selfIssued);
if (!issuerKey) {
certificatePath->selfIssued = -1;
} else {
OSStatus status = SecCertificateIsSignedBy(
certificatePath->certificates[certificatePath->selfIssued],
issuerKey);
CFRelease(issuerKey);
if (!status) {
certificatePath->isSelfSigned = true;
} else {
certificatePath->selfIssued = -1;
}
}
}
return kSecPathVerifySuccess;
}
CFIndex SecCertificatePathScore(
SecCertificatePathRef certificatePath, CFAbsoluteTime verifyTime) {
CFIndex score = 0;
if (certificatePath->isAnchored) {
score += 10000;
}
score += 10 * certificatePath->count;
if (certificatePath->isSelfSigned) {
if (certificatePath->selfIssued == certificatePath->count - 1)
score += 10;
else
score += 5;
}
if (certificatePath->lastVerifiedSigner != certificatePath->count - 1) {
secdebug("trust", "lvs: %" PRIdCFIndex " count: %" PRIdCFIndex,
certificatePath->lastVerifiedSigner, certificatePath->count);
score -= 100000;
}
CFIndex ix;
for (ix = 0; ix < certificatePath->count - 1; ++ix) {
if (!SecCertificateIsValid(certificatePath->certificates[ix],
verifyTime))
score -= 1;
}
return score;
}