legacydevid.cpp
1 /* 2 * Copyright (c) 2019 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 #include "legacydevid.h" 25 #include "SecAssessment.h" 26 #include "requirement.h" 27 28 #include <Security/SecCertificatePriv.h> 29 30 namespace Security { 31 namespace CodeSigning { 32 33 static const CFStringRef kLegacyPolicyPreferenceDomain = CFSTR("com.apple.security.syspolicy"); 34 static const CFStringRef kLegacyPolicyAccountCreationCutOff = CFSTR("AccountCreationCutOffDate"); 35 static const CFStringRef kLegacyPolicySecureTimestampCutOff = CFSTR("SecureTimestampCutOffDate"); 36 static const CFAbsoluteTime kLegacyPolicyAccountCreationDefaultCutOff = 576374400.0; // seconds from January 1, 2001 to April 7, 2019 GMT 37 static const CFAbsoluteTime kLegacyPolicySecureTimestampDefaultCutOff = 581040000.0; // seconds from January 1, 2001 to June 1, 2019 GMT 38 39 static CFDateRef 40 copyCutOffDate(const CFStringRef key, CFAbsoluteTime defaultCutoff) 41 { 42 CFDateRef defaultDate = CFDateCreate(NULL, defaultCutoff); 43 CFDateRef outputDate = defaultDate; 44 CFDateRef prefDate = NULL; 45 46 CFTypeRef prefVal = (CFDateRef)CFPreferencesCopyValue(key, 47 kLegacyPolicyPreferenceDomain, 48 kCFPreferencesCurrentUser, 49 kCFPreferencesAnyHost); 50 if (prefVal && CFGetTypeID(prefVal) == CFDateGetTypeID()) { 51 prefDate = (CFDateRef)prefVal; 52 } 53 54 if (prefDate) { 55 CFComparisonResult res = CFDateCompare(defaultDate, prefDate, NULL); 56 if (res > 0) { 57 outputDate = prefDate; 58 } 59 } 60 61 CFRetain(outputDate); 62 63 if (prefVal) { 64 CFRelease(prefVal); 65 } 66 if (defaultDate) { 67 CFRelease(defaultDate); 68 } 69 return outputDate; 70 } 71 72 bool 73 meetsDeveloperIDLegacyAllowedPolicy(const Requirement::Context *context) 74 { 75 CFRef<CFDataRef> cd; 76 CFRef<CFErrorRef> error; 77 CFRef<CFStringRef> teamID; 78 bool meets_legacy_policy = false; 79 SecCSDigestAlgorithm hashType = kSecCodeSignatureNoHash; 80 SecCertificateRef cert = NULL; 81 CFAbsoluteTime accountCreationTime = 0.0; 82 83 if (context == NULL) { 84 meets_legacy_policy = false; 85 goto lb_exit; 86 } 87 88 // First check account creation date in certs 89 // An account creation date after the cut off must be notarized so it fails the legacy policy. 90 // No account creation date or an account creation date before the cut off requires additional checking 91 cert = context->cert(Requirement::leafCert); 92 if (SecCertificateGetDeveloperIDDate(cert, &accountCreationTime, &error.aref())) { 93 //There is an account creation date 94 CFRef<CFDateRef> accountCreationDate = CFDateCreate(NULL, accountCreationTime); 95 CFRef<CFDateRef> accountCreationCutoffDate = copyCutOffDate(kLegacyPolicyAccountCreationCutOff, 96 kLegacyPolicyAccountCreationDefaultCutOff); 97 secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Account Creation Date Cutoff: %@", accountCreationCutoffDate.get()); 98 secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Account Creation date: %@", accountCreationDate.get()); 99 100 CFComparisonResult res = CFDateCompare(accountCreationDate, accountCreationCutoffDate, NULL); 101 if (res >= 0) { 102 // The account was created on or after our cut off so it doesn't meet legacy policy 103 meets_legacy_policy = false; 104 secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Account creation date %@ is after cut-off %@", accountCreationDate.get(), accountCreationCutoffDate.get()); 105 goto lb_exit; 106 } 107 // Account creation date before the cut off means we fall through 108 } else { 109 CFIndex errorCode = CFErrorGetCode(error); 110 if (errorCode != errSecMissingRequiredExtension) { 111 secerror("Unexpected error checking account creation date: %ld", errorCode); 112 meets_legacy_policy = false; 113 goto lb_exit; 114 } 115 // there was no account creation date so fall through 116 } 117 118 // Next check secure time stamp 119 if (context->secureTimestamp) { 120 CFRef<CFDateRef> secureTimestampCutoffDate = copyCutOffDate(kLegacyPolicySecureTimestampCutOff, 121 kLegacyPolicySecureTimestampDefaultCutOff); 122 secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Secure Timestamp Cutoff Date cutoff: %@", secureTimestampCutoffDate.get()); 123 secinfo("meetsDevleoperIDLegacyAllowedPolicy", "Secure Timestamp: %@", context->secureTimestamp); 124 CFComparisonResult res = CFDateCompare(context->secureTimestamp, secureTimestampCutoffDate, NULL); 125 if (res >= 0) { 126 // Secure timestamp is on or after the cut of so it doesn't meet legacy policy 127 meets_legacy_policy = false; 128 secinfo("meetsDeveloperIDLegacyAllowedPolicy", "Secure timestamp %@ is after cut-off %@", context->secureTimestamp, secureTimestampCutoffDate.get()); 129 } else { 130 // Secure timestamp is before the cut off so we meet the legacy policy 131 meets_legacy_policy = true; 132 } 133 } 134 135 if (!meets_legacy_policy) { 136 // Just check against the legacy lists, both by hash and team ID. 137 if (context->directory) { 138 cd.take(context->directory->cdhash()); 139 hashType = (SecCSDigestAlgorithm)context->directory->hashType; 140 } else if (context->packageChecksum) { 141 cd = context->packageChecksum; 142 hashType = context->packageAlgorithm; 143 } 144 145 if (cd.get() == NULL) { 146 // No cdhash means we can't check the legacy lists 147 meets_legacy_policy = false; 148 goto lb_exit; 149 } 150 151 if (context->teamIdentifier) { 152 teamID.take(CFStringCreateWithCString(kCFAllocatorDefault, context->teamIdentifier, kCFStringEncodingUTF8)); 153 } 154 155 secnotice("legacy_list", "checking the legacy list for %d, %@, %@", hashType, cd.get(), teamID.get()); 156 #if TARGET_OS_OSX 157 if (SecAssessmentLegacyCheck(cd, hashType, teamID, &error.aref())) { 158 meets_legacy_policy = true; 159 } else { 160 meets_legacy_policy = false; 161 if (error.get() != NULL) { 162 secerror("Error checking with notarization daemon: %ld", CFErrorGetCode(error)); 163 } 164 } 165 #endif 166 } 167 lb_exit: 168 secnotice("legacy_list", "meetsDeveloperIDLegacyAllowedPolicy = %d", meets_legacy_policy); 169 return meets_legacy_policy; 170 } 171 172 } 173 }