]> git.saurik.com Git - apple/security.git/blobdiff - sec/Security/SecCertificatePath.c
Security-55163.44.tar.gz
[apple/security.git] / sec / Security / SecCertificatePath.c
diff --git a/sec/Security/SecCertificatePath.c b/sec/Security/SecCertificatePath.c
new file mode 100644 (file)
index 0000000..a88d989
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2007-2010 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * SecCertificatePath.c - CoreFoundation based certificate path object
+ */
+
+#include "SecCertificatePath.h"
+
+#include <Security/SecTrust.h>
+#include <Security/SecTrustStore.h>
+#include <Security/SecItem.h>
+#include <Security/SecCertificateInternal.h>
+#include <Security/SecFramework.h>
+#include <CoreFoundation/CFRuntime.h>
+#include <CoreFoundation/CFSet.h>
+#include <CoreFoundation/CFString.h>
+#include <CoreFoundation/CFNumber.h>
+#include <CoreFoundation/CFArray.h>
+#include <CoreFoundation/CFPropertyList.h>
+#include <AssertMacros.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <MacErrors.h>
+#include "SecRSAKey.h"
+#include <libDER/oids.h>
+#include <security_utilities/debugging.h>
+#include <Security/SecInternal.h>
+
+#pragma mark -
+#pragma mark SecCertificatePath
+/********************************************************
+ ************* SecCertificatePath object ****************
+ ********************************************************/
+struct SecCertificatePath {
+    CFRuntimeBase              _base;
+       CFIndex                         count;
+
+       /* Index of next parent source to search for parents. */
+       CFIndex                         nextParentSource;
+
+       /* Index of last certificate in chain who's signature has been verified.
+          0 means nothing has been checked.  1 means the leaf has been verified
+          against it's issuer, etc. */
+       CFIndex                         lastVerifiedSigner;
+
+       /* Index of first self issued certificate in the chain.  -1 mean there is
+          none.  0 means the leaf is self signed.  */
+       CFIndex                         selfIssued;
+
+       /* True iff cert at index selfIssued does in fact self verify. */
+       bool                            isSelfSigned;
+
+       /* True if the root of this path is a trusted anchor.
+          FIXME get rid of this since it's a property of the evaluation, not a
+          static feature of a certificate path? */
+       bool                            isAnchored;
+       SecCertificateRef       certificates[];
+};
+
+/* CFRuntime regsitration data. */
+static pthread_once_t kSecCertificatePathRegisterClass = PTHREAD_ONCE_INIT;
+static CFTypeID kSecCertificatePathTypeID = _kCFRuntimeNotATypeID;
+
+static void SecCertificatePathDestroy(CFTypeRef cf) {
+       SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf;
+       CFIndex ix;
+       for (ix = 0; ix < certificatePath->count; ++ix) {
+               CFRelease(certificatePath->certificates[ix]);
+    }
+}
+
+static Boolean SecCertificatePathEqual(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;
+       // hashCode = 31 * SecCertificatePathGetTypeID();
+       CFIndex ix;
+       for (ix = 0; ix < certificatePath->count; ++ix) {
+               hashCode += CFHash(certificatePath->certificates[ix]);
+       }
+       return hashCode;
+}
+
+static CFStringRef SecCertificatePathDescribe(CFTypeRef cf) {
+       SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf;
+    CFMutableStringRef desc = CFStringCreateMutable(kCFAllocatorDefault, 0);
+    CFStringRef typeStr = CFCopyTypeIDDescription(CFGetTypeID(cf));
+    CFStringAppendFormat(desc, NULL,
+        CFSTR("<%@ lvs: %d 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;
+}
+
+static void SecCertificatePathRegisterClass(void) {
+       static const CFRuntimeClass kSecCertificatePathClass = {
+               0,                                                                                              /* version */
+        "SecCertificatePath",                                                  /* class name */
+               NULL,                                                                                   /* init */
+               NULL,                                                                                   /* copy */
+               SecCertificatePathDestroy,                                              /* dealloc */
+               SecCertificatePathEqual,                                                /* equal */
+               SecCertificatePathHash,                                                 /* hash */
+               NULL,                                                                                   /* copyFormattingDesc */
+               SecCertificatePathDescribe                                              /* copyDebugDesc */
+       };
+
+    kSecCertificatePathTypeID =
+               _CFRuntimeRegisterClass(&kSecCertificatePathClass);
+}
+
+/* SecCertificatePath API functions. */
+CFTypeID SecCertificatePathGetTypeID(void) {
+    pthread_once(&kSecCertificatePathRegisterClass,
+               SecCertificatePathRegisterClass);
+    return kSecCertificatePathTypeID;
+}
+
+/* Create a new certificate path from an old one. */
+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;
+       CFRetain(certificate);
+
+    return result;
+}
+
+/* Create a new certificate path from an array of CFDataRefs. */
+SecCertificatePathRef SecCertificatePathCreateWithArray(CFArrayRef certificates) {
+       CFIndex count = CFArrayGetCount(certificates);
+    CFIndex size = sizeof(struct SecCertificatePath) +
+               count * sizeof(SecCertificateRef);
+    SecCertificatePathRef result =
+               (SecCertificatePathRef)_CFRuntimeCreateInstance(kCFAllocatorDefault,
+               SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0);
+       if (!result)
+        return NULL;
+
+       result->count = count;
+       result->nextParentSource = 0;
+       result->lastVerifiedSigner = count;
+       result->selfIssued = -1;
+       result->isSelfSigned = false;
+       result->isAnchored = false;
+       CFIndex ix;
+       for (ix = 0; ix < count; ++ix) {
+        CFDataRef data = CFArrayGetValueAtIndex(certificates, ix);
+        SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, data);
+               result->certificates[ix] = certificate;
+       }
+
+    return result;
+}
+
+SecCertificatePathRef SecCertificatePathCopyFromParent(
+    SecCertificatePathRef path, CFIndex skipCount) {
+    CFAllocatorRef allocator = kCFAllocatorDefault;
+       CFIndex count;
+       CFIndex selfIssued, lastVerifiedSigner;
+       bool isSelfSigned;
+
+    /* Ensure we are at least returning a path of length 1. */
+    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;
+
+    /* First make sure the new leaf is signed by path's current leaf. */
+    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;
+}
+
+/* Create an array of CFDataRefs from a certificate path. */
+CFArrayRef SecCertificatePathCopyArray(SecCertificatePathRef path) {
+    CFIndex ix, count = path->count;
+    CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, count,
+        &kCFTypeArrayCallBacks);
+       for (ix = 0; ix < count; ++ix) {
+        CFDataRef data = SecCertificateCopyData(path->certificates[ix]);
+        CFArrayAppendValue(result, data);
+        CFRelease(data);
+       }
+    return result;
+}
+
+/* Record the fact that we found our own root cert as our parent
+   certificate. */
+void SecCertificatePathSetSelfIssued(
+       SecCertificatePathRef certificatePath) {
+       if (certificatePath->selfIssued >= 0) {
+               secdebug("trust", "%@ is already issued at %d", 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;
+}
+
+/* Return the index of the first non anchor certificate in the chain that is
+   self signed counting from the leaf up.  Return -1 if there is none. */
+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->count;
+}
+
+SecCertificateRef SecCertificatePathGetCertificateAtIndex(
+       SecCertificatePathRef certificatePath, CFIndex ix) {
+       check(certificatePath);
+       check(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
+/* Return the leaf certificate for certificatePath. */
+SecCertificateRef SecCertificatePathGetLeaf(
+       SecCertificatePathRef certificatePath) {
+       return SecCertificatePathGetCertificateAtIndex(certificatePath, 0);
+}
+#endif
+
+/* Return the root certificate for certificatePath.  Note that root is just
+   the top of the path as far as it is constructed.  It may or may not be
+   trusted or self signed.  */
+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)) {
+                /* Algorithm oids differ, params stay NULL. */
+                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, &params1,
+        &keyData1);
+}
+
+SecPathVerifyStatus SecCertificatePathVerify(
+       SecCertificatePathRef certificatePath) {
+       check(certificatePath);
+       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;
+}
+
+/* Return a score for this certificate chain. */
+CFIndex SecCertificatePathScore(
+       SecCertificatePathRef certificatePath, CFAbsoluteTime verifyTime) {
+       CFIndex score = 0;
+       if (certificatePath->isAnchored) {
+               /* Anchored paths for the win! */
+               score += 10000;
+       }
+
+       /* Score points for each certificate in the chain. */
+       score += 10 * certificatePath->count;
+
+       if (certificatePath->isSelfSigned) {
+               /* If there is a self signed certificate at the end ofthe chain we
+                  count it as an extra certificate.  If there is one in the middle
+                  of the chain we count it for half. */
+               if (certificatePath->selfIssued == certificatePath->count - 1)
+                       score += 10;
+               else
+                       score += 5;
+       }
+
+       /* Paths that don't verify score terribly. */
+       if (certificatePath->lastVerifiedSigner != certificatePath->count - 1) {
+               secdebug("trust", "lvs: %d count: %d",
+                       certificatePath->lastVerifiedSigner, certificatePath->count);
+               score -= 100000;
+       }
+
+       /* Subtract 1 point for each not valid certificate, make sure we
+       subtract less than the amount we add per certificate, since
+       regardless of temporal validity we still prefer longer chains
+       to shorter ones.  This distinction is just to ensure that when
+       everything else is equal we prefer the chain with the most
+       certificates that are valid at the given verifyTime. */
+       CFIndex ix;
+       for (ix = 0; ix < certificatePath->count - 1; ++ix) {
+               if (!SecCertificateIsValid(certificatePath->certificates[ix],
+                       verifyTime))
+                       score -= 1;
+       }
+
+       return score;
+}