/ OSX / libsecurity_codesigning / lib / legacydevid.cpp
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  }