2 * Copyright (c) 2017 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 <pthread/pthread.h>
26 #include "server_entitlement_helpers.h"
28 #include <Security/SecTask.h>
29 #include <Security/SecTaskPriv.h>
30 #include <ipc/securityd_client.h>
31 #include <Security/SecEntitlements.h>
32 #include <Security/SecItem.h>
33 #include <utilities/SecCFRelease.h>
34 #include <utilities/SecCFWrappers.h>
35 #include <utilities/debugging.h>
37 CFStringRef
SecTaskCopyStringForEntitlement(SecTaskRef task
,
38 CFStringRef entitlement
)
40 CFStringRef value
= (CFStringRef
)SecTaskCopyValueForEntitlement(task
,
42 if (value
&& CFGetTypeID(value
) != CFStringGetTypeID()) {
50 CFArrayRef
SecTaskCopyArrayOfStringsForEntitlement(SecTaskRef task
,
51 CFStringRef entitlement
)
53 CFArrayRef value
= (CFArrayRef
)SecTaskCopyValueForEntitlement(task
,
56 if (CFGetTypeID(value
) == CFArrayGetTypeID()) {
57 CFIndex ix
, count
= CFArrayGetCount(value
);
58 for (ix
= 0; ix
< count
; ++ix
) {
59 CFStringRef string
= (CFStringRef
)CFArrayGetValueAtIndex(value
, ix
);
60 if (CFGetTypeID(string
) != CFStringGetTypeID()) {
75 CFStringRef
SecTaskCopyApplicationIdentifier(SecTaskRef task
) {
76 return SecTaskCopyStringForEntitlement(task
,
77 kSecEntitlementApplicationIdentifier
);
80 CFArrayRef
SecTaskCopySharedWebCredentialDomains(SecTaskRef task
) {
81 return SecTaskCopyArrayOfStringsForEntitlement(task
,
82 kSecEntitlementAssociatedDomains
);
85 CFArrayRef
SecTaskCopyAccessGroups(SecTaskRef task
) {
86 CFMutableArrayRef groups
= NULL
;
87 CFArrayRef keychainAccessGroups
= SecTaskCopyArrayOfStringsForEntitlement(task
,
88 kSecEntitlementKeychainAccessGroups
);
89 CFArrayRef appleSecurityApplicationGroups
= SecTaskCopyArrayOfStringsForEntitlement(task
,
90 kSecEntitlementAppleSecurityApplicationGroups
);
91 CFStringRef appID
= SecTaskCopyApplicationIdentifier(task
);
92 CFIndex kagLen
= keychainAccessGroups
? CFArrayGetCount(keychainAccessGroups
) : 0;
93 CFIndex asagLen
= appleSecurityApplicationGroups
? CFArrayGetCount(appleSecurityApplicationGroups
) : 0;
94 bool entitlementsValidated
= true;
95 bool hasEntitlements
= (kagLen
+ asagLen
+ (appID
? 1 : 0)) > 0;
97 entitlementsValidated
= SecTaskEntitlementsValidated(task
);
98 if ((appID
|| asagLen
) && !entitlementsValidated
) {
103 CFIndex len
= kagLen
+ asagLen
+ (appID
? 1 : 0);
104 // Always allow access to com.apple.token access group, unless entitlement validation explicitly failed.
105 CFIndex tokenLen
= (!hasEntitlements
|| entitlementsValidated
) ? 1 : 0;
110 groups
= CFArrayCreateMutable(kCFAllocatorDefault
, len
+ tokenLen
, &kCFTypeArrayCallBacks
);
112 CFArrayAppendArray(groups
, keychainAccessGroups
, CFRangeMake(0, kagLen
));
114 CFArrayAppendValue(groups
, appID
);
116 CFArrayAppendArray(groups
, appleSecurityApplicationGroups
, CFRangeMake(0, asagLen
));
118 CFArrayAppendValue(groups
, kSecAttrAccessGroupToken
);
119 #if TARGET_IPHONE_SIMULATOR
121 secwarning("No keychain access group specified while running in simulator, falling back to default set");
122 groups
= (CFMutableArrayRef
)CFRetainSafe(SecAccessGroupsGetCurrent());
126 CFReleaseSafe(appID
);
127 CFReleaseSafe(keychainAccessGroups
);
128 CFReleaseSafe(appleSecurityApplicationGroups
);
133 pthread_key_t taskThreadKey
;
134 void secTaskDiagnoseEntitlements(CFArrayRef accessGroups
) {
135 SecTaskRef taskRef
= pthread_getspecific(taskThreadKey
);
139 CFErrorRef error
= NULL
;
140 CFArrayRef entitlementNames
= CFArrayCreateForCFTypes(NULL
,
141 kSecEntitlementApplicationIdentifier
,
142 kSecEntitlementKeychainAccessGroups
,
143 kSecEntitlementAppleSecurityApplicationGroups
,
145 CFDictionaryRef rawEntitlements
= SecTaskCopyValuesForEntitlements(taskRef
, entitlementNames
, &error
);
146 CFReleaseNull(entitlementNames
);
148 // exclude some error types because they're accounted-for and not the reason we're here
149 if (rawEntitlements
== NULL
&& error
) {
150 CFErrorDomain domain
= CFErrorGetDomain(error
);
151 if (domain
&& CFEqual(domain
, kCFErrorDomainPOSIX
)) {
152 CFIndex c
= CFErrorGetCode(error
);
156 case ESRCH
: // no such process (bad pid or process died)
164 uint32_t cs_flags
= SecTaskGetCodeSignStatus(taskRef
);
165 CFStringRef identifier
= SecTaskCopySigningIdentifier(taskRef
, NULL
);
166 CFStringRef message
= NULL
;
168 if (rawEntitlements
== NULL
) { // NULL indicates failure-to-fetch (SecTask entitlements not initialized)
169 message
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("failed to fetch keychain client entitlements. task=%@ procid=%@ cs_flags=0x%08.8x error=%@"),
170 taskRef
, identifier
, cs_flags
, error
);
171 secerror("MISSING keychain entitlements: retrieve-entitlements error %@", error
);
173 // non-NULL entitlement return => SecTaskCopyEntitlements succeeeded, no error
174 // but note that kernel EINVAL => no entitlements, no error to deal with unsigned code
175 message
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("found no keychain client entitlements. task=%@ procid=%@ cs_flags=0x%08.8x"),
176 taskRef
, identifier
, cs_flags
);
177 secerror("MISSING keychain entitlements: raw entitlement values: %@", rawEntitlements
);
178 secerror("MISSING keychain entitlements: original ag: %@", accessGroups
);
179 CFArrayRef newAccessGroups
= SecTaskCopyAccessGroups(taskRef
);
180 secerror("MISSING keychain entitlements: newly parsed ag: %@", newAccessGroups
);
181 CFReleaseNull(newAccessGroups
);
183 char buffer
[1000] = "?";
184 CFStringGetCString(message
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
);
185 secerror("%s", buffer
);
186 __security_simulatecrash(message
, __sec_exception_code_MissingEntitlements
);
188 CFReleaseNull(rawEntitlements
);
189 CFReleaseNull(message
);
190 CFReleaseNull(identifier
);
191 CFReleaseNull(error
);
195 bool SecTaskGetBooleanValueForEntitlement(SecTaskRef task
,
196 CFStringRef entitlement
) {
197 CFTypeRef canModify
= SecTaskCopyValueForEntitlement(task
, entitlement
, NULL
);
200 CFTypeID canModifyType
= CFGetTypeID(canModify
);
201 bool ok
= (CFBooleanGetTypeID() == canModifyType
) && CFBooleanGetValue((CFBooleanRef
)canModify
);
202 CFRelease(canModify
);