2 * Copyright (c) 2009-2014 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 <CoreFoundation/CoreFoundation.h>
25 #include <CoreFoundation/CFRuntime.h>
26 #include <IOKit/IOKitLib.h>
27 #include <IOKit/IOCFUnserialize.h>
28 #include <System/sys/codesign.h>
29 #include <bsm/libbsm.h>
33 #include <sys/sysctl.h>
36 #include "SecCodePriv.h"
37 #include "SecRequirement.h"
40 #include "SecTaskPriv.h"
49 audit_token_t token_storage
;
51 /* Track whether we've loaded entitlements independently since after the
52 * load, entitlements may legitimately be NULL */
53 Boolean entitlementsLoaded
;
54 CFDictionaryRef entitlements
;
58 kSecCodeMagicEntitlement
= 0xfade7171, /* entitlement blob */
62 CFTypeID _kSecTaskTypeID
= _kCFRuntimeNotATypeID
;
64 static void SecTaskFinalize(CFTypeRef cfTask
)
66 SecTaskRef task
= (SecTaskRef
) cfTask
;
68 if (task
->entitlements
!= NULL
) {
69 CFRelease(task
->entitlements
);
70 task
->entitlements
= NULL
;
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
;
81 const char *task_name
;
82 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, task
->pid
};
84 size_t len
= sizeof(kp
);
85 if (sysctl(mib
, 4, &kp
, &len
, NULL
, 0) == -1 || len
== 0)
86 task_name
= strerror(errno
);
88 task_name
= kp
.kp_proc
.p_comm
;
90 return CFStringCreateWithFormat(CFGetAllocator(task
), NULL
, CFSTR("%s[%" PRIdPID
"]"), task_name
, task
->pid
);
93 static void SecTaskRegisterClass(void)
95 static const CFRuntimeClass SecTaskClass
= {
97 .className
= "SecTask",
100 .finalize
= SecTaskFinalize
,
103 .copyFormattingDesc
= NULL
,
104 .copyDebugDesc
= SecTaskCopyDebugDescription
,
107 _kSecTaskTypeID
= _CFRuntimeRegisterClass(&SecTaskClass
);
110 CFTypeID
SecTaskGetTypeID(void)
112 static pthread_once_t secTaskRegisterClassOnce
= PTHREAD_ONCE_INIT
;
114 /* Register the class with the CF runtime the first time through */
115 pthread_once(&secTaskRegisterClassOnce
, SecTaskRegisterClass
);
117 return _kSecTaskTypeID
;
120 static SecTaskRef
SecTaskCreateWithPID(CFAllocatorRef allocator
, pid_t pid
)
122 CFIndex extra
= sizeof(struct __SecTask
) - sizeof(CFRuntimeBase
);
123 SecTaskRef task
= (SecTaskRef
) _CFRuntimeCreateInstance(allocator
, SecTaskGetTypeID(), extra
, NULL
);
126 task
->entitlementsLoaded
= false;
127 task
->entitlements
= NULL
;
133 SecTaskRef
SecTaskCreateWithAuditToken(CFAllocatorRef allocator
, audit_token_t token
)
137 task
= SecTaskCreateWithPID(allocator
, audit_token_to_pid(token
));
140 task
->token_storage
= token
;
141 task
->token
= &task
->token_storage
;
148 SecTaskRef
SecTaskCreateFromSelf(CFAllocatorRef allocator
)
150 return SecTaskCreateWithPID(allocator
, getpid());
154 * Determine if the given task meets a specified requirement.
157 SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
)
160 SecCodeRef code
= NULL
;
161 SecRequirementRef req
= NULL
;
162 pid_t pid
= task
->pid
;
166 status
= SecCodeCreateWithPID(pid
, kSecCSDefaultFlags
, &code
);
167 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCreateWithPID=%d", status);
169 status
= SecRequirementCreateWithString(requirement
,
170 kSecCSDefaultFlags
, &req
);
171 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecRequirementCreateWithString=%d", status);
174 status
= SecCodeCheckValidity(code
, kSecCSDefaultFlags
, req
);
175 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCheckValidity=%d", status);
185 static CFRange
myMakeRange(CFIndex loc
, CFIndex len
) {
186 CFRange r
= {.location
= loc
, .length
= len
};
195 csops_task(SecTaskRef task
, int ops
, void *blob
, size_t size
)
199 return csops_audittoken(task
->pid
, ops
, blob
, size
, task
->token
);
202 return csops(task
->pid
, ops
, blob
, size
);
205 static int SecTaskLoadEntitlements(SecTaskRef task
, CFErrorRef
*error
)
207 CFMutableDataRef data
= NULL
;
208 struct csheader header
;
212 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, &header
, sizeof(header
));
214 // we only gave a header's worth of buffer. If this succeeded, we have no entitlements
215 task
->entitlementsLoaded
= true;
218 if (errno
!= ERANGE
) {
219 // ERANGE means "your buffer is too small, it now tells you how much you need
220 // EINVAL is what the kernel says for unsigned code AND broken code, so we'll have to let that pass
221 if (errno
== EINVAL
) {
222 task
->entitlementsLoaded
= true;
228 // kernel told us the needed buffer size in header.length; proceed
230 bufferlen
= ntohl(header
.length
);
231 /* check for insane values */
232 if (bufferlen
> 1024 * 1024 || bufferlen
< 8) {
236 data
= CFDataCreateMutable(NULL
, bufferlen
);
241 CFDataSetLength(data
, bufferlen
);
242 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, CFDataGetMutableBytePtr(data
), bufferlen
);
247 CFDataDeleteBytes(data
, myMakeRange(0, 8));
248 task
->entitlements
= CFPropertyListCreateWithData(NULL
, data
, 0, NULL
, error
);
249 task
->entitlementsLoaded
= true;
254 *error
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
);
259 CFTypeRef
SecTaskCopyValueForEntitlement(SecTaskRef task
, CFStringRef entitlement
, CFErrorRef
*error
)
261 /* Load entitlements if necessary */
262 if (task
->entitlementsLoaded
== false) {
263 SecTaskLoadEntitlements(task
, error
);
266 CFTypeRef value
= NULL
;
267 if (task
->entitlements
!= NULL
) {
268 value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
270 /* Return something the caller must release */
279 CFDictionaryRef
SecTaskCopyValuesForEntitlements(SecTaskRef task
, CFArrayRef entitlements
, CFErrorRef
*error
)
281 /* Load entitlements if necessary */
282 if (task
->entitlementsLoaded
== false) {
283 SecTaskLoadEntitlements(task
, error
);
286 /* Iterate over the passed in entitlements, populating the dictionary
287 * If entitlements were loaded but none were present, return an empty
289 CFMutableDictionaryRef values
= NULL
;
290 if (task
->entitlementsLoaded
== true) {
292 CFIndex i
, count
= CFArrayGetCount(entitlements
);
293 values
= CFDictionaryCreateMutable(CFGetAllocator(task
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
294 if (task
->entitlements
!= NULL
) {
295 for (i
= 0; i
< count
; i
++) {
296 CFStringRef entitlement
= CFArrayGetValueAtIndex(entitlements
, i
);
297 CFTypeRef value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
299 CFDictionarySetValue(values
, entitlement
, value
);
308 Boolean
SecTaskEntitlementsValidated(SecTaskRef task
) {
309 // TODO: Cache the result
310 uint32_t csflags
= 0;
311 const uint32_t mask
= CS_VALID
| CS_KILL
| CS_ENTITLEMENTS_VALIDATED
;
312 int rc
= csops_task(task
, CS_OPS_STATUS
, &csflags
, sizeof(csflags
));
313 return rc
!= -1 && ((csflags
& mask
) == mask
);
317 SecTaskCopySigningIdentifier(SecTaskRef task
, CFErrorRef
*error
)
319 CFStringRef signingId
= NULL
;
321 struct csheader header
;
325 ret
= csops_task(task
, CS_OPS_IDENTITY
, &header
, sizeof(header
));
326 if (ret
!= -1 || errno
!= ERANGE
)
329 bufferlen
= ntohl(header
.length
);
330 /* check for insane values */
331 if (bufferlen
> 1024 * 1024 || bufferlen
< 8) {
335 data
= malloc(bufferlen
+ 1);
340 ret
= csops_task(task
, CS_OPS_IDENTITY
, data
, bufferlen
);
345 data
[bufferlen
] = '\0';
347 signingId
= CFStringCreateWithCString(NULL
, data
+ 8, kCFStringEncodingUTF8
);
353 *error
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
);
359 SecTaskGetCodeSignStatus(SecTaskRef task
)
362 if (csops_task(task
, CS_OPS_STATUS
, &flags
, sizeof(flags
)) != 0)