1/* 2 * Copyright (c) 2008,2010-2013 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "SecTask.h" 25 26#include <utilities/debugging.h> 27 28#include <AssertMacros.h> 29#include <CoreFoundation/CFRuntime.h> 30#include <IOKit/IOKitLib.h> 31#include <IOKit/IOCFUnserialize.h> 32#include <System/sys/codesign.h> 33#include <bsm/libbsm.h> 34#include <inttypes.h> 35#include <utilities/SecCFWrappers.h> 36 37#define USE_LIBPROC 0 38#if USE_LIBPROC 39#include <libproc.h> 40#else 41#include <sys/sysctl.h> 42#endif 43 44struct __SecTask { 45 CFRuntimeBase base; 46 47 pid_t pid_self; 48 audit_token_t token; 49 50 /* Track whether we've loaded entitlements independently since after the 51 * load, entitlements may legitimately be NULL */ 52 Boolean entitlementsLoaded; 53 CFDictionaryRef entitlements; 54}; 55 56static bool check_task(SecTaskRef task) { 57 return SecTaskGetTypeID() == CFGetTypeID(task); 58} 59 60static void SecTaskFinalize(CFTypeRef cfTask) 61{ 62 SecTaskRef task = (SecTaskRef) cfTask; 63 64 if (task->entitlements != NULL) { 65 CFRelease(task->entitlements); 66 task->entitlements = NULL; 67 } 68} 69 70 71// Define PRIdPID (proper printf format string for pid_t) 72#define PRIdPID PRId32 73 74static CFStringRef SecTaskCopyDebugDescription(CFTypeRef cfTask) 75{ 76 SecTaskRef task = (SecTaskRef) cfTask; 77 pid_t pid; 78 if (task->pid_self==-1) { 79 audit_token_to_au32(task->token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL); 80 } else { 81 pid = task->pid_self; 82 } 83 84#if USE_LIBPROC 85#define MAX_PROCNAME 32 86 char task_name[MAX_PROCNAME + 1] = {}; 87 proc_name(pid, task_name, MAX_PROCNAME); 88#else 89 const char *task_name; 90 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 91 struct kinfo_proc kp; 92 size_t len = sizeof(kp); 93 if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1 || len == 0) 94 task_name = strerror(errno); 95 else 96 task_name = kp.kp_proc.p_comm; 97#endif 98 99 return CFStringCreateWithFormat(CFGetAllocator(task), NULL, CFSTR("%s[%" PRIdPID "]"), task_name, pid); 100} 101 102CFGiblisWithFunctions(SecTask, NULL, NULL, SecTaskFinalize, NULL, NULL, NULL, SecTaskCopyDebugDescription, NULL, NULL, NULL) 103 104static SecTaskRef init_task_ref(CFAllocatorRef allocator) 105{ 106 CFIndex extra = sizeof(struct __SecTask) - sizeof(CFRuntimeBase); 107 return (SecTaskRef) _CFRuntimeCreateInstance(allocator, SecTaskGetTypeID(), extra, NULL); 108} 109 110SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator) 111{ 112 SecTaskRef task = init_task_ref(allocator); 113 if (task != NULL) { 114 115 memset(&task->token, 0, sizeof(task->token)); 116 task->entitlementsLoaded = false; 117 task->entitlements = NULL; 118 task->pid_self = getpid(); 119 } 120 121 return task; 122} 123 124SecTaskRef SecTaskCreateWithAuditToken(CFAllocatorRef allocator, audit_token_t token) 125{ 126 SecTaskRef task = init_task_ref(allocator); 127 if (task != NULL) { 128 129 memcpy(&task->token, &token, sizeof(token)); 130 task->entitlementsLoaded = false; 131 task->entitlements = NULL; 132 task->pid_self = -1; 133 } 134 135 return task; 136} 137 138struct csheader { 139 uint32_t magic; 140 uint32_t length; 141}; 142 143static int 144csops_task(SecTaskRef task, int ops, void *blob, size_t size) 145{ 146 if (task->pid_self==-1) { 147 pid_t pid; 148 audit_token_to_au32(task->token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL); 149 return csops_audittoken(pid, ops, blob, size, &task->token); 150 } 151 else 152 return csops(task->pid_self, ops, blob, size); 153} 154 155/* This may need to be exported at some point */ 156CFStringRef 157SecTaskCopySigningIdentifier(SecTaskRef task, CFErrorRef *error) 158{ 159 CFStringRef signingId = NULL; 160 char *data = NULL; 161 struct csheader header; 162 uint32_t bufferlen; 163 int ret; 164 165 ret = csops_task(task, CS_OPS_IDENTITY, &header, sizeof(header)); 166 if (ret != -1 || errno != ERANGE) 167 return NULL; 168 169 bufferlen = ntohl(header.length); 170 /* check for insane values */ 171 if (bufferlen > 1024 * 1024 || bufferlen < 8) { 172 ret = EINVAL; 173 goto out; 174 } 175 data = malloc(bufferlen + 1); 176 if (data == NULL) { 177 ret = ENOMEM; 178 goto out; 179 } 180 ret = csops_task(task, CS_OPS_IDENTITY, data, bufferlen); 181 if (ret) { 182 ret = errno; 183 goto out; 184 } 185 data[bufferlen] = '\0'; 186 187 signingId = CFStringCreateWithCString(NULL, data + 8, kCFStringEncodingUTF8); 188 189 out: 190 if (data) 191 free(data); 192 if (ret && error) 193 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ret, NULL); 194 195 return signingId; 196} 197 198 199static bool SecTaskLoadEntitlements(SecTaskRef task, CFErrorRef *error) 200{ 201 CFMutableDictionaryRef entitlements = NULL; 202 struct csheader header; 203 uint8_t *buffer = NULL; 204 uint32_t bufferlen; 205 int ret; 206 207 208 ret = csops_task(task, CS_OPS_ENTITLEMENTS_BLOB, &header, sizeof(header)); 209 /* Any other combination means no entitlements */ 210 if (ret == -1 && errno == ERANGE) { 211 bufferlen = ntohl(header.length); 212 /* check for insane values */ 213 if (bufferlen > 1024 * 1024 || bufferlen < 8) { 214 ret = EINVAL; 215 goto out; 216 } 217 buffer = malloc(bufferlen); 218 if (buffer == NULL) { 219 ret = ENOMEM; 220 goto out; 221 } 222 ret = csops_task(task, CS_OPS_ENTITLEMENTS_BLOB, buffer, bufferlen); 223 if (ret) { 224 ret = errno; 225 goto out; 226 } 227 228 CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer+8, bufferlen-8, kCFAllocatorNull); 229 entitlements = (CFMutableDictionaryRef) CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListMutableContainers, NULL, error); 230 CFRelease(data); 231 232 if((entitlements==NULL) || (CFGetTypeID(entitlements)!=CFDictionaryGetTypeID())){ 233 ret = EINVAL; 234 goto out; 235 } 236 } 237 238 task->entitlements = entitlements ? CFRetain(entitlements) : NULL; 239 task->entitlementsLoaded = true; 240 241out: 242 if(entitlements) 243 CFRelease(entitlements); 244 if(buffer) 245 free(buffer); 246 if (ret && error && *error==NULL) 247 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ret, NULL); 248 return ret == 0; 249} 250 251 252CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error) 253{ 254 CFTypeRef value = NULL; 255 require(check_task(task), out); 256 257 /* Load entitlements if necessary */ 258 if (task->entitlementsLoaded == false) { 259 require_quiet(SecTaskLoadEntitlements(task, error), out); 260 } 261 262 if (task->entitlements != NULL) { 263 value = CFDictionaryGetValue(task->entitlements, entitlement); 264 265 /* Return something the caller must release */ 266 if (value != NULL) { 267 CFRetain(value); 268 } 269 } 270out: 271 return value; 272} 273 274CFDictionaryRef SecTaskCopyValuesForEntitlements(SecTaskRef task, CFArrayRef entitlements, CFErrorRef *error) 275{ 276 CFMutableDictionaryRef values = NULL; 277 require(check_task(task), out); 278 279 /* Load entitlements if necessary */ 280 if (task->entitlementsLoaded == false) { 281 SecTaskLoadEntitlements(task, error); 282 } 283 284 /* Iterate over the passed in entitlements, populating the dictionary 285 * If entitlements were loaded but none were present, return an empty 286 * dictionary */ 287 if (task->entitlementsLoaded == true) { 288 289 CFIndex i, count = CFArrayGetCount(entitlements); 290 values = CFDictionaryCreateMutable(CFGetAllocator(task), count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 291 if (task->entitlements != NULL) { 292 for (i = 0; i < count; i++) { 293 CFStringRef entitlement = CFArrayGetValueAtIndex(entitlements, i); 294 CFTypeRef value = CFDictionaryGetValue(task->entitlements, entitlement); 295 if (value != NULL) { 296 CFDictionarySetValue(values, entitlement, value); 297 } 298 } 299 } 300 } 301out: 302 return values; 303} 304