1/* 2 * Copyright (c) 2009-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 <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> 30#include <inttypes.h> 31#include <pthread.h> 32#include <sys/sysctl.h> 33 34#include "SecCode.h" 35#include "SecCodePriv.h" 36#include "SecRequirement.h" 37 38#include "SecTask.h" 39#include "SecTaskPriv.h" 40 41 42struct __SecTask { 43 CFRuntimeBase base; 44 45 pid_t pid; 46 47 audit_token_t *token; 48 audit_token_t token_storage; 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 56enum { 57 kSecCodeMagicEntitlement = 0xfade7171, /* entitlement blob */ 58}; 59 60 61CFTypeID _kSecTaskTypeID = _kCFRuntimeNotATypeID; 62 63static void SecTaskFinalize(CFTypeRef cfTask) 64{ 65 SecTaskRef task = (SecTaskRef) cfTask; 66 67 if (task->entitlements != NULL) { 68 CFRelease(task->entitlements); 69 task->entitlements = NULL; 70 } 71} 72 73 74// Define PRIdPID (proper printf format string for pid_t) 75#define PRIdPID PRId32 76 77static CFStringRef SecTaskCopyDebugDescription(CFTypeRef cfTask) 78{ 79 SecTaskRef task = (SecTaskRef) cfTask; 80 const char *task_name; 81 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, task->pid}; 82 struct kinfo_proc kp; 83 size_t len = sizeof(kp); 84 if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1 || len == 0) 85 task_name = strerror(errno); 86 else 87 task_name = kp.kp_proc.p_comm; 88 89 return CFStringCreateWithFormat(CFGetAllocator(task), NULL, CFSTR("%s[%" PRIdPID "]"), task_name, task->pid); 90} 91 92static void SecTaskRegisterClass(void) 93{ 94 static const CFRuntimeClass SecTaskClass = { 95 .version = 0, 96 .className = "SecTask", 97 .init = NULL, 98 .copy = NULL, 99 .finalize = SecTaskFinalize, 100 .equal = NULL, 101 .hash = NULL, 102 .copyFormattingDesc = NULL, 103 .copyDebugDesc = SecTaskCopyDebugDescription, 104 }; 105 106 _kSecTaskTypeID = _CFRuntimeRegisterClass(&SecTaskClass); 107} 108 109CFTypeID SecTaskGetTypeID(void) 110{ 111 static pthread_once_t secTaskRegisterClassOnce = PTHREAD_ONCE_INIT; 112 113 /* Register the class with the CF runtime the first time through */ 114 pthread_once(&secTaskRegisterClassOnce, SecTaskRegisterClass); 115 116 return _kSecTaskTypeID; 117} 118 119static SecTaskRef SecTaskCreateWithPID(CFAllocatorRef allocator, pid_t pid) 120{ 121 CFIndex extra = sizeof(struct __SecTask) - sizeof(CFRuntimeBase); 122 SecTaskRef task = (SecTaskRef) _CFRuntimeCreateInstance(allocator, SecTaskGetTypeID(), extra, NULL); 123 if (task != NULL) { 124 task->pid = pid; 125 task->entitlementsLoaded = false; 126 task->entitlements = NULL; 127 } 128 129 return task; 130} 131 132SecTaskRef SecTaskCreateWithAuditToken(CFAllocatorRef allocator, audit_token_t token) 133{ 134 SecTaskRef task; 135 136 task = SecTaskCreateWithPID(allocator, audit_token_to_pid(token)); 137 if (task != NULL) { 138#if 0 139 task->token_storage = token; 140 task->token = &task->token_storage; 141#endif 142 } 143 144 return task; 145} 146 147SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator) 148{ 149 return SecTaskCreateWithPID(allocator, getpid()); 150} 151 152/* 153 * Determine if the given task meets a specified requirement. 154 */ 155OSStatus 156SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement) 157{ 158 OSStatus status; 159 SecCodeRef code = NULL; 160 SecRequirementRef req = NULL; 161 pid_t pid = task->pid; 162 if (pid <= 0) { 163 return errSecParam; 164 } 165 status = SecCodeCreateWithPID(pid, kSecCSDefaultFlags, &code); 166 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCreateWithPID=%d", status); 167 if (!status) { 168 status = SecRequirementCreateWithString(requirement, 169 kSecCSDefaultFlags, &req); 170 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecRequirementCreateWithString=%d", status); 171 } 172 if (!status) { 173 status = SecCodeCheckValidity(code, kSecCSDefaultFlags, req); 174 //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCheckValidity=%d", status); 175 } 176 if (req) 177 CFRelease(req); 178 if (code) 179 CFRelease(code); 180 181 return status; 182} 183 184static CFRange myMakeRange(CFIndex loc, CFIndex len) { 185 CFRange r = {.location = loc, .length = len }; 186 return r; 187} 188struct csheader { 189 uint32_t magic; 190 uint32_t length; 191}; 192 193static int 194csops_task(SecTaskRef task, int ops, void *blob, size_t size) 195{ 196 if (task->token) 197 return csops_audittoken(task->pid, ops, blob, size, task->token); 198 else 199 return csops(task->pid, ops, blob, size); 200} 201 202static int SecTaskLoadEntitlements(SecTaskRef task, CFErrorRef *error) 203{ 204 CFMutableDataRef data = NULL; 205 struct csheader header; 206 uint32_t bufferlen; 207 int ret; 208 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; 213 return 0; 214 } 215 216 bufferlen = ntohl(header.length); 217 /* check for insane values */ 218 if (bufferlen > 1024 * 1024 || bufferlen < 8) { 219 ret = EINVAL; 220 goto out; 221 } 222 data = CFDataCreateMutable(NULL, bufferlen); 223 if (data == NULL) { 224 ret = ENOMEM; 225 goto out; 226 } 227 CFDataSetLength(data, bufferlen); 228 ret = csops_task(task, CS_OPS_ENTITLEMENTS_BLOB, CFDataGetMutableBytePtr(data), bufferlen); 229 if (ret) { 230 ret = errno; 231 goto out; 232 } 233 CFDataDeleteBytes(data, myMakeRange(0, 8)); 234 task->entitlements = CFPropertyListCreateWithData(NULL, data, 0, NULL, error); 235 task->entitlementsLoaded = true; 236 out: 237 if (data) 238 CFRelease(data); 239 if (ret && error) 240 *error = CFErrorCreate(NULL, kCFErrorDomainMach, ret, NULL); 241 242 return ret; 243} 244 245CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error) 246{ 247 /* Load entitlements if necessary */ 248 if (task->entitlementsLoaded == false) { 249 SecTaskLoadEntitlements(task, error); 250 } 251 252 CFTypeRef value = NULL; 253 if (task->entitlements != NULL) { 254 value = CFDictionaryGetValue(task->entitlements, entitlement); 255 256 /* Return something the caller must release */ 257 if (value != NULL) { 258 CFRetain(value); 259 } 260 } 261 262 return value; 263} 264 265CFDictionaryRef SecTaskCopyValuesForEntitlements(SecTaskRef task, CFArrayRef entitlements, CFErrorRef *error) 266{ 267 /* Load entitlements if necessary */ 268 if (task->entitlementsLoaded == false) { 269 SecTaskLoadEntitlements(task, error); 270 } 271 272 /* Iterate over the passed in entitlements, populating the dictionary 273 * If entitlements were loaded but none were present, return an empty 274 * dictionary */ 275 CFMutableDictionaryRef values = NULL; 276 if (task->entitlementsLoaded == true) { 277 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); 284 if (value != NULL) { 285 CFDictionarySetValue(values, entitlement, value); 286 } 287 } 288 } 289 } 290 291 return values; 292} 293