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>
32 #include <sys/sysctl.h>
35 #include "SecCodePriv.h"
36 #include "SecRequirement.h"
39 #include "SecTaskPriv.h"
48 audit_token_t token_storage
;
50 /* Track whether we've loaded entitlements independently since after the
51 * load, entitlements may legitimately be NULL */
52 Boolean entitlementsLoaded
;
53 CFDictionaryRef entitlements
;
57 kSecCodeMagicEntitlement
= 0xfade7171, /* entitlement blob */
61 CFTypeID _kSecTaskTypeID
= _kCFRuntimeNotATypeID
;
63 static void SecTaskFinalize(CFTypeRef cfTask
)
65 SecTaskRef task
= (SecTaskRef
) cfTask
;
67 if (task
->entitlements
!= NULL
) {
68 CFRelease(task
->entitlements
);
69 task
->entitlements
= NULL
;
74 // Define PRIdPID (proper printf format string for pid_t)
75 #define PRIdPID PRId32
77 static CFStringRef
SecTaskCopyDebugDescription(CFTypeRef cfTask
)
79 SecTaskRef task
= (SecTaskRef
) cfTask
;
80 const char *task_name
;
81 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, task
->pid
};
83 size_t len
= sizeof(kp
);
84 if (sysctl(mib
, 4, &kp
, &len
, NULL
, 0) == -1 || len
== 0)
85 task_name
= strerror(errno
);
87 task_name
= kp
.kp_proc
.p_comm
;
89 return CFStringCreateWithFormat(CFGetAllocator(task
), NULL
, CFSTR("%s[%" PRIdPID
"]"), task_name
, task
->pid
);
92 static void SecTaskRegisterClass(void)
94 static const CFRuntimeClass SecTaskClass
= {
96 .className
= "SecTask",
99 .finalize
= SecTaskFinalize
,
102 .copyFormattingDesc
= NULL
,
103 .copyDebugDesc
= SecTaskCopyDebugDescription
,
106 _kSecTaskTypeID
= _CFRuntimeRegisterClass(&SecTaskClass
);
109 CFTypeID
SecTaskGetTypeID(void)
111 static pthread_once_t secTaskRegisterClassOnce
= PTHREAD_ONCE_INIT
;
113 /* Register the class with the CF runtime the first time through */
114 pthread_once(&secTaskRegisterClassOnce
, SecTaskRegisterClass
);
116 return _kSecTaskTypeID
;
119 static SecTaskRef
SecTaskCreateWithPID(CFAllocatorRef allocator
, pid_t pid
)
121 CFIndex extra
= sizeof(struct __SecTask
) - sizeof(CFRuntimeBase
);
122 SecTaskRef task
= (SecTaskRef
) _CFRuntimeCreateInstance(allocator
, SecTaskGetTypeID(), extra
, NULL
);
125 task
->entitlementsLoaded
= false;
126 task
->entitlements
= NULL
;
132 SecTaskRef
SecTaskCreateWithAuditToken(CFAllocatorRef allocator
, audit_token_t token
)
136 task
= SecTaskCreateWithPID(allocator
, audit_token_to_pid(token
));
139 task
->token_storage
= token
;
140 task
->token
= &task
->token_storage
;
147 SecTaskRef
SecTaskCreateFromSelf(CFAllocatorRef allocator
)
149 return SecTaskCreateWithPID(allocator
, getpid());
153 * Determine if the given task meets a specified requirement.
156 SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
)
159 SecCodeRef code
= NULL
;
160 SecRequirementRef req
= NULL
;
161 pid_t pid
= task
->pid
;
165 status
= SecCodeCreateWithPID(pid
, kSecCSDefaultFlags
, &code
);
166 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCreateWithPID=%d", status);
168 status
= SecRequirementCreateWithString(requirement
,
169 kSecCSDefaultFlags
, &req
);
170 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecRequirementCreateWithString=%d", status);
173 status
= SecCodeCheckValidity(code
, kSecCSDefaultFlags
, req
);
174 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCheckValidity=%d", status);
184 static CFRange
myMakeRange(CFIndex loc
, CFIndex len
) {
185 CFRange r
= {.location
= loc
, .length
= len
};
194 csops_task(SecTaskRef task
, int ops
, void *blob
, size_t size
)
197 return csops_audittoken(task
->pid
, ops
, blob
, size
, task
->token
);
199 return csops(task
->pid
, ops
, blob
, size
);
202 static int SecTaskLoadEntitlements(SecTaskRef task
, CFErrorRef
*error
)
204 CFMutableDataRef data
= NULL
;
205 struct csheader header
;
209 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, &header
, sizeof(header
));
210 if (ret
!= -1 || errno
!= ERANGE
) {
211 /* no entitlements */
212 task
->entitlementsLoaded
= true;
216 bufferlen
= ntohl(header
.length
);
217 /* check for insane values */
218 if (bufferlen
> 1024 * 1024 || bufferlen
< 8) {
222 data
= CFDataCreateMutable(NULL
, bufferlen
);
227 CFDataSetLength(data
, bufferlen
);
228 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, CFDataGetMutableBytePtr(data
), bufferlen
);
233 CFDataDeleteBytes(data
, myMakeRange(0, 8));
234 task
->entitlements
= CFPropertyListCreateWithData(NULL
, data
, 0, NULL
, error
);
235 task
->entitlementsLoaded
= true;
240 *error
= CFErrorCreate(NULL
, kCFErrorDomainMach
, ret
, NULL
);
245 CFTypeRef
SecTaskCopyValueForEntitlement(SecTaskRef task
, CFStringRef entitlement
, CFErrorRef
*error
)
247 /* Load entitlements if necessary */
248 if (task
->entitlementsLoaded
== false) {
249 SecTaskLoadEntitlements(task
, error
);
252 CFTypeRef value
= NULL
;
253 if (task
->entitlements
!= NULL
) {
254 value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
256 /* Return something the caller must release */
265 CFDictionaryRef
SecTaskCopyValuesForEntitlements(SecTaskRef task
, CFArrayRef entitlements
, CFErrorRef
*error
)
267 /* Load entitlements if necessary */
268 if (task
->entitlementsLoaded
== false) {
269 SecTaskLoadEntitlements(task
, error
);
272 /* Iterate over the passed in entitlements, populating the dictionary
273 * If entitlements were loaded but none were present, return an empty
275 CFMutableDictionaryRef values
= NULL
;
276 if (task
->entitlementsLoaded
== true) {
278 CFIndex i
, count
= CFArrayGetCount(entitlements
);
279 values
= CFDictionaryCreateMutable(CFGetAllocator(task
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
280 if (task
->entitlements
!= NULL
) {
281 for (i
= 0; i
< count
; i
++) {
282 CFStringRef entitlement
= CFArrayGetValueAtIndex(entitlements
, i
);
283 CFTypeRef value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
285 CFDictionarySetValue(values
, entitlement
, value
);
294 Boolean
SecTaskEntitlementsValidated(SecTaskRef task
) {
295 // TODO: Cache the result
296 uint32_t csflags
= 0;
297 const uint32_t mask
= CS_VALID
| CS_KILL
| CS_ENTITLEMENTS_VALIDATED
;
298 int rc
= csops_task(task
, CS_OPS_STATUS
, &csflags
, sizeof(csflags
));
299 return rc
!= -1 && ((csflags
& mask
) == mask
);