]> git.saurik.com Git - apple/security.git/blob - OSX/sec/ipc/server_entitlement_helpers.c
Security-58286.20.16.tar.gz
[apple/security.git] / OSX / sec / ipc / server_entitlement_helpers.c
1 /*
2 * Copyright (c) 2017 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 <pthread/pthread.h>
25
26 #include "server_entitlement_helpers.h"
27
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>
36
37 CFStringRef SecTaskCopyStringForEntitlement(SecTaskRef task,
38 CFStringRef entitlement)
39 {
40 CFStringRef value = (CFStringRef)SecTaskCopyValueForEntitlement(task,
41 entitlement, NULL);
42 if (value && CFGetTypeID(value) != CFStringGetTypeID()) {
43 CFRelease(value);
44 value = NULL;
45 }
46
47 return value;
48 }
49
50 CFArrayRef SecTaskCopyArrayOfStringsForEntitlement(SecTaskRef task,
51 CFStringRef entitlement)
52 {
53 CFArrayRef value = (CFArrayRef)SecTaskCopyValueForEntitlement(task,
54 entitlement, NULL);
55 if (value) {
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()) {
61 CFRelease(value);
62 value = NULL;
63 break;
64 }
65 }
66 } else {
67 CFRelease(value);
68 value = NULL;
69 }
70 }
71
72 return value;
73 }
74
75 CFStringRef SecTaskCopyApplicationIdentifier(SecTaskRef task) {
76 return SecTaskCopyStringForEntitlement(task,
77 kSecEntitlementApplicationIdentifier);
78 }
79 #if TARGET_OS_IOS
80 CFArrayRef SecTaskCopySharedWebCredentialDomains(SecTaskRef task) {
81 return SecTaskCopyArrayOfStringsForEntitlement(task,
82 kSecEntitlementAssociatedDomains);
83 }
84 #endif
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;
96 #if TARGET_OS_OSX
97 entitlementsValidated = SecTaskEntitlementsValidated(task);
98 if ((appID || asagLen) && !entitlementsValidated) {
99 CFReleaseNull(appID);
100 asagLen = 0;
101 }
102 #endif
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;
106 #if TARGET_OS_IPHONE
107 if (len + tokenLen)
108 #endif
109 {
110 groups = CFArrayCreateMutable(kCFAllocatorDefault, len + tokenLen, &kCFTypeArrayCallBacks);
111 if (kagLen)
112 CFArrayAppendArray(groups, keychainAccessGroups, CFRangeMake(0, kagLen));
113 if (appID)
114 CFArrayAppendValue(groups, appID);
115 if (asagLen)
116 CFArrayAppendArray(groups, appleSecurityApplicationGroups, CFRangeMake(0, asagLen));
117 if (tokenLen)
118 CFArrayAppendValue(groups, kSecAttrAccessGroupToken);
119 #if TARGET_IPHONE_SIMULATOR
120 } else {
121 secwarning("No keychain access group specified while running in simulator, falling back to default set");
122 groups = (CFMutableArrayRef)CFRetainSafe(SecAccessGroupsGetCurrent());
123 #endif
124 }
125
126 CFReleaseSafe(appID);
127 CFReleaseSafe(keychainAccessGroups);
128 CFReleaseSafe(appleSecurityApplicationGroups);
129 return groups;
130 }
131
132 #if TARGET_OS_IPHONE
133 pthread_key_t taskThreadKey;
134 void secTaskDiagnoseEntitlements(CFArrayRef accessGroups) {
135 SecTaskRef taskRef = pthread_getspecific(taskThreadKey);
136 if (taskRef == NULL)
137 return;
138
139 CFErrorRef error = NULL;
140 CFArrayRef entitlementNames = CFArrayCreateForCFTypes(NULL,
141 kSecEntitlementApplicationIdentifier,
142 kSecEntitlementKeychainAccessGroups,
143 kSecEntitlementAppleSecurityApplicationGroups,
144 NULL);
145 CFDictionaryRef rawEntitlements = SecTaskCopyValuesForEntitlements(taskRef, entitlementNames, &error);
146 CFReleaseNull(entitlementNames);
147
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);
153 int err = (int) c;
154
155 switch (err) {
156 case ESRCH: // no such process (bad pid or process died)
157 return;
158 default:
159 break;
160 }
161 }
162 }
163
164 uint32_t cs_flags = SecTaskGetCodeSignStatus(taskRef);
165 CFStringRef identifier = SecTaskCopySigningIdentifier(taskRef, NULL);
166 CFStringRef message = NULL;
167
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);
172 } else {
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);
182 }
183 char buffer[1000] = "?";
184 CFStringGetCString(message, buffer, sizeof(buffer), kCFStringEncodingUTF8);
185 secerror("%s", buffer);
186 __security_simulatecrash(message, __sec_exception_code_MissingEntitlements);
187
188 CFReleaseNull(rawEntitlements);
189 CFReleaseNull(message);
190 CFReleaseNull(identifier);
191 CFReleaseNull(error);
192 }
193 #endif
194
195 bool SecTaskGetBooleanValueForEntitlement(SecTaskRef task,
196 CFStringRef entitlement) {
197 CFTypeRef canModify = SecTaskCopyValueForEntitlement(task, entitlement, NULL);
198 if (!canModify)
199 return false;
200 CFTypeID canModifyType = CFGetTypeID(canModify);
201 bool ok = (CFBooleanGetTypeID() == canModifyType) && CFBooleanGetValue((CFBooleanRef)canModify);
202 CFRelease(canModify);
203 return ok;
204 }
205