1/* Copyright (c) 2012-2013 Apple Inc. All Rights Reserved. */ 2 3#include "process.h" 4#include "server.h" 5#include "session.h" 6#include "debugging.h" 7#include "authd_private.h" 8#include "authtoken.h" 9#include "authutilities.h" 10#include "ccaudit.h" 11 12#include <Security/SecCode.h> 13#include <Security/SecRequirement.h> 14 15struct _process_s { 16 __AUTH_BASE_STRUCT_HEADER__; 17 18 audit_info_s auditInfo; 19 20 session_t session; 21 22 CFMutableBagRef authTokens; 23 dispatch_queue_t dispatch_queue; 24 25 CFMutableSetRef connections; 26 27 SecCodeRef codeRef; 28 char code_url[PATH_MAX+1]; 29 char * code_identifier; 30 CFDataRef code_requirement_data; 31 SecRequirementRef code_requirement; 32 CFDictionaryRef code_entitlements; 33 34 mach_port_t bootstrap; 35 36 bool appleSigned; 37}; 38 39static void 40_unregister_auth_tokens(const void *value, void *context) 41{ 42 auth_token_t auth = (auth_token_t)value; 43 process_t proc = (process_t)context; 44 45 CFIndex count = auth_token_remove_process(auth, proc); 46 if ((count == 0) && auth_token_check_state(auth, auth_token_state_registered)) { 47 server_unregister_auth_token(auth); 48 } 49} 50 51static void 52_destroy_zombie_tokens(process_t proc) 53{ 54 LOGD("process[%i] destroy zombies, %ld auth tokens", process_get_pid(proc), CFBagGetCount(proc->authTokens)); 55 _cf_bag_iterate(proc->authTokens, ^bool(CFTypeRef value) { 56 auth_token_t auth = (auth_token_t)value; 57 LOGD("process[%i] %p, creator=%i, zombie=%i, process_cout=%ld", process_get_pid(proc), auth, auth_token_is_creator(auth, proc), auth_token_check_state(auth, auth_token_state_zombie), auth_token_get_process_count(auth)); 58 if (auth_token_is_creator(auth, proc) && auth_token_check_state(auth, auth_token_state_zombie) && (auth_token_get_process_count(auth) == 1)) { 59 CFBagRemoveValue(proc->authTokens, auth); 60 } 61 return true; 62 }); 63} 64 65static void 66_process_finalize(CFTypeRef value) 67{ 68 process_t proc = (process_t)value; 69 70 LOGV("process[%i]: deallocated %p", proc->auditInfo.pid, proc); 71 72 dispatch_barrier_sync(proc->dispatch_queue, ^{ 73 CFBagApplyFunction(proc->authTokens, _unregister_auth_tokens, proc); 74 }); 75 76 session_remove_process(proc->session, proc); 77 78 dispatch_release(proc->dispatch_queue); 79 CFReleaseSafe(proc->authTokens); 80 CFReleaseSafe(proc->connections); 81 CFReleaseSafe(proc->session); 82 CFReleaseSafe(proc->codeRef); 83 CFReleaseSafe(proc->code_requirement); 84 CFReleaseSafe(proc->code_requirement_data); 85 CFReleaseSafe(proc->code_entitlements); 86 free_safe(proc->code_identifier); 87 if (proc->bootstrap != MACH_PORT_NULL) { 88 mach_port_deallocate(mach_task_self(), proc->bootstrap); 89 } 90} 91 92AUTH_TYPE_INSTANCE(process, 93 .init = NULL, 94 .copy = NULL, 95 .finalize = _process_finalize, 96 .equal = NULL, 97 .hash = NULL, 98 .copyFormattingDesc = NULL, 99 .copyDebugDesc = NULL 100 ); 101 102static CFTypeID process_get_type_id() { 103 static CFTypeID type_id = _kCFRuntimeNotATypeID; 104 static dispatch_once_t onceToken; 105 106 dispatch_once(&onceToken, ^{ 107 type_id = _CFRuntimeRegisterClass(&_auth_type_process); 108 }); 109 110 return type_id; 111} 112 113process_t 114process_create(const audit_info_s * auditInfo, session_t session) 115{ 116 OSStatus status = errSecSuccess; 117 process_t proc = NULL; 118 CFDictionaryRef code_info = NULL; 119 CFURLRef code_url = NULL; 120 121 require(session != NULL, done); 122 require(auditInfo != NULL, done); 123 124 proc = (process_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, process_get_type_id(), AUTH_CLASS_SIZE(process), NULL); 125 require(proc != NULL, done); 126 127 proc->auditInfo = *auditInfo; 128 129 proc->session = (session_t)CFRetain(session); 130 131 proc->connections = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL); 132 133 proc->authTokens = CFBagCreateMutable(kCFAllocatorDefault, 0, &kCFTypeBagCallBacks); 134 check(proc->authTokens != NULL); 135 136 proc->dispatch_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 137 check(proc->dispatch_queue != NULL); 138 139 CFMutableDictionaryRef codeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 140 CFNumberRef codePid = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &proc->auditInfo.pid); 141 CFDictionarySetValue(codeDict, kSecGuestAttributePid, codePid); 142 status = SecCodeCopyGuestWithAttributes(NULL, codeDict, kSecCSDefaultFlags, &proc->codeRef); 143 CFReleaseSafe(codeDict); 144 CFReleaseSafe(codePid); 145 146 if (status) { 147 LOGE("process[%i]: failed to create code ref %i", proc->auditInfo.pid, status); 148 CFReleaseNull(proc); 149 goto done; 150 } 151 152 status = SecCodeCopySigningInformation(proc->codeRef, kSecCSRequirementInformation, &code_info); 153 require_noerr_action(status, done, LOGV("process[%i]: SecCodeCopySigningInformation failed with %i", proc->auditInfo.pid, status)); 154 155 CFTypeRef value = NULL; 156 if (CFDictionaryGetValueIfPresent(code_info, kSecCodeInfoDesignatedRequirement, (const void**)&value)) { 157 if (CFGetTypeID(value) == SecRequirementGetTypeID()) { 158 SecRequirementCopyData((SecRequirementRef)value, kSecCSDefaultFlags, &proc->code_requirement_data); 159 if (proc->code_requirement_data) { 160 SecRequirementCreateWithData(proc->code_requirement_data, kSecCSDefaultFlags, &proc->code_requirement); 161 } 162 } 163 value = NULL; 164 } 165 166 if (SecCodeCopyPath(proc->codeRef, kSecCSDefaultFlags, &code_url) == errSecSuccess) { 167 CFURLGetFileSystemRepresentation(code_url, true, (UInt8*)proc->code_url, sizeof(proc->code_url)); 168 } 169 170 if (CFDictionaryGetValueIfPresent(code_info, kSecCodeInfoIdentifier, &value)) { 171 if (CFGetTypeID(value) == CFStringGetTypeID()) { 172 proc->code_identifier = _copy_cf_string(value, NULL); 173 } 174 value = NULL; 175 } 176 177 if (CFDictionaryGetValueIfPresent(code_info, kSecCodeInfoEntitlementsDict, &value)) { 178 if (CFGetTypeID(value) == CFDictionaryGetTypeID()) { 179 proc->code_entitlements = CFDictionaryCreateCopy(kCFAllocatorDefault, value); 180 } 181 value = NULL; 182 } 183 184 // This is the clownfish supported way to check for a Mac App Store or B&I signed build 185 CFStringRef requirementString = CFSTR("(anchor apple) or (anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9])"); 186 SecRequirementRef secRequirementRef = NULL; 187 status = SecRequirementCreateWithString(requirementString, kSecCSDefaultFlags, &secRequirementRef); 188 if (status == errSecSuccess) { 189 proc->appleSigned = process_verify_requirment(proc, secRequirementRef); 190 } 191 CFReleaseSafe(secRequirementRef); 192 193 LOGV("process[%i]: created (sid=%i) %s %p", proc->auditInfo.pid, proc->auditInfo.asid, proc->code_url, proc); 194 195done: 196 CFReleaseSafe(code_info); 197 CFReleaseSafe(code_url); 198 return proc; 199} 200 201const void * 202process_get_key(process_t proc) 203{ 204 return &proc->auditInfo; 205} 206 207uid_t 208process_get_uid(process_t proc) 209{ 210 return proc ? proc->auditInfo.euid : (uid_t)-2; 211} 212 213pid_t 214process_get_pid(process_t proc) 215{ 216 return proc ? proc->auditInfo.pid : -1; 217} 218 219int32_t process_get_generation(process_t proc) 220{ 221 return proc->auditInfo.tid; 222} 223 224session_id_t 225process_get_session_id(process_t proc) 226{ 227 return proc ? proc->auditInfo.asid : -1; 228} 229 230session_t 231process_get_session(process_t proc) 232{ 233 return proc->session; 234} 235 236const audit_info_s * 237process_get_audit_info(process_t proc) 238{ 239 return &proc->auditInfo; 240} 241 242SecCodeRef 243process_get_code(process_t proc) 244{ 245 return proc->codeRef; 246} 247 248const char * 249process_get_code_url(process_t proc) 250{ 251 return proc->code_url; 252} 253 254void 255process_add_auth_token(process_t proc, auth_token_t auth) 256{ 257 dispatch_sync(proc->dispatch_queue, ^{ 258 CFBagAddValue(proc->authTokens, auth); 259 if (CFBagGetCountOfValue(proc->authTokens, auth) == 1) { 260 auth_token_add_process(auth, proc); 261 } 262 }); 263} 264 265void 266process_remove_auth_token(process_t proc, auth_token_t auth, AuthorizationFlags flags) 267{ 268 dispatch_sync(proc->dispatch_queue, ^{ 269 bool destroy = false; 270 bool creator = auth_token_is_creator(auth, proc); 271 CFIndex count = auth_token_get_process_count(auth); 272 273 // if we are the last ones associated with this auth token or the caller passed in the kAuthorizationFlagDestroyRights 274 // then we break the link between the process and auth token. If another process holds a reference 275 // then kAuthorizationFlagDestroyRights will only break the link and not destroy the auth token 276 // <rdar://problem/14553640> 277 if ((count == 1) || 278 (flags & kAuthorizationFlagDestroyRights)) 279 { 280 destroy = true; 281 goto done; 282 } 283 284 // If we created this token and someone else is holding a reference to it 285 // don't destroy the link until they have freed the authorization ref 286 // instead set the zombie state on the auth_token 287 if (creator) { 288 if (CFBagGetCountOfValue(proc->authTokens, auth) == 1) { 289 auth_token_set_state(auth, auth_token_state_zombie); 290 } else { 291 destroy = true; 292 } 293 } else { 294 destroy = true; 295 } 296 297 done: 298 if (destroy) { 299 CFBagRemoveValue(proc->authTokens, auth); 300 if (!CFBagContainsValue(proc->authTokens, auth)) { 301 auth_token_remove_process(auth, proc); 302 303 if ((count == 1) && auth_token_check_state(auth, auth_token_state_registered)) { 304 server_unregister_auth_token(auth); 305 } 306 } 307 } 308 309 // destroy all eligible zombies 310 _destroy_zombie_tokens(proc); 311 }); 312} 313 314auth_token_t 315process_find_copy_auth_token(process_t proc, const AuthorizationBlob * blob) 316{ 317 __block CFTypeRef auth = NULL; 318 dispatch_sync(proc->dispatch_queue, ^{ 319 _cf_bag_iterate(proc->authTokens, ^bool(CFTypeRef value) { 320 auth_token_t iter = (auth_token_t)value; 321 if (memcmp(blob, auth_token_get_blob(iter), sizeof(AuthorizationBlob)) == 0) { 322 auth = iter; 323 CFRetain(auth); 324 return false; 325 } 326 return true; 327 }); 328 }); 329 return (auth_token_t)auth; 330} 331 332CFIndex 333process_get_auth_token_count(process_t proc) 334{ 335 __block CFIndex count = 0; 336 dispatch_sync(proc->dispatch_queue, ^{ 337 count = CFBagGetCount(proc->authTokens); 338 }); 339 return count; 340} 341 342CFIndex 343process_add_connection(process_t proc, connection_t conn) 344{ 345 __block CFIndex count = 0; 346 dispatch_sync(proc->dispatch_queue, ^{ 347 CFSetAddValue(proc->connections, conn); 348 count = CFSetGetCount(proc->connections); 349 }); 350 return count; 351} 352 353CFIndex 354process_remove_connection(process_t proc, connection_t conn) 355{ 356 __block CFIndex count = 0; 357 dispatch_sync(proc->dispatch_queue, ^{ 358 CFSetRemoveValue(proc->connections, conn); 359 count = CFSetGetCount(proc->connections); 360 }); 361 return count; 362} 363 364CFIndex 365process_get_connection_count(process_t proc) 366{ 367 __block CFIndex count = 0; 368 dispatch_sync(proc->dispatch_queue, ^{ 369 count = CFSetGetCount(proc->connections); 370 }); 371 return count; 372} 373 374CFTypeRef 375process_copy_entitlement_value(process_t proc, const char * entitlement) 376{ 377 CFTypeRef value = NULL; 378 require(entitlement != NULL, done); 379 380 CFStringRef key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, entitlement, kCFStringEncodingUTF8, kCFAllocatorNull); 381 if (proc->code_entitlements && key && (CFDictionaryGetValueIfPresent(proc->code_entitlements, key, &value))) { 382 CFRetainSafe(value); 383 } 384 CFReleaseSafe(key); 385 386done: 387 return value; 388} 389 390bool 391process_has_entitlement(process_t proc, const char * entitlement) 392{ 393 bool entitled = false; 394 require(entitlement != NULL, done); 395 396 CFStringRef key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, entitlement, kCFStringEncodingUTF8, kCFAllocatorNull); 397 CFTypeRef value = NULL; 398 if (proc->code_entitlements && key && (CFDictionaryGetValueIfPresent(proc->code_entitlements, key, &value))) { 399 if (CFGetTypeID(value) == CFBooleanGetTypeID()) { 400 entitled = CFBooleanGetValue(value); 401 } 402 } 403 CFReleaseSafe(key); 404 405done: 406 return entitled; 407} 408 409bool 410process_has_entitlement_for_right(process_t proc, const char * right) 411{ 412 bool entitled = false; 413 require(right != NULL, done); 414 415 CFTypeRef rights = NULL; 416 if (proc->code_entitlements && CFDictionaryGetValueIfPresent(proc->code_entitlements, CFSTR("com.apple.private.AuthorizationServices"), &rights)) { 417 if (CFGetTypeID(rights) == CFArrayGetTypeID()) { 418 CFStringRef key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, right, kCFStringEncodingUTF8, kCFAllocatorNull); 419 require(key != NULL, done); 420 421 CFIndex count = CFArrayGetCount(rights); 422 for (CFIndex i = 0; i < count; i++) { 423 if (CFEqual(CFArrayGetValueAtIndex(rights, i), key)) { 424 entitled = true; 425 break; 426 } 427 } 428 CFReleaseSafe(key); 429 } 430 } 431 432done: 433 return entitled; 434} 435 436const char * 437process_get_identifier(process_t proc) 438{ 439 return proc->code_identifier; 440} 441 442CFDataRef 443process_get_requirement_data(process_t proc) 444{ 445 return proc->code_requirement_data; 446} 447 448SecRequirementRef 449process_get_requirement(process_t proc) 450{ 451 return proc->code_requirement; 452} 453 454bool process_verify_requirment(process_t proc, SecRequirementRef requirment) 455{ 456 OSStatus status = SecCodeCheckValidity(proc->codeRef, kSecCSDefaultFlags, requirment); 457 if (status != errSecSuccess) { 458 LOGV("process[%i]: code requirement check failed (%d)", proc->auditInfo.pid, status); 459 } 460 return (status == errSecSuccess); 461} 462 463// Returns true if the process was signed by B&I or the Mac App Store 464bool process_apple_signed(process_t proc) { 465 return proc->appleSigned; 466} 467 468mach_port_t process_get_bootstrap(process_t proc) 469{ 470 return proc->bootstrap; 471} 472 473bool process_set_bootstrap(process_t proc, mach_port_t bootstrap) 474{ 475 if (bootstrap != MACH_PORT_NULL) { 476 if (proc->bootstrap != MACH_PORT_NULL) { 477 mach_port_deallocate(mach_task_self(), proc->bootstrap); 478 } 479 proc->bootstrap = bootstrap; 480 return true; 481 } 482 return false; 483} 484 485