2 * Copyright (c) 2008,2010-2013 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@
25 #include "SecTaskPriv.h"
27 #include <utilities/debugging.h>
28 #include <utilities/entitlements.h>
30 #include <AssertMacros.h>
31 #include <CoreFoundation/CFRuntime.h>
32 #include <IOKit/IOKitLib.h>
33 #include <IOKit/IOCFUnserialize.h>
34 #include <System/sys/codesign.h>
35 #include <bsm/libbsm.h>
38 #include <utilities/SecCFWrappers.h>
39 #include <xpc/private.h>
41 #include <sys/sysctl.h>
44 /* These won't exist until we unify codesigning */
45 #include <Security/SecCode.h>
46 #include <Security/SecCodePriv.h>
47 #include <Security/SecRequirement.h>
48 #endif /* TARGET_OS_OSX */
55 /* Track whether we've loaded entitlements independently since after the
56 * load, entitlements may legitimately be NULL */
57 Boolean entitlementsLoaded
;
58 CFDictionaryRef entitlements
;
60 /* for debugging only, shown by debugDescription */
64 static bool check_task(SecTaskRef task
) {
65 return SecTaskGetTypeID() == CFGetTypeID(task
);
68 static void SecTaskFinalize(CFTypeRef cfTask
)
70 SecTaskRef task
= (SecTaskRef
) cfTask
;
71 CFReleaseNull(task
->entitlements
);
75 // Define PRIdPID (proper printf format string for pid_t)
76 #define PRIdPID PRId32
78 static CFStringRef
SecTaskCopyDebugDescription(CFTypeRef cfTask
)
80 SecTaskRef task
= (SecTaskRef
) cfTask
;
82 audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
);
84 const char *task_name
;
85 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
};
87 size_t len
= sizeof(kp
);
88 if (sysctl(mib
, 4, &kp
, &len
, NULL
, 0) == -1 || len
== 0)
89 task_name
= strerror(errno
);
91 task_name
= kp
.kp_proc
.p_comm
;
93 return CFStringCreateWithFormat(CFGetAllocator(task
), NULL
, CFSTR("%s[%" PRIdPID
"]/%d#%d LF=%d"), task_name
, pid
,
94 task
->entitlementsLoaded
, task
->entitlements
? (int)CFDictionaryGetCount(task
->entitlements
) : -1, task
->lastFailure
);
97 CFGiblisWithFunctions(SecTask
, NULL
, NULL
, SecTaskFinalize
, NULL
, NULL
, NULL
, SecTaskCopyDebugDescription
, NULL
, NULL
, NULL
)
99 static SecTaskRef
init_task_ref(CFAllocatorRef allocator
)
101 CFIndex extra
= sizeof(struct __SecTask
) - sizeof(CFRuntimeBase
);
102 return (SecTaskRef
) _CFRuntimeCreateInstance(allocator
, SecTaskGetTypeID(), extra
, NULL
);
105 SecTaskRef
SecTaskCreateFromSelf(CFAllocatorRef allocator
)
107 SecTaskRef task
= init_task_ref(allocator
);
110 kern_return_t kr
= KERN_FAILURE
;
111 mach_msg_type_number_t autoken_cnt
= TASK_AUDIT_TOKEN_COUNT
;
112 kr
= task_info(mach_task_self(), TASK_AUDIT_TOKEN
, (task_info_t
)&task
->token
, &autoken_cnt
);
113 if (kr
== KERN_SUCCESS
) {
114 task
->entitlementsLoaded
= false;
115 task
->entitlements
= NULL
;
124 SecTaskRef
SecTaskCreateWithAuditToken(CFAllocatorRef allocator
, audit_token_t token
)
126 SecTaskRef task
= init_task_ref(allocator
);
129 memcpy(&task
->token
, &token
, sizeof(token
));
130 task
->entitlementsLoaded
= false;
131 task
->entitlements
= NULL
;
138 SecTaskCreateWithXPCMessage(xpc_object_t _Nonnull message
)
142 if (message
== NULL
|| xpc_get_type(message
) != XPC_TYPE_DICTIONARY
) {
145 xpc_dictionary_get_audit_token(message
, &token
);
147 return SecTaskCreateWithAuditToken(NULL
, token
);
158 csops_task(SecTaskRef task
, int ops
, void *blob
, size_t size
)
163 audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
);
164 rc
= csops_audittoken(pid
, ops
, blob
, size
, &task
->token
);
166 task
->lastFailure
= (rc
== -1) ? errno
: 0;
171 SecTaskCopyIdentifier(SecTaskRef task
, int op
, CFErrorRef
*error
)
173 CFStringRef signingId
= NULL
;
175 struct csheader header
;
179 ret
= csops_task(task
, op
, &header
, sizeof(header
));
180 if (ret
!= -1 || errno
!= ERANGE
)
183 bufferlen
= ntohl(header
.length
);
184 /* check for insane values */
185 if (bufferlen
> 1024 * 1024 || bufferlen
< 8) {
189 data
= malloc(bufferlen
+ 1);
194 ret
= csops_task(task
, op
, data
, bufferlen
);
199 data
[bufferlen
] = '\0';
201 signingId
= CFStringCreateWithCString(NULL
, data
+ 8, kCFStringEncodingUTF8
);
207 *error
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
);
213 SecTaskCopySigningIdentifier(SecTaskRef task
, CFErrorRef
*error
)
215 return SecTaskCopyIdentifier(task
, CS_OPS_IDENTITY
, error
);
219 SecTaskCopyTeamIdentifier(SecTaskRef task
, CFErrorRef
*error
)
221 return SecTaskCopyIdentifier(task
, CS_OPS_TEAMID
, error
);
225 SecTaskGetCodeSignStatus(SecTaskRef task
)
228 if (csops_task(task
, CS_OPS_STATUS
, &flags
, sizeof(flags
)) != 0)
233 static bool SecTaskLoadEntitlements(SecTaskRef task
, CFErrorRef
*error
)
235 CFMutableDictionaryRef entitlements
= NULL
;
236 struct csheader header
;
237 uint8_t *buffer
= NULL
;
241 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, &header
, sizeof(header
));
242 /* Any other combination means no entitlements */
244 if (errno
!= ERANGE
) {
245 int entitlementErrno
= errno
;
247 uint32_t cs_flags
= -1;
248 if (-1 == csops_task(task
, CS_OPS_STATUS
, &cs_flags
, sizeof(cs_flags
))) {
249 syslog(LOG_NOTICE
, "Failed to get cs_flags, error=%d", errno
);
252 if (cs_flags
!= 0) { // was signed
254 audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
);
255 syslog(LOG_NOTICE
, "SecTaskLoadEntitlements failed error=%d cs_flags=%x, pid=%d", entitlementErrno
, cs_flags
, pid
); // to ease diagnostics
257 CFStringRef description
= SecTaskCopyDebugDescription(task
);
258 char *descriptionBuf
= NULL
;
259 CFIndex descriptionSize
= CFStringGetLength(description
) * 4;
260 descriptionBuf
= (char *)malloc(descriptionSize
);
261 if (!CFStringGetCString(description
, descriptionBuf
, descriptionSize
, kCFStringEncodingUTF8
)) {
262 descriptionBuf
[0] = 0;
265 syslog(LOG_NOTICE
, "SecTaskCopyDebugDescription: %s", descriptionBuf
);
266 CFReleaseNull(description
);
267 free(descriptionBuf
);
269 task
->lastFailure
= entitlementErrno
; // was overwritten by csops_task(CS_OPS_STATUS) above
271 // EINVAL is what the kernel says for unsigned code, so we'll have to let that pass
272 if (entitlementErrno
== EINVAL
) {
273 task
->entitlementsLoaded
= true;
276 ret
= entitlementErrno
; // what really went wrong
277 goto out
; // bail out
279 bufferlen
= ntohl(header
.length
);
280 /* check for insane values */
281 if (bufferlen
> 1024 * 1024 || bufferlen
< 8) {
285 buffer
= malloc(bufferlen
);
286 if (buffer
== NULL
) {
290 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, buffer
, bufferlen
);
296 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, buffer
+8, bufferlen
-8, kCFAllocatorNull
);
297 entitlements
= (CFMutableDictionaryRef
) CFPropertyListCreateWithData(kCFAllocatorDefault
, data
, kCFPropertyListMutableContainers
, NULL
, error
);
300 if ((entitlements
==NULL
) || (CFGetTypeID(entitlements
)!=CFDictionaryGetTypeID())){
301 ret
= EDOM
; // don't use EINVAL here; it conflates problems with syscall error returns
305 bool entitlementsModified
= updateCatalystEntitlements(entitlements
);
306 if (entitlementsModified
) {
308 audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
);
309 secinfo("SecTask", "Fixed catalyst entitlements for process %d", pid
);
313 task
->entitlements
= entitlements
? CFRetain(entitlements
) : NULL
;
314 task
->entitlementsLoaded
= true;
317 CFReleaseNull(entitlements
);
320 if (ret
&& error
&& *error
==NULL
)
321 *error
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
);
326 CFTypeRef
SecTaskCopyValueForEntitlement(SecTaskRef task
, CFStringRef entitlement
, CFErrorRef
*error
)
328 CFTypeRef value
= NULL
;
329 require(check_task(task
), out
);
331 /* Load entitlements if necessary */
332 if (task
->entitlementsLoaded
== false) {
333 require_quiet(SecTaskLoadEntitlements(task
, error
), out
);
336 if (task
->entitlements
!= NULL
) {
337 value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
339 /* Return something the caller must release */
348 CFDictionaryRef
SecTaskCopyValuesForEntitlements(SecTaskRef task
, CFArrayRef entitlements
, CFErrorRef
*error
)
350 CFMutableDictionaryRef values
= NULL
;
351 require(check_task(task
), out
);
353 /* Load entitlements if necessary */
354 if (task
->entitlementsLoaded
== false) {
355 SecTaskLoadEntitlements(task
, error
);
358 /* Iterate over the passed in entitlements, populating the dictionary
359 * If entitlements were loaded but none were present, return an empty
361 if (task
->entitlementsLoaded
== true) {
363 CFIndex i
, count
= CFArrayGetCount(entitlements
);
364 values
= CFDictionaryCreateMutable(CFGetAllocator(task
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
365 if (task
->entitlements
!= NULL
) {
366 for (i
= 0; i
< count
; i
++) {
367 CFStringRef entitlement
= CFArrayGetValueAtIndex(entitlements
, i
);
368 CFTypeRef value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
370 CFDictionarySetValue(values
, entitlement
, value
);
381 * Determine if the given task meets a specified requirement.
384 SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
)
387 SecCodeRef code
= NULL
;
388 SecRequirementRef req
= NULL
;
390 CFMutableDictionaryRef codeDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
391 CFDataRef auditData
= CFDataCreate(kCFAllocatorDefault
, (const UInt8
*)&task
->token
, sizeof(audit_token_t
));
392 CFDictionarySetValue(codeDict
, kSecGuestAttributeAudit
, auditData
);
393 status
= SecCodeCopyGuestWithAttributes(NULL
, codeDict
, kSecCSDefaultFlags
, &code
);
394 CFReleaseNull(codeDict
);
395 CFReleaseNull(auditData
);
398 status
= SecRequirementCreateWithString(requirement
,
399 kSecCSDefaultFlags
, &req
);
402 status
= SecCodeCheckValidity(code
, kSecCSDefaultFlags
, req
);
410 #endif /* SEC_OS_OSX */
412 Boolean
SecTaskEntitlementsValidated(SecTaskRef task
) {
413 // TODO: Cache the result
414 uint32_t csflags
= 0;
415 const uint32_t mask
= CS_VALID
| CS_KILL
| CS_ENTITLEMENTS_VALIDATED
;
416 const uint32_t debug_mask
= CS_DEBUGGED
| CS_ENTITLEMENTS_VALIDATED
;
417 int rc
= csops_task(task
, CS_OPS_STATUS
, &csflags
, sizeof(csflags
));
418 // Allow debugged processes that were valid to continue being treated as valid
419 // We need this all the time (not just on internal) because third parties may need to debug their entitled process in xcode
420 return (rc
!= -1) && ((mask
& csflags
) == mask
|| (debug_mask
& csflags
) == debug_mask
);