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>
34 #include <utilities/SecCFWrappers.h>
38 #include "SecCodePriv.h"
39 #include "SecRequirement.h"
42 #include "SecTaskPriv.h"
52 /* Track whether we've loaded entitlements independently since after the
53 * load, entitlements may legitimately be NULL */
54 Boolean entitlementsLoaded
;
55 CFDictionaryRef entitlements
;
57 /* for debugging only, shown by debugDescription */
62 kSecCodeMagicEntitlement
= 0xfade7171, /* entitlement blob */
66 CFTypeID _kSecTaskTypeID
= _kCFRuntimeNotATypeID
;
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
;
83 if (task
->pid_self
==-1) {
84 audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
);
89 const char *task_name
;
90 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
};
92 size_t len
= sizeof(kp
);
93 if (sysctl(mib
, 4, &kp
, &len
, NULL
, 0) == -1 || len
== 0)
94 task_name
= strerror(errno
);
96 task_name
= kp
.kp_proc
.p_comm
;
98 return CFStringCreateWithFormat(CFGetAllocator(task
), NULL
, CFSTR("%s[%" PRIdPID
"]"), task_name
, pid
);
101 static void SecTaskRegisterClass(void)
103 static const CFRuntimeClass SecTaskClass
= {
105 .className
= "SecTask",
108 .finalize
= SecTaskFinalize
,
111 .copyFormattingDesc
= NULL
,
112 .copyDebugDesc
= SecTaskCopyDebugDescription
,
115 _kSecTaskTypeID
= _CFRuntimeRegisterClass(&SecTaskClass
);
118 CFTypeID
SecTaskGetTypeID(void)
120 static pthread_once_t secTaskRegisterClassOnce
= PTHREAD_ONCE_INIT
;
122 /* Register the class with the CF runtime the first time through */
123 pthread_once(&secTaskRegisterClassOnce
, SecTaskRegisterClass
);
125 return _kSecTaskTypeID
;
128 static SecTaskRef
init_task_ref(CFAllocatorRef allocator
)
130 CFIndex extra
= sizeof(struct __SecTask
) - sizeof(CFRuntimeBase
);
131 return (SecTaskRef
) _CFRuntimeCreateInstance(allocator
, SecTaskGetTypeID(), extra
, NULL
);
134 SecTaskRef
SecTaskCreateWithAuditToken(CFAllocatorRef allocator
, audit_token_t token
)
136 SecTaskRef task
= init_task_ref(allocator
);
139 memcpy(&task
->token
, &token
, sizeof(token
));
140 task
->entitlementsLoaded
= false;
141 task
->entitlements
= NULL
;
148 SecTaskRef
SecTaskCreateFromSelf(CFAllocatorRef allocator
)
150 SecTaskRef task
= init_task_ref(allocator
);
153 memset(&task
->token
, 0, sizeof(task
->token
));
154 task
->entitlementsLoaded
= false;
155 task
->entitlements
= NULL
;
156 task
->pid_self
= getpid();
163 * Determine if the given task meets a specified requirement.
166 SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
)
169 SecCodeRef code
= NULL
;
170 SecRequirementRef req
= NULL
;
171 CFDataRef auditData
= NULL
;
172 CFNumberRef pidRef
= NULL
;
174 CFMutableDictionaryRef codeDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
175 if(task
->pid_self
==-1) {
176 auditData
= CFDataCreate(kCFAllocatorDefault
, (const UInt8
*)&task
->token
, sizeof(audit_token_t
));
177 CFDictionarySetValue(codeDict
, kSecGuestAttributeAudit
, auditData
);
179 pidRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &task
->pid_self
);
180 CFDictionarySetValue(codeDict
, kSecGuestAttributePid
, pidRef
);
183 status
= SecCodeCopyGuestWithAttributes(NULL
, codeDict
, kSecCSDefaultFlags
, &code
);
184 CFReleaseNull(codeDict
);
185 CFReleaseNull(auditData
);
186 CFReleaseNull(pidRef
);
189 status
= SecRequirementCreateWithString(requirement
,
190 kSecCSDefaultFlags
, &req
);
193 status
= SecCodeCheckValidity(code
, kSecCSDefaultFlags
, req
);
202 static CFRange
myMakeRange(CFIndex loc
, CFIndex len
) {
203 CFRange r
= {.location
= loc
, .length
= len
};
212 csops_task(SecTaskRef task
, int ops
, void *blob
, size_t size
)
215 if (task
->pid_self
==-1) {
217 audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
);
218 rc
= csops_audittoken(pid
, ops
, blob
, size
, &task
->token
);
221 rc
= csops(task
->pid_self
, ops
, blob
, size
);
222 task
->lastFailure
= (rc
== -1) ? errno
: 0;
226 static int SecTaskLoadEntitlements(SecTaskRef task
, CFErrorRef
*error
)
228 CFMutableDataRef data
= NULL
;
229 struct csheader header
;
233 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, &header
, sizeof(header
));
235 // we only gave a header's worth of buffer. If this succeeded, we have no entitlements
236 task
->entitlementsLoaded
= true;
239 if (errno
!= ERANGE
) {
240 // ERANGE means "your buffer is too small, it now tells you how much you need
241 // EINVAL is what the kernel says for unsigned code AND broken code, so we'll have to let that pass
242 if (errno
== EINVAL
) {
243 task
->entitlementsLoaded
= true;
249 // kernel told us the needed buffer size in header.length; proceed
251 bufferlen
= ntohl(header
.length
);
252 /* check for insane values */
253 if (bufferlen
> 1024 * 1024 || bufferlen
< 8) {
257 data
= CFDataCreateMutable(NULL
, bufferlen
);
262 CFDataSetLength(data
, bufferlen
);
263 ret
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, CFDataGetMutableBytePtr(data
), bufferlen
);
268 CFDataDeleteBytes(data
, myMakeRange(0, 8));
269 task
->entitlements
= CFPropertyListCreateWithData(NULL
, data
, 0, NULL
, error
);
270 task
->entitlementsLoaded
= true;
275 *error
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
);
280 CFTypeRef
SecTaskCopyValueForEntitlement(SecTaskRef task
, CFStringRef entitlement
, CFErrorRef
*error
)
282 /* Load entitlements if necessary */
283 if (task
->entitlementsLoaded
== false) {
284 SecTaskLoadEntitlements(task
, error
);
287 CFTypeRef value
= NULL
;
288 if (task
->entitlements
!= NULL
) {
289 value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
291 /* Return something the caller must release */
300 CFDictionaryRef
SecTaskCopyValuesForEntitlements(SecTaskRef task
, CFArrayRef entitlements
, CFErrorRef
*error
)
302 /* Load entitlements if necessary */
303 if (task
->entitlementsLoaded
== false) {
304 SecTaskLoadEntitlements(task
, error
);
307 /* Iterate over the passed in entitlements, populating the dictionary
308 * If entitlements were loaded but none were present, return an empty
310 CFMutableDictionaryRef values
= NULL
;
311 if (task
->entitlementsLoaded
== true) {
313 CFIndex i
, count
= CFArrayGetCount(entitlements
);
314 values
= CFDictionaryCreateMutable(CFGetAllocator(task
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
315 if (task
->entitlements
!= NULL
) {
316 for (i
= 0; i
< count
; i
++) {
317 CFStringRef entitlement
= CFArrayGetValueAtIndex(entitlements
, i
);
318 CFTypeRef value
= CFDictionaryGetValue(task
->entitlements
, entitlement
);
320 CFDictionarySetValue(values
, entitlement
, value
);
329 Boolean
SecTaskEntitlementsValidated(SecTaskRef task
) {
330 // TODO: Cache the result
331 uint32_t csflags
= 0;
332 const uint32_t mask
= CS_VALID
| CS_KILL
| CS_ENTITLEMENTS_VALIDATED
;
333 int rc
= csops_task(task
, CS_OPS_STATUS
, &csflags
, sizeof(csflags
));
334 return rc
!= -1 && ((csflags
& mask
) == mask
);
338 SecTaskCopySigningIdentifier(SecTaskRef task
, CFErrorRef
*error
)
340 CFStringRef signingId
= NULL
;
342 struct csheader header
;
346 ret
= csops_task(task
, CS_OPS_IDENTITY
, &header
, sizeof(header
));
347 if (ret
!= -1 || errno
!= ERANGE
)
350 bufferlen
= ntohl(header
.length
);
351 /* check for insane values */
352 if (bufferlen
> 1024 * 1024 || bufferlen
< 8) {
356 data
= malloc(bufferlen
+ 1);
361 ret
= csops_task(task
, CS_OPS_IDENTITY
, data
, bufferlen
);
366 data
[bufferlen
] = '\0';
368 signingId
= CFStringCreateWithCString(NULL
, data
+ 8, kCFStringEncodingUTF8
);
374 *error
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
);
380 SecTaskGetCodeSignStatus(SecTaskRef task
)
383 if (csops_task(task
, CS_OPS_STATUS
, &flags
, sizeof(flags
)) != 0)