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> 
  29 #include <AssertMacros.h> 
  30 #include <CoreFoundation/CFRuntime.h> 
  31 #include <IOKit/IOKitLib.h> 
  32 #include <IOKit/IOCFUnserialize.h> 
  33 #include <System/sys/codesign.h> 
  34 #include <bsm/libbsm.h> 
  37 #include <utilities/SecCFWrappers.h> 
  39 #include <sys/sysctl.h> 
  42 /* These won't exist until we unify codesigning */ 
  44 #include "SecCodePriv.h" 
  45 #include "SecRequirement.h" 
  46 #endif /* TARGET_OS_OSX */ 
  53     /* Track whether we've loaded entitlements independently since after the 
  54      * load, entitlements may legitimately be NULL */ 
  55     Boolean entitlementsLoaded
; 
  56     CFDictionaryRef entitlements
; 
  58         /* for debugging only, shown by debugDescription */ 
  62 static bool check_task(SecTaskRef task
) { 
  63     return SecTaskGetTypeID() == CFGetTypeID(task
); 
  66 static void SecTaskFinalize(CFTypeRef cfTask
) 
  68     SecTaskRef task 
= (SecTaskRef
) cfTask
; 
  69     CFReleaseNull(task
->entitlements
); 
  73 // Define PRIdPID (proper printf format string for pid_t) 
  74 #define PRIdPID PRId32 
  76 static CFStringRef 
SecTaskCopyDebugDescription(CFTypeRef cfTask
) 
  78     SecTaskRef task 
= (SecTaskRef
) cfTask
; 
  80     audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
); 
  82     const char *task_name
; 
  83     int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
}; 
  85     size_t len 
= sizeof(kp
); 
  86     if (sysctl(mib
, 4, &kp
, &len
, NULL
, 0) == -1 || len 
== 0) 
  87         task_name 
= strerror(errno
); 
  89         task_name 
= kp
.kp_proc
.p_comm
; 
  91     return CFStringCreateWithFormat(CFGetAllocator(task
), NULL
, CFSTR("%s[%" PRIdPID 
"]/%d#%d LF=%d"), task_name
, pid
, 
  92                                                                         task
->entitlementsLoaded
, task
->entitlements 
? (int)CFDictionaryGetCount(task
->entitlements
) : -1, task
->lastFailure
); 
  95 CFGiblisWithFunctions(SecTask
, NULL
, NULL
, SecTaskFinalize
, NULL
, NULL
, NULL
, SecTaskCopyDebugDescription
, NULL
, NULL
, NULL
) 
  97 static SecTaskRef 
init_task_ref(CFAllocatorRef allocator
) 
  99     CFIndex extra 
= sizeof(struct __SecTask
) - sizeof(CFRuntimeBase
); 
 100     return (SecTaskRef
) _CFRuntimeCreateInstance(allocator
, SecTaskGetTypeID(), extra
, NULL
); 
 103 SecTaskRef 
SecTaskCreateFromSelf(CFAllocatorRef allocator
) 
 105     SecTaskRef task 
= init_task_ref(allocator
); 
 108         kern_return_t kr 
= KERN_FAILURE
; 
 109         mach_msg_type_number_t autoken_cnt 
= TASK_AUDIT_TOKEN_COUNT
; 
 110         kr 
= task_info(mach_task_self(), TASK_AUDIT_TOKEN
, (task_info_t
)&task
->token
, &autoken_cnt
); 
 111         if (kr 
== KERN_SUCCESS
) { 
 112             task
->entitlementsLoaded 
= false; 
 113             task
->entitlements 
= NULL
; 
 122 SecTaskRef 
SecTaskCreateWithAuditToken(CFAllocatorRef allocator
, audit_token_t token
) 
 124     SecTaskRef task 
= init_task_ref(allocator
); 
 127         memcpy(&task
->token
, &token
, sizeof(token
)); 
 128         task
->entitlementsLoaded 
= false; 
 129         task
->entitlements 
= NULL
; 
 141 csops_task(SecTaskRef task
, int ops
, void *blob
, size_t size
) 
 146     audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
); 
 147     rc 
= csops_audittoken(pid
, ops
, blob
, size
, &task
->token
); 
 149         task
->lastFailure 
= (rc 
== -1) ? errno 
: 0; 
 154 SecTaskCopySigningIdentifier(SecTaskRef task
, CFErrorRef 
*error
) 
 156         CFStringRef signingId 
= NULL
; 
 158         struct csheader header
; 
 162         ret 
= csops_task(task
, CS_OPS_IDENTITY
, &header
, sizeof(header
)); 
 163         if (ret 
!= -1 || errno 
!= ERANGE
) 
 166         bufferlen 
= ntohl(header
.length
); 
 167         /* check for insane values */ 
 168         if (bufferlen 
> 1024 * 1024 || bufferlen 
< 8) { 
 172         data 
= malloc(bufferlen 
+ 1); 
 177         ret 
= csops_task(task
, CS_OPS_IDENTITY
, data
, bufferlen
); 
 182         data
[bufferlen
] = '\0'; 
 184         signingId 
= CFStringCreateWithCString(NULL
, data 
+ 8, kCFStringEncodingUTF8
); 
 190                 *error 
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
); 
 196 SecTaskGetCodeSignStatus(SecTaskRef task
) 
 199     if (csops_task(task
, CS_OPS_STATUS
, &flags
, sizeof(flags
)) != 0) 
 204 static bool SecTaskLoadEntitlements(SecTaskRef task
, CFErrorRef 
*error
) 
 206     CFMutableDictionaryRef entitlements 
= NULL
; 
 207     struct csheader header
; 
 208     uint8_t *buffer 
= NULL
; 
 213     ret 
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, &header
, sizeof(header
)); 
 214     /* Any other combination means no entitlements */ 
 216                 if (errno 
!= ERANGE
) { 
 217             int entitlementErrno 
= errno
; 
 219                         uint32_t cs_flags 
= -1; 
 220             if (-1 == csops_task(task
, CS_OPS_STATUS
, &cs_flags
, sizeof(cs_flags
))) { 
 221                 syslog(LOG_NOTICE
, "Failed to get cs_flags, error=%d", errno
); 
 224                         if (cs_flags 
!= 0) {    // was signed 
 227                                 audit_token_to_au32(task
->token
, NULL
, NULL
, NULL
, NULL
, NULL
, &pid
, NULL
, NULL
); 
 228                     syslog(LOG_NOTICE
, "SecTaskLoadEntitlements failed error=%d cs_flags=%x, pid=%d", entitlementErrno
, cs_flags
, pid
); // to ease diagnostics 
 230                                 CFStringRef description 
= SecTaskCopyDebugDescription(task
); 
 231                                 char *descriptionBuf 
= NULL
; 
 232                                 CFIndex descriptionSize 
= CFStringGetLength(description
) * 4; 
 233                                 descriptionBuf 
= (char *)malloc(descriptionSize
); 
 234                                 if (!CFStringGetCString(description
, descriptionBuf
, descriptionSize
, kCFStringEncodingUTF8
)) { 
 235                                         descriptionBuf
[0] = 0; 
 238                                 syslog(LOG_NOTICE
, "SecTaskCopyDebugDescription: %s", descriptionBuf
); 
 239                                 CFReleaseNull(description
); 
 240                                 free(descriptionBuf
); 
 242                         task
->lastFailure 
= entitlementErrno
;   // was overwritten by csops_task(CS_OPS_STATUS) above 
 244                         // EINVAL is what the kernel says for unsigned code, so we'll have to let that pass 
 245                         if (entitlementErrno 
== EINVAL
) { 
 246                                 task
->entitlementsLoaded 
= true; 
 249                         ret 
= entitlementErrno
; // what really went wrong 
 250                         goto out
;               // bail out 
 252         bufferlen 
= ntohl(header
.length
); 
 253         /* check for insane values */ 
 254         if (bufferlen 
> 1024 * 1024 || bufferlen 
< 8) { 
 258         buffer 
= malloc(bufferlen
); 
 259         if (buffer 
== NULL
) { 
 263         ret 
= csops_task(task
, CS_OPS_ENTITLEMENTS_BLOB
, buffer
, bufferlen
); 
 269         CFDataRef data 
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, buffer
+8, bufferlen
-8, kCFAllocatorNull
); 
 270         entitlements 
= (CFMutableDictionaryRef
) CFPropertyListCreateWithData(kCFAllocatorDefault
, data
, kCFPropertyListMutableContainers
, NULL
, error
); 
 273         if((entitlements
==NULL
) || (CFGetTypeID(entitlements
)!=CFDictionaryGetTypeID())){ 
 274             ret 
= EDOM
; // don't use EINVAL here; it conflates problems with syscall error returns 
 279     task
->entitlements 
= entitlements 
? CFRetain(entitlements
) : NULL
; 
 280     task
->entitlementsLoaded 
= true; 
 283     CFReleaseNull(entitlements
); 
 286     if (ret 
&& error 
&& *error
==NULL
) 
 287         *error 
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, ret
, NULL
); 
 292 CFTypeRef 
SecTaskCopyValueForEntitlement(SecTaskRef task
, CFStringRef entitlement
, CFErrorRef 
*error
) 
 294     CFTypeRef value 
= NULL
; 
 295     require(check_task(task
), out
); 
 297     /* Load entitlements if necessary */ 
 298     if (task
->entitlementsLoaded 
== false) { 
 299         require_quiet(SecTaskLoadEntitlements(task
, error
), out
); 
 302     if (task
->entitlements 
!= NULL
) { 
 303         value 
= CFDictionaryGetValue(task
->entitlements
, entitlement
); 
 305         /* Return something the caller must release */ 
 314 CFDictionaryRef 
SecTaskCopyValuesForEntitlements(SecTaskRef task
, CFArrayRef entitlements
, CFErrorRef 
*error
) 
 316     CFMutableDictionaryRef values 
= NULL
; 
 317     require(check_task(task
), out
); 
 319     /* Load entitlements if necessary */ 
 320     if (task
->entitlementsLoaded 
== false) { 
 321         SecTaskLoadEntitlements(task
, error
); 
 324     /* Iterate over the passed in entitlements, populating the dictionary 
 325      * If entitlements were loaded but none were present, return an empty 
 327     if (task
->entitlementsLoaded 
== true) { 
 329         CFIndex i
, count 
= CFArrayGetCount(entitlements
); 
 330         values 
= CFDictionaryCreateMutable(CFGetAllocator(task
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
 331         if (task
->entitlements 
!= NULL
) { 
 332             for (i 
= 0; i 
< count
; i
++) { 
 333                 CFStringRef entitlement 
= CFArrayGetValueAtIndex(entitlements
, i
); 
 334                 CFTypeRef value 
= CFDictionaryGetValue(task
->entitlements
, entitlement
); 
 336                     CFDictionarySetValue(values
, entitlement
, value
); 
 347  * Determine if the given task meets a specified requirement. 
 350 SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
) 
 353     SecCodeRef code 
= NULL
; 
 354     SecRequirementRef req 
= NULL
; 
 356     CFMutableDictionaryRef codeDict 
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
 357     CFDataRef auditData 
= CFDataCreate(kCFAllocatorDefault
, (const UInt8 
*)&task
->token
, sizeof(audit_token_t
)); 
 358     CFDictionarySetValue(codeDict
, kSecGuestAttributeAudit
, auditData
); 
 359     status 
= SecCodeCopyGuestWithAttributes(NULL
, codeDict
, kSecCSDefaultFlags
, &code
); 
 360     CFReleaseNull(codeDict
); 
 361     CFReleaseNull(auditData
); 
 364         status 
= SecRequirementCreateWithString(requirement
, 
 365                                                 kSecCSDefaultFlags
, &req
); 
 368         status 
= SecCodeCheckValidity(code
, kSecCSDefaultFlags
, req
); 
 376 #endif /* TARGET_OS_OSX */ 
 378 Boolean 
SecTaskEntitlementsValidated(SecTaskRef task
) { 
 379     // TODO: Cache the result 
 380     uint32_t csflags 
= 0; 
 381     const uint32_t mask 
= CS_VALID 
| CS_KILL 
| CS_ENTITLEMENTS_VALIDATED
; 
 382     int rc 
= csops_task(task
, CS_OPS_STATUS
, &csflags
, sizeof(csflags
)); 
 383     return rc 
!= -1 && ((csflags 
& mask
) == mask
);