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)