1 /* Copyright (c) 2012-2013 Apple Inc. All Rights Reserved. */ 
   7 #include "authd_private.h" 
   9 #include "authutilities.h" 
  12 #include <Security/SecCode.h> 
  13 #include <Security/SecRequirement.h> 
  18     __AUTH_BASE_STRUCT_HEADER__
; 
  20     audit_info_s auditInfo
; 
  24     CFMutableBagRef authTokens
; 
  25     dispatch_queue_t dispatch_queue
; 
  27     CFMutableSetRef connections
; 
  29     char code_url
[PATH_MAX
+1]; 
  30     char * code_identifier
; 
  31     CFDataRef code_requirement_data
; 
  32     SecRequirementRef code_requirement
; 
  33     CFDictionaryRef code_entitlements
; 
  35     mach_port_t bootstrap
; 
  38         bool firstPartySigned
; 
  42 _unregister_auth_tokens(const void *value
, void *context
) 
  44     auth_token_t auth 
= (auth_token_t
)value
; 
  45     process_t proc 
= (process_t
)context
; 
  47     CFIndex count 
= auth_token_remove_process(auth
, proc
); 
  48     if ((count 
== 0)  && auth_token_check_state(auth
, auth_token_state_registered
)) { 
  49         server_unregister_auth_token(auth
); 
  54 _destroy_zombie_tokens(process_t proc
) 
  56     os_log_debug(AUTHD_LOG
, "destroy zombies, %ld auth tokens", CFBagGetCount(proc
->authTokens
)); 
  57     _cf_bag_iterate(proc
->authTokens
, ^bool(CFTypeRef value
) { 
  58         auth_token_t auth 
= (auth_token_t
)value
; 
  59         os_log_debug(AUTHD_LOG
, "process:, creator=%i, zombie=%i, process_cout=%ld", auth_token_is_creator(auth
, proc
), auth_token_check_state(auth
, auth_token_state_zombie
), auth_token_get_process_count(auth
)); 
  60         if (auth_token_is_creator(auth
, proc
) && auth_token_check_state(auth
, auth_token_state_zombie
) && (auth_token_get_process_count(auth
) == 1)) { 
  61             CFBagRemoveValue(proc
->authTokens
, auth
); 
  68 _process_finalize(CFTypeRef value
) 
  70     process_t proc 
= (process_t
)value
; 
  72     os_log_debug(AUTHD_LOG
, "process deallocated"); 
  74     dispatch_barrier_sync(proc
->dispatch_queue
, ^{ 
  75         CFBagApplyFunction(proc
->authTokens
, _unregister_auth_tokens
, proc
); 
  78     session_remove_process(proc
->session
, proc
); 
  80     dispatch_release(proc
->dispatch_queue
); 
  81     CFReleaseNull(proc
->authTokens
); 
  82     CFReleaseNull(proc
->connections
); 
  83     CFReleaseNull(proc
->session
); 
  84     CFReleaseNull(proc
->code_requirement
); 
  85     CFReleaseNull(proc
->code_requirement_data
); 
  86     CFReleaseNull(proc
->code_entitlements
); 
  87     free_safe(proc
->code_identifier
); 
  88     if (proc
->bootstrap 
!= MACH_PORT_NULL
) { 
  89         mach_port_deallocate(mach_task_self(), proc
->bootstrap
); 
  90                 proc
->bootstrap 
= MACH_PORT_NULL
; 
  94 AUTH_TYPE_INSTANCE(process
, 
  97                    .finalize 
= _process_finalize
, 
 100                    .copyFormattingDesc 
= NULL
, 
 101                    .copyDebugDesc 
= NULL
 
 104 static CFTypeID 
process_get_type_id() { 
 105     static CFTypeID type_id 
= _kCFRuntimeNotATypeID
; 
 106     static dispatch_once_t onceToken
; 
 108     dispatch_once(&onceToken
, ^{ 
 109         type_id 
= _CFRuntimeRegisterClass(&_auth_type_process
); 
 116 process_create(const audit_info_s 
* auditInfo
, session_t session
) 
 118     OSStatus status 
= errSecSuccess
; 
 119     process_t proc 
= NULL
; 
 120     CFDictionaryRef code_info 
= NULL
; 
 121     CFURLRef code_url 
= NULL
; 
 122     SecCodeRef codeRef 
= NULL
; 
 124     require(session 
!= NULL
, done
); 
 125     require(auditInfo 
!= NULL
, done
); 
 127     proc 
= (process_t
)_CFRuntimeCreateInstance(kCFAllocatorDefault
, process_get_type_id(), AUTH_CLASS_SIZE(process
), NULL
); 
 128     require(proc 
!= NULL
, done
); 
 130     proc
->auditInfo 
= *auditInfo
; 
 132     proc
->session 
= (session_t
)CFRetain(session
); 
 134     proc
->connections 
= CFSetCreateMutable(kCFAllocatorDefault
, 0, NULL
); 
 136     proc
->authTokens 
= CFBagCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeBagCallBacks
); 
 137     check(proc
->authTokens 
!= NULL
); 
 139     proc
->dispatch_queue 
= dispatch_queue_create(NULL
, DISPATCH_QUEUE_SERIAL
); 
 140     check(proc
->dispatch_queue 
!= NULL
); 
 142     CFMutableDictionaryRef codeDict 
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
 143     CFDataRef auditToken 
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (UInt8 
*)&auditInfo
->opaqueToken
, sizeof(auditInfo
->opaqueToken
), kCFAllocatorNull
); 
 145         CFDictionarySetValue(codeDict
, kSecGuestAttributeAudit
, auditToken
); 
 146         CFReleaseNull(auditToken
); 
 148         CFReleaseSafe(codeDict
); 
 149         os_log_error(AUTHD_LOG
, "process: PID %d failed to create audit token", proc
->auditInfo
.pid
); 
 153     status 
= SecCodeCopyGuestWithAttributes(NULL
, codeDict
, kSecCSDefaultFlags
, &codeRef
); 
 154     CFReleaseSafe(codeDict
); 
 157         os_log_error(AUTHD_LOG
, "process: PID %d failed to create code ref %d", proc
->auditInfo
.pid
, (int)status
); 
 162     status 
= SecCodeCheckValidity(codeRef
, kSecCSDefaultFlags
, NULL
); 
 163     require_noerr_action(status
, done
, os_log_error(AUTHD_LOG
, "process: PID %d SecCodeCheckValidity failed with %d", proc
->auditInfo
.pid
, (int)status
)); 
 165     status 
= SecCodeCopySigningInformation(codeRef
, kSecCSRequirementInformation
, &code_info
); 
 166     require_noerr_action(status
, done
, os_log_debug(AUTHD_LOG
, "process: PID %d SecCodeCopySigningInformation failed with %d", proc
->auditInfo
.pid
, (int)status
)); 
 168     CFTypeRef value 
= NULL
; 
 169     if (CFDictionaryGetValueIfPresent(code_info
, kSecCodeInfoDesignatedRequirement
, (const void**)&value
)) { 
 170         if (CFGetTypeID(value
) == SecRequirementGetTypeID()) { 
 171             SecRequirementCopyData((SecRequirementRef
)value
, kSecCSDefaultFlags
, &proc
->code_requirement_data
); 
 172             if (proc
->code_requirement_data
) { 
 173                 SecRequirementCreateWithData(proc
->code_requirement_data
, kSecCSDefaultFlags
, &proc
->code_requirement
); 
 179     if (SecCodeCopyPath(codeRef
, kSecCSDefaultFlags
, &code_url
) == errSecSuccess
) { 
 180         CFURLGetFileSystemRepresentation(code_url
, true, (UInt8
*)proc
->code_url
, sizeof(proc
->code_url
)); 
 183     if (CFDictionaryGetValueIfPresent(code_info
, kSecCodeInfoIdentifier
, &value
)) { 
 184         if (CFGetTypeID(value
) == CFStringGetTypeID()) { 
 185             proc
->code_identifier 
= _copy_cf_string(value
, NULL
); 
 190     if (CFDictionaryGetValueIfPresent(code_info
, kSecCodeInfoEntitlementsDict
, &value
)) { 
 191         if (CFGetTypeID(value
) == CFDictionaryGetTypeID()) { 
 192             proc
->code_entitlements 
= CFDictionaryCreateCopy(kCFAllocatorDefault
, value
); 
 197     // This is the clownfish supported way to check for a Mac App Store or B&I signed build 
 198         // AppStore apps must have resource envelope 2. Check with spctl -a -t exec -vv <path> 
 199     CFStringRef firstPartyRequirement 
= CFSTR("anchor apple"); 
 200         CFStringRef appStoreRequirement 
= CFSTR("anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9] exists"); 
 201     SecRequirementRef secRequirementRef 
= NULL
; 
 202     status 
= SecRequirementCreateWithString(firstPartyRequirement
, kSecCSDefaultFlags
, &secRequirementRef
); 
 203     if (status 
== errSecSuccess
) { 
 204         proc
->firstPartySigned 
= process_verify_requirement(proc
, codeRef
, secRequirementRef
); 
 205                 CFReleaseNull(secRequirementRef
); 
 207         status 
= SecRequirementCreateWithString(appStoreRequirement
, kSecCSDefaultFlags
, &secRequirementRef
); 
 208         if (status 
== errSecSuccess
) { 
 209                 proc
->appStoreSigned 
= process_verify_requirement(proc
, codeRef
, secRequirementRef
); 
 210                 CFReleaseSafe(secRequirementRef
); 
 212     os_log_debug(AUTHD_LOG
, "process: PID %d created (sid=%i) %{public}s", proc
->auditInfo
.pid
, proc
->auditInfo
.asid
, proc
->code_url
); 
 215     CFReleaseSafe(code_info
); 
 216     CFReleaseSafe(code_url
); 
 217     CFReleaseSafe(codeRef
); 
 222 process_get_key(process_t proc
) 
 224     return &proc
->auditInfo
; 
 228 process_get_uid(process_t proc
) 
 230     assert(proc
); // marked non-null 
 231     return proc
->auditInfo
.euid
; 
 235 process_get_pid(process_t proc
) 
 237     assert(proc
); // marked non-null 
 238     return proc
->auditInfo
.pid
; 
 241 int32_t process_get_generation(process_t proc
) 
 243     assert(proc
); // marked non-null 
 244     return proc
->auditInfo
.tid
; 
 248 process_get_session_id(process_t proc
) 
 250     assert(proc
); // marked non-null 
 251     return proc
->auditInfo
.asid
; 
 255 process_get_session(process_t proc
) 
 257     assert(proc
); // marked non-null 
 258     return proc
->session
; 
 262 process_get_audit_info(process_t proc
) 
 264     return &proc
->auditInfo
; 
 268 process_get_code_url(process_t proc
) 
 270     return proc
->code_url
; 
 274 process_add_auth_token(process_t proc
, auth_token_t auth
) 
 276     dispatch_sync(proc
->dispatch_queue
, ^{ 
 277         CFBagAddValue(proc
->authTokens
, auth
); 
 278         if (CFBagGetCountOfValue(proc
->authTokens
, auth
) == 1) { 
 279             auth_token_add_process(auth
, proc
); 
 285 process_remove_auth_token(process_t proc
, auth_token_t auth
, uint32_t flags
) 
 287     dispatch_sync(proc
->dispatch_queue
, ^{ 
 288         bool destroy 
= false; 
 289         bool creator 
= auth_token_is_creator(auth
, proc
); 
 290         CFIndex count 
= auth_token_get_process_count(auth
); 
 292         // if we are the last ones associated with this auth token or the caller passed in the kAuthorizationFlagDestroyRights 
 293         // then we break the link between the process and auth token.  If another process holds a reference 
 294         // then kAuthorizationFlagDestroyRights will only break the link and not destroy the auth token 
 295         // <rdar://problem/14553640> 
 297             (flags 
& kAuthorizationFlagDestroyRights
)) 
 303         // If we created this token and someone else is holding a reference to it 
 304         // don't destroy the link until they have freed the authorization ref 
 305         // instead set the zombie state on the auth_token 
 307             if (CFBagGetCountOfValue(proc
->authTokens
, auth
) == 1) { 
 308                 auth_token_set_state(auth
, auth_token_state_zombie
); 
 318             CFBagRemoveValue(proc
->authTokens
, auth
); 
 319             if (!CFBagContainsValue(proc
->authTokens
, auth
)) { 
 320                 auth_token_remove_process(auth
, proc
); 
 322                 if ((count 
== 1) && auth_token_check_state(auth
, auth_token_state_registered
)) { 
 323                     server_unregister_auth_token(auth
); 
 328         // destroy all eligible zombies 
 329         _destroy_zombie_tokens(proc
); 
 334 process_find_copy_auth_token(process_t proc
, const AuthorizationBlob 
* blob
) 
 336     __block CFTypeRef auth 
= NULL
; 
 337     dispatch_sync(proc
->dispatch_queue
, ^{ 
 338         _cf_bag_iterate(proc
->authTokens
, ^bool(CFTypeRef value
) { 
 339             auth_token_t iter 
= (auth_token_t
)value
; 
 340             if (memcmp(blob
, auth_token_get_blob(iter
), sizeof(AuthorizationBlob
)) == 0) { 
 348     return (auth_token_t
)auth
; 
 352 process_get_auth_token_count(process_t proc
) 
 354     __block CFIndex count 
= 0; 
 355     dispatch_sync(proc
->dispatch_queue
, ^{ 
 356         count 
= CFBagGetCount(proc
->authTokens
); 
 362 process_add_connection(process_t proc
, connection_t conn
) 
 364     __block CFIndex count 
= 0; 
 365     dispatch_sync(proc
->dispatch_queue
, ^{ 
 366         CFSetAddValue(proc
->connections
, conn
); 
 367         count 
= CFSetGetCount(proc
->connections
); 
 373 process_remove_connection(process_t proc
, connection_t conn
) 
 375     __block CFIndex count 
= 0; 
 376     dispatch_sync(proc
->dispatch_queue
, ^{ 
 377         CFSetRemoveValue(proc
->connections
, conn
); 
 378         count 
= CFSetGetCount(proc
->connections
); 
 384 process_get_connection_count(process_t proc
) 
 386     __block CFIndex count 
= 0; 
 387     dispatch_sync(proc
->dispatch_queue
, ^{ 
 388         count 
= CFSetGetCount(proc
->connections
); 
 394 process_copy_entitlement_value(process_t proc
, const char * entitlement
) 
 396     CFTypeRef value 
= NULL
; 
 397     require(entitlement 
!= NULL
, done
); 
 399     CFStringRef key 
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, entitlement
, kCFStringEncodingUTF8
, kCFAllocatorNull
); 
 400     if (proc
->code_entitlements 
&& key 
&& (CFDictionaryGetValueIfPresent(proc
->code_entitlements
, key
, &value
))) { 
 410 process_has_entitlement(process_t proc
, const char * entitlement
) 
 412     bool entitled 
= false; 
 413     require(entitlement 
!= NULL
, done
); 
 415     CFStringRef key 
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, entitlement
, kCFStringEncodingUTF8
, kCFAllocatorNull
); 
 416     CFTypeRef value 
= NULL
; 
 417     if (proc
->code_entitlements 
&& key 
&& (CFDictionaryGetValueIfPresent(proc
->code_entitlements
, key
, &value
))) { 
 418         if (CFGetTypeID(value
) == CFBooleanGetTypeID()) { 
 419             entitled 
= CFBooleanGetValue(value
); 
 429 process_has_entitlement_for_right(process_t proc
, const char * right
) 
 431     bool entitled 
= false; 
 432     require(right 
!= NULL
, done
); 
 434     CFTypeRef rights 
= NULL
; 
 435     if (proc
->code_entitlements 
&& CFDictionaryGetValueIfPresent(proc
->code_entitlements
, CFSTR("com.apple.private.AuthorizationServices"), &rights
)) { 
 436         if (CFGetTypeID(rights
) == CFArrayGetTypeID()) { 
 437             CFStringRef key 
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, right
, kCFStringEncodingUTF8
, kCFAllocatorNull
); 
 438             require(key 
!= NULL
, done
); 
 440             CFIndex count 
= CFArrayGetCount(rights
); 
 441             for (CFIndex i 
= 0; i 
< count
; i
++) { 
 442                 if (CFEqual(CFArrayGetValueAtIndex(rights
, i
), key
)) { 
 456 process_get_identifier(process_t proc
) 
 458     return proc
->code_identifier
; 
 462 process_get_requirement_data(process_t proc
) 
 464     return proc
->code_requirement_data
; 
 468 process_get_requirement(process_t proc
) 
 470     return proc
->code_requirement
; 
 473 bool process_verify_requirement(process_t proc
, SecCodeRef codeRef
, SecRequirementRef requirment
) 
 475     OSStatus status 
= SecCodeCheckValidity(codeRef
, kSecCSDefaultFlags
, requirment
); 
 476     if (status 
!= errSecSuccess
) { 
 477         os_log_debug(AUTHD_LOG
, "process: PID %d code requirement check failed (%d)", proc
->auditInfo
.pid
, (int)status
); 
 479     return (status 
== errSecSuccess
); 
 482 // Returns true if the process was signed by B&I or the Mac App Store 
 483 bool process_apple_signed(process_t proc
) { 
 484     return (proc
->firstPartySigned 
|| proc
->appStoreSigned
); 
 487 // Returns true if the process was signed by B&I 
 488 bool process_firstparty_signed(process_t proc
) { 
 489         return proc
->firstPartySigned
; 
 492 mach_port_t 
process_get_bootstrap(process_t proc
) 
 494     return proc
->bootstrap
; 
 497 bool process_set_bootstrap(process_t proc
, mach_port_t bootstrap
) 
 499     if (bootstrap 
!= MACH_PORT_NULL
) { 
 500         if (proc
->bootstrap 
!= MACH_PORT_NULL
) { 
 501             mach_port_deallocate(mach_task_self(), proc
->bootstrap
); 
 503         proc
->bootstrap 
= bootstrap
;