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 "sectask/SystemEntitlements.h"
33 #include <Security/SecItem.h>
34 #include "utilities/SecCFRelease.h"
35 #include "utilities/SecCFWrappers.h"
36 #include "utilities/debugging.h"
37 #include <os/feature_private.h>
39 CFStringRef
SecTaskCopyStringForEntitlement(SecTaskRef task
,
40 CFStringRef entitlement
)
42 CFStringRef value
= (CFStringRef
)SecTaskCopyValueForEntitlement(task
,
44 if (value
&& CFGetTypeID(value
) != CFStringGetTypeID()) {
51 CFArrayRef
SecTaskCopyArrayOfStringsForEntitlement(SecTaskRef task
,
52 CFStringRef entitlement
)
54 CFArrayRef value
= (CFArrayRef
)SecTaskCopyValueForEntitlement(task
,
57 if (CFGetTypeID(value
) == CFArrayGetTypeID()) {
58 CFIndex ix
, count
= CFArrayGetCount(value
);
59 for (ix
= 0; ix
< count
; ++ix
) {
60 CFStringRef string
= (CFStringRef
)CFArrayGetValueAtIndex(value
, ix
);
61 if (CFGetTypeID(string
) != CFStringGetTypeID()) {
74 CFStringRef
SecTaskCopyApplicationIdentifier(SecTaskRef task
) {
75 // Catalyst apps may have the iOS style application identifier.
76 CFStringRef result
= SecTaskCopyStringForEntitlement(task
,
77 kSecEntitlementBasicApplicationIdentifier
);
79 result
= SecTaskCopyStringForEntitlement(task
,
80 kSecEntitlementAppleApplicationIdentifier
);
86 CFArrayRef
SecTaskCopySharedWebCredentialDomains(SecTaskRef task
) {
87 return SecTaskCopyArrayOfStringsForEntitlement(task
,
88 kSecEntitlementAssociatedDomains
);
92 CFArrayRef
SecTaskCopyAccessGroups(SecTaskRef task
)
94 CFMutableArrayRef groups
= NULL
;
96 bool onDemandInstallable
= SecTaskGetBooleanValueForEntitlement(task
, kSystemEntitlementOnDemandInstallCapable
);
98 CFArrayRef keychainAccessGroups
, appleSecurityApplicationGroups
;
100 CFArrayRef associatedAppIDs
;
102 keychainAccessGroups
= SecTaskCopyArrayOfStringsForEntitlement(task
, kSecEntitlementKeychainAccessGroups
);
103 appleSecurityApplicationGroups
= SecTaskCopyArrayOfStringsForEntitlement(task
, kSecEntitlementAppleSecurityApplicationGroups
);
104 appID
= SecTaskCopyApplicationIdentifier(task
);
105 // Catalyst apps (may?) have this entitlement.
106 associatedAppIDs
= SecTaskCopyArrayOfStringsForEntitlement(task
, kSecEntitlementAssociatedApplicationIdentifier
);
108 groups
= CFArrayCreateMutableForCFTypes(NULL
);
110 if (keychainAccessGroups
) {
111 CFArrayAppendArray(groups
, keychainAccessGroups
, CFRangeMake(0, CFArrayGetCount(keychainAccessGroups
)));
115 const bool entitlementsValidated
= SecTaskEntitlementsValidated(task
);
117 const bool entitlementsValidated
= true;
120 if (entitlementsValidated
) {
121 // If app-id or k-a-g are present but binary is not validated, just honor k-a-g.
122 // Because AMFI would not allow client to run if it would have k-a-g entitlement
123 // and not being properly signed. Assoc-app-id behaves similarly.
124 if (associatedAppIDs
) {
125 CFArrayAppendAll(groups
, associatedAppIDs
);
128 CFArrayAppendValue(groups
, appID
);
130 if (appleSecurityApplicationGroups
) {
131 if (onDemandInstallable
) {
132 // This is perfectly legal for other functionality but not for keychain use
133 secnotice("entitlements", "Ignoring \"%@\" because client is API-restricted", kSecEntitlementAppleSecurityApplicationGroups
);
135 CFArrayAppendArray(groups
, appleSecurityApplicationGroups
, CFRangeMake(0, CFArrayGetCount(appleSecurityApplicationGroups
)));
139 // Try to provide some hopefully helpful diagnostics for common failure cases.
140 if (CFArrayGetCount(groups
) == 0) {
142 secwarning("Entitlement %@=%@ is ignored because of invalid application signature or incorrect provisioning profile",
143 kSecEntitlementApplicationIdentifier
, appID
);
145 if (appleSecurityApplicationGroups
) {
146 secwarning("Entitlement %@=%@ is ignored because of invalid application signature or incorrect provisioning profile",
147 kSecEntitlementAppleSecurityApplicationGroups
, appleSecurityApplicationGroups
);
152 // Do not allow to explicitly specify com.apple.token if token support is not allowed by feature flags.
153 CFIndex index
= CFArrayGetFirstIndexOfValue(groups
, CFRangeMake(0, CFArrayGetCount(groups
)), kSecAttrAccessGroupToken
);
154 if (index
!= kCFNotFound
) {
155 if (os_feature_enabled(CryptoTokenKit
, UseTokens
)) {
156 // Make sure that com.apple.token is last one. This is because it is always read-only group and therefore updating keychain
157 // operations without explicitly set kSecAttrAccessGroup attribute would always fail.
158 CFArrayRemoveValueAtIndex(groups
, index
);
159 CFArrayAppendValue(groups
, kSecAttrAccessGroupToken
);
161 secwarning("Keychain access group com.apple.token ignored, feature not available");
162 CFArrayRemoveValueAtIndex(groups
, index
);
168 * We would like to add implicit token access group always, but we avoid doing that in case that application
169 * clearly intended to use non-smartcard functionality of keychain but messed up signing or provisioning. In this case,
170 * we want to return -34018 (errSecMissingEntitlements) to help diagnosing the issue for the app authors and adding
171 * implicit token access group would instead result in errSecItemNotFound.
173 bool entitlementsFailure
= (CFArrayGetCount(groups
) == 0 && appID
!= NULL
);
174 if (!entitlementsFailure
) {
175 bool addTokenGroup
= os_feature_enabled(CryptoTokenKit
, UseTokens
);
176 if (addTokenGroup
&& !CFArrayContainsValue(groups
, CFRangeMake(0, CFArrayGetCount(groups
)), kSecAttrAccessGroupToken
)) {
177 CFArrayAppendValue(groups
, kSecAttrAccessGroupToken
);
182 CFReleaseNull(associatedAppIDs
);
183 CFReleaseNull(appID
);
184 CFReleaseNull(keychainAccessGroups
);
185 CFReleaseNull(appleSecurityApplicationGroups
);
191 SecTaskGetBooleanValueForEntitlement(SecTaskRef task
, CFStringRef entitlement
)
193 CFTypeRef value
= SecTaskCopyValueForEntitlement(task
, entitlement
, NULL
);
196 if (value
&& CFBooleanGetTypeID() == CFGetTypeID(value
)) {
197 ok
= CFBooleanGetValue(value
);
199 CFReleaseNull(value
);