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@
26 #include <utilities/debugging.h>
28 #include <AssertMacros.h>
29 #include <CoreFoundation/CFRuntime.h>
30 #include <IOKit/IOKitLib.h>
31 #include <IOKit/IOCFUnserialize.h>
32 #include <System/sys/codesign.h>
33 #include <bsm/libbsm.h>
36 #include <utilities/SecCFWrappers.h>
42 #include <sys/sysctl.h>
51 /* Track whether we've loaded entitlements independently since after the
52 * load, entitlements may legitimately be NULL */
53 Boolean entitlementsLoaded
;
54 CFDictionaryRef entitlements
;
57 static bool check_task(SecTaskRef task
) {
58 return SecTaskGetTypeID() == CFGetTypeID(task
);
61 static void SecTaskFinalize(CFTypeRef cfTask
)
63 SecTaskRef task
= (SecTaskRef
) cfTask
;
65 if (task
->entitlements
!= NULL
) {
66 CFRelease(task
->entitlements
);
67 task
->entitlements
= NULL
;
72 // Define PRIdPID (proper printf format string for pid_t)
73 #define PRIdPID PRId32
75 static CFStringRef
SecTaskCopyDebugDescription(CFTypeRef cfTask
)
77 SecTaskRef task
= (SecTaskRef
) cfTask
;
79 if (task
->pid_self
==-1) {
80 audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
);
86 #define MAX_PROCNAME 32
87 char task_name
[MAX_PROCNAME
+ 1] = {};
88 proc_name(pid
, task_name
, MAX_PROCNAME
);
90 const char *task_name
;
91 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
};
93 size_t len
= sizeof(kp
);
94 if (sysctl(mib
, 4, &kp
, &len
, NULL
, 0) == -1 || len
== 0)
95 task_name
= strerror(errno
);
97 task_name
= kp
.kp_proc
.p_comm
;
100 return CFStringCreateWithFormat(CFGetAllocator(task
), NULL
, CFSTR("%s[%" PRIdPID
"]"), task_name
, pid
);
103 CFGiblisWithFunctions(SecTask
, NULL
, NULL
, SecTaskFinalize
, NULL
, NULL
, NULL
, SecTaskCopyDebugDescription
, NULL
, NULL
, NULL
)
105 static SecTaskRef
init_task_ref(CFAllocatorRef allocator
)
107 CFIndex extra
= sizeof(struct __SecTask
) - sizeof(CFRuntimeBase
);
108 return (SecTaskRef
) _CFRuntimeCreateInstance(allocator
, SecTaskGetTypeID(), extra
, NULL
);
111 SecTaskRef
SecTaskCreateFromSelf(CFAllocatorRef allocator
)
113 SecTaskRef task
= init_task_ref(allocator
);
116 memset(&task
->token
, 0, sizeof(task
->token
));
117 task
->entitlementsLoaded
= false;
118 task
->entitlements
= NULL
;
119 task
->pid_self
= getpid();
125 SecTaskRef
SecTaskCreateWithAuditToken(CFAllocatorRef allocator
, audit_token_t token
)
127 SecTaskRef task
= init_task_ref(allocator
);
130 memcpy(&task
->token
, &token
, sizeof(token
));
131 task
->entitlementsLoaded
= false;
132 task
->entitlements
= NULL
;
145 csops_task(SecTaskRef task
, int ops
, void *blob
, size_t size
)
147 if (task
->pid_self
==-1) {
149 audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
);
150 return csops_audittoken(pid
, ops
, blob
, size
, &task
->token
);
153 return csops(task
->pid_self
, ops
, blob
, size
);
156 /* This may need to be exported at some point */
158 SecTaskCopySigningIdentifier(SecTaskRef task
, CFErrorRef
*error
)
160 CFStringRef signingId
= NULL
;
162 struct csheader header
;
166 ret
= csops_task(task
, CS_OPS_IDENTITY
, &header
, sizeof(header
));
167 if (ret
!= -1 || errno
!= ERANGE
)
170 bufferlen
= ntohl(header
.length
);
171 /* check for insane values */
172 if (bufferlen
> 1024 * 1024 || bufferlen
< 8) {
176 data
= malloc(bufferlen
+ 1);
181 ret
= csops_task(task
, CS_OPS_IDENTITY
, data
, bufferlen
);
186 data
[bufferlen
] = '\0';
188 signingId
= CFStringCreateWithCString(NULL
, data
+ 8, kCFStringEncodingUTF8
);
194 *error
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
);
200 static bool SecTaskLoadEntitlements(SecTaskRef task
, CFErrorRef
*error
)
202 CFMutableDictionaryRef entitlements
= NULL
;
203 struct csheader header
;
204 uint8_t *buffer
= NULL
;
209 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, &header
, sizeof(header
));
210 /* Any other combination means no entitlements */
212 if (errno
!= ERANGE
) {
213 int entitlementErrno
= errno
;
215 uint32_t cs_flags
= -1;
216 if (-1 == csops_task(task
, CS_OPS_STATUS
, &cs_flags
, sizeof(cs_flags
))) {
217 syslog(LOG_NOTICE
, "Failed to get cs_flags, error=%d", errno
);
220 syslog(LOG_NOTICE
, "SecTaskLoadEntitlements failed error=%d cs_flags=%x, task->pid_self=%d", entitlementErrno
, cs_flags
, task
->pid_self
); // to ease diagnostics
222 CFStringRef description
= SecTaskCopyDebugDescription(task
);
223 char *descriptionBuf
= NULL
;
224 CFIndex descriptionSize
= CFStringGetLength(description
) * 4;
225 descriptionBuf
= (char *)malloc(descriptionSize
);
226 if (!CFStringGetCString(description
, descriptionBuf
, descriptionSize
, kCFStringEncodingUTF8
)) {
227 descriptionBuf
[0] = 0;
230 syslog(LOG_NOTICE
, "SecTaskCopyDebugDescription: %s", descriptionBuf
);
231 CFRelease(description
);
232 free(descriptionBuf
);
234 // EINVAL is what the kernel says for unsigned code, so we'll have to let that pass
235 if (entitlementErrno
== EINVAL
) {
236 task
->entitlementsLoaded
= true;
239 ret
= entitlementErrno
; // what really went wrong
240 goto out
; // bail out
242 bufferlen
= ntohl(header
.length
);
243 /* check for insane values */
244 if (bufferlen
> 1024 * 1024 || bufferlen
< 8) {
248 buffer
= malloc(bufferlen
);
249 if (buffer
== NULL
) {
253 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, buffer
, bufferlen
);
259 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, buffer
+8, bufferlen
-8, kCFAllocatorNull
);
260 entitlements
= (CFMutableDictionaryRef
) CFPropertyListCreateWithData(kCFAllocatorDefault
, data
, kCFPropertyListMutableContainers
, NULL
, error
);
263 if((entitlements
==NULL
) || (CFGetTypeID(entitlements
)!=CFDictionaryGetTypeID())){
269 task
->entitlements
= entitlements
? CFRetain(entitlements
) : NULL
;
270 task
->entitlementsLoaded
= true;
274 CFRelease(entitlements
);
277 if (ret
&& error
&& *error
==NULL
)
278 *error
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
);
283 CFTypeRef
SecTaskCopyValueForEntitlement(SecTaskRef task
, CFStringRef entitlement
, CFErrorRef
*error
)
285 CFTypeRef value
= NULL
;
286 require(check_task(task
), out
);
288 /* Load entitlements if necessary */
289 if (task
->entitlementsLoaded
== false) {
290 require_quiet(SecTaskLoadEntitlements(task
, error
), out
);
293 if (task
->entitlements
!= NULL
) {
294 value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
296 /* Return something the caller must release */
305 CFDictionaryRef
SecTaskCopyValuesForEntitlements(SecTaskRef task
, CFArrayRef entitlements
, CFErrorRef
*error
)
307 CFMutableDictionaryRef values
= NULL
;
308 require(check_task(task
), out
);
310 /* Load entitlements if necessary */
311 if (task
->entitlementsLoaded
== false) {
312 SecTaskLoadEntitlements(task
, error
);
315 /* Iterate over the passed in entitlements, populating the dictionary
316 * If entitlements were loaded but none were present, return an empty
318 if (task
->entitlementsLoaded
== true) {
320 CFIndex i
, count
= CFArrayGetCount(entitlements
);
321 values
= CFDictionaryCreateMutable(CFGetAllocator(task
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
322 if (task
->entitlements
!= NULL
) {
323 for (i
= 0; i
< count
; i
++) {
324 CFStringRef entitlement
= CFArrayGetValueAtIndex(entitlements
, i
);
325 CFTypeRef value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
327 CFDictionarySetValue(values
, entitlement
, value
);