]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/legacydevid.cpp
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / legacydevid.cpp
diff --git a/OSX/libsecurity_codesigning/lib/legacydevid.cpp b/OSX/libsecurity_codesigning/lib/legacydevid.cpp
new file mode 100644 (file)
index 0000000..239d73e
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2019 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@
+ */
+
+#include "legacydevid.h"
+#include "SecAssessment.h"
+#include "requirement.h"
+
+#include <Security/SecCertificatePriv.h>
+
+namespace Security {
+namespace CodeSigning {
+
+static const CFStringRef kLegacyPolicyPreferenceDomain = CFSTR("com.apple.security.syspolicy");
+static const CFStringRef kLegacyPolicyAccountCreationCutOff = CFSTR("AccountCreationCutOffDate");
+static const CFStringRef kLegacyPolicySecureTimestampCutOff = CFSTR("SecureTimestampCutOffDate");
+static const CFAbsoluteTime kLegacyPolicyAccountCreationDefaultCutOff = 576374400.0; // seconds from January 1, 2001 to April 7, 2019 GMT
+static const CFAbsoluteTime kLegacyPolicySecureTimestampDefaultCutOff = 581040000.0; // seconds from January 1, 2001 to June 1, 2019 GMT
+
+static CFDateRef
+copyCutOffDate(const CFStringRef key, CFAbsoluteTime defaultCutoff)
+{
+    CFDateRef defaultDate = CFDateCreate(NULL, defaultCutoff);
+    CFDateRef outputDate = defaultDate;
+    CFDateRef prefDate = NULL;
+
+    CFTypeRef prefVal = (CFDateRef)CFPreferencesCopyValue(key,
+                                                          kLegacyPolicyPreferenceDomain,
+                                                          kCFPreferencesCurrentUser,
+                                                          kCFPreferencesAnyHost);
+    if (prefVal && CFGetTypeID(prefVal) == CFDateGetTypeID()) {
+        prefDate = (CFDateRef)prefVal;
+    }
+
+    if (prefDate) {
+        CFComparisonResult res = CFDateCompare(defaultDate, prefDate, NULL);
+        if (res > 0) {
+            outputDate = prefDate;
+        }
+    }
+
+    CFRetain(outputDate);
+
+    if (prefVal) {
+        CFRelease(prefVal);
+    }
+    if (defaultDate) {
+        CFRelease(defaultDate);
+    }
+    return outputDate;
+}
+
+bool
+meetsDeveloperIDLegacyAllowedPolicy(const Requirement::Context *context)
+{
+    CFRef<CFDataRef> cd;
+    CFRef<CFErrorRef> error;
+    CFRef<CFStringRef> teamID;
+    bool meets_legacy_policy = false;
+    SecCSDigestAlgorithm hashType = kSecCodeSignatureNoHash;
+    SecCertificateRef cert = NULL;
+    CFAbsoluteTime accountCreationTime = 0.0;
+
+    if (context == NULL) {
+        meets_legacy_policy = false;
+        goto lb_exit;
+    }
+
+    // First check account creation date in certs
+    // An account creation date after the cut off must be notarized so it fails the legacy policy.
+    // No account creation date or an account creation date before the cut off requires additional checking
+    cert = context->cert(Requirement::leafCert);
+    if (SecCertificateGetDeveloperIDDate(cert, &accountCreationTime, &error.aref())) {
+        //There is an account creation date
+        CFRef<CFDateRef> accountCreationDate = CFDateCreate(NULL, accountCreationTime);
+        CFRef<CFDateRef> accountCreationCutoffDate = copyCutOffDate(kLegacyPolicyAccountCreationCutOff,
+                                                                    kLegacyPolicyAccountCreationDefaultCutOff);
+        secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Account Creation Date Cutoff: %@", accountCreationCutoffDate.get());
+        secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Account Creation date: %@", accountCreationDate.get());
+
+        CFComparisonResult res = CFDateCompare(accountCreationDate, accountCreationCutoffDate, NULL);
+        if (res >= 0) {
+            // The account was created on or after our cut off so it doesn't meet legacy policy
+            meets_legacy_policy = false;
+            secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Account creation date %@ is after cut-off %@", accountCreationDate.get(), accountCreationCutoffDate.get());
+            goto lb_exit;
+        }
+        // Account creation date before the cut off means we fall through
+    } else {
+        CFIndex errorCode = CFErrorGetCode(error);
+        if (errorCode != errSecMissingRequiredExtension) {
+            secerror("Unexpected error checking account creation date: %ld", errorCode);
+            meets_legacy_policy = false;
+            goto lb_exit;
+        }
+        // there was no account creation date so fall through
+    }
+
+    // Next check secure time stamp
+    if (context->secureTimestamp) {
+        CFRef<CFDateRef> secureTimestampCutoffDate = copyCutOffDate(kLegacyPolicySecureTimestampCutOff,
+                                                                    kLegacyPolicySecureTimestampDefaultCutOff);
+        secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Secure Timestamp Cutoff Date cutoff: %@", secureTimestampCutoffDate.get());
+        secinfo("meetsDevleoperIDLegacyAllowedPolicy", "Secure Timestamp: %@", context->secureTimestamp);
+        CFComparisonResult res = CFDateCompare(context->secureTimestamp, secureTimestampCutoffDate, NULL);
+        if (res >= 0) {
+            // Secure timestamp is on or after the cut of so it doesn't meet legacy policy
+            meets_legacy_policy = false;
+            secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Secure timestamp %@ is after cut-off %@", context->secureTimestamp, secureTimestampCutoffDate.get());
+        } else {
+            // Secure timestamp is before the cut off so we meet the legacy policy
+            meets_legacy_policy = true;
+        }
+    }
+
+    if (!meets_legacy_policy) {
+        // Just check against the legacy lists, both by hash and team ID.
+        if (context->directory) {
+            cd.take(context->directory->cdhash());
+            hashType = (SecCSDigestAlgorithm)context->directory->hashType;
+        } else if (context->packageChecksum) {
+            cd = context->packageChecksum;
+            hashType = context->packageAlgorithm;
+        }
+
+        if (cd.get() == NULL) {
+            // No cdhash means we can't check the legacy lists
+            meets_legacy_policy = false;
+            goto lb_exit;
+        }
+
+        if (context->teamIdentifier) {
+            teamID.take(CFStringCreateWithCString(kCFAllocatorDefault, context->teamIdentifier, kCFStringEncodingUTF8));
+        }
+
+        secnotice("legacy_list", "checking the legacy list for %d, %@, %@", hashType, cd.get(), teamID.get());
+    #if TARGET_OS_OSX
+        if (SecAssessmentLegacyCheck(cd, hashType, teamID, &error.aref())) {
+            meets_legacy_policy = true;
+        } else {
+            meets_legacy_policy = false;
+            if (error.get() != NULL) {
+                secerror("Error checking with notarization daemon: %ld", CFErrorGetCode(error));
+            }
+        }
+    #endif
+    }
+lb_exit:
+    secnotice("legacy_list", "meetsDeveloperIDLegacyAllowedPolicy = %d", meets_legacy_policy);
+    return meets_legacy_policy;
+}
+
+}
+}