2 * Copyright (c) 2019 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "legacydevid.h"
25 #include "SecAssessment.h"
26 #include "requirement.h"
28 #include <Security/SecCertificatePriv.h>
31 namespace CodeSigning
{
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
40 copyCutOffDate(const CFStringRef key
, CFAbsoluteTime defaultCutoff
)
42 CFDateRef defaultDate
= CFDateCreate(NULL
, defaultCutoff
);
43 CFDateRef outputDate
= defaultDate
;
44 CFDateRef prefDate
= NULL
;
46 CFTypeRef prefVal
= (CFDateRef
)CFPreferencesCopyValue(key
,
47 kLegacyPolicyPreferenceDomain
,
48 kCFPreferencesCurrentUser
,
49 kCFPreferencesAnyHost
);
50 if (prefVal
&& CFGetTypeID(prefVal
) == CFDateGetTypeID()) {
51 prefDate
= (CFDateRef
)prefVal
;
55 CFComparisonResult res
= CFDateCompare(defaultDate
, prefDate
, NULL
);
57 outputDate
= prefDate
;
67 CFRelease(defaultDate
);
73 meetsDeveloperIDLegacyAllowedPolicy(const Requirement::Context
*context
)
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;
83 if (context
== NULL
) {
84 meets_legacy_policy
= false;
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());
100 CFComparisonResult res
= CFDateCompare(accountCreationDate
, accountCreationCutoffDate
, NULL
);
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());
107 // Account creation date before the cut off means we fall through
109 CFIndex errorCode
= CFErrorGetCode(error
);
110 if (errorCode
!= errSecMissingRequiredExtension
) {
111 secerror("Unexpected error checking account creation date: %ld", errorCode
);
112 meets_legacy_policy
= false;
115 // there was no account creation date so fall through
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
);
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());
130 // Secure timestamp is before the cut off so we meet the legacy policy
131 meets_legacy_policy
= true;
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
;
145 if (cd
.get() == NULL
) {
146 // No cdhash means we can't check the legacy lists
147 meets_legacy_policy
= false;
151 if (context
->teamIdentifier
) {
152 teamID
.take(CFStringCreateWithCString(kCFAllocatorDefault
, context
->teamIdentifier
, kCFStringEncodingUTF8
));
155 secnotice("legacy_list", "checking the legacy list for %d, %@, %@", hashType
, cd
.get(), teamID
.get());
157 if (SecAssessmentLegacyCheck(cd
, hashType
, teamID
, &error
.aref())) {
158 meets_legacy_policy
= true;
160 meets_legacy_policy
= false;
161 if (error
.get() != NULL
) {
162 secerror("Error checking with notarization daemon: %ld", CFErrorGetCode(error
));
168 secnotice("legacy_list", "meetsDeveloperIDLegacyAllowedPolicy = %d", meets_legacy_policy
);
169 return meets_legacy_policy
;