1 /* Copyright (c) 2012-2013 Apple Inc. All Rights Reserved. */
7 #include "authd_private.h"
9 #include "authutilities.h"
13 #include <Security/SecCode.h>
14 #include <Security/SecRequirement.h>
16 #include <security_utilities/simulatecrash_assert.h>
21 __AUTH_BASE_STRUCT_HEADER__
;
23 audit_info_s auditInfo
;
27 CFMutableBagRef authTokens
;
28 dispatch_queue_t dispatch_queue
;
30 CFMutableSetRef connections
;
32 char code_url
[PATH_MAX
+1];
33 char * code_identifier
;
34 CFDataRef code_requirement_data
;
35 SecRequirementRef code_requirement
;
36 CFDictionaryRef code_entitlements
;
38 mach_port_t bootstrap
;
41 bool firstPartySigned
;
45 _unregister_auth_tokens(const void *value
, void *context
)
47 auth_token_t auth
= (auth_token_t
)value
;
48 process_t proc
= (process_t
)context
;
50 CFIndex count
= auth_token_remove_process(auth
, proc
);
51 if ((count
== 0) && auth_token_check_state(auth
, auth_token_state_registered
)) {
52 server_unregister_auth_token(auth
);
57 _destroy_zombie_tokens(process_t proc
)
59 os_log_debug(AUTHD_LOG
, "destroy zombies, %ld auth tokens", CFBagGetCount(proc
->authTokens
));
60 _cf_bag_iterate(proc
->authTokens
, ^bool(CFTypeRef value
) {
61 auth_token_t auth
= (auth_token_t
)value
;
62 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
));
63 if (auth_token_is_creator(auth
, proc
) && auth_token_check_state(auth
, auth_token_state_zombie
) && (auth_token_get_process_count(auth
) == 1)) {
64 CFBagRemoveValue(proc
->authTokens
, auth
);
71 _process_finalize(CFTypeRef value
)
73 process_t proc
= (process_t
)value
;
75 os_log_debug(AUTHD_LOG
, "process deallocated");
77 dispatch_barrier_sync(proc
->dispatch_queue
, ^{
78 CFBagApplyFunction(proc
->authTokens
, _unregister_auth_tokens
, proc
);
81 session_remove_process(proc
->session
, proc
);
83 dispatch_release(proc
->dispatch_queue
);
84 CFReleaseNull(proc
->authTokens
);
85 CFReleaseNull(proc
->connections
);
86 CFReleaseNull(proc
->session
);
87 CFReleaseNull(proc
->code_requirement
);
88 CFReleaseNull(proc
->code_requirement_data
);
89 CFReleaseNull(proc
->code_entitlements
);
90 free_safe(proc
->code_identifier
);
91 if (proc
->bootstrap
!= MACH_PORT_NULL
) {
92 mach_port_deallocate(mach_task_self(), proc
->bootstrap
);
93 proc
->bootstrap
= MACH_PORT_NULL
;
97 AUTH_TYPE_INSTANCE(process
,
100 .finalize
= _process_finalize
,
103 .copyFormattingDesc
= NULL
,
104 .copyDebugDesc
= NULL
107 static CFTypeID
process_get_type_id() {
108 static CFTypeID type_id
= _kCFRuntimeNotATypeID
;
109 static dispatch_once_t onceToken
;
111 dispatch_once(&onceToken
, ^{
112 type_id
= _CFRuntimeRegisterClass(&_auth_type_process
);
119 process_create(const audit_info_s
* auditInfo
, session_t session
)
121 OSStatus status
= errSecSuccess
;
122 process_t proc
= NULL
;
123 CFDictionaryRef code_info
= NULL
;
124 CFURLRef code_url
= NULL
;
125 SecCodeRef codeRef
= NULL
;
127 require(session
!= NULL
, done
);
128 require(auditInfo
!= NULL
, done
);
130 proc
= (process_t
)_CFRuntimeCreateInstance(kCFAllocatorDefault
, process_get_type_id(), AUTH_CLASS_SIZE(process
), NULL
);
131 require(proc
!= NULL
, done
);
133 proc
->auditInfo
= *auditInfo
;
135 proc
->session
= (session_t
)CFRetain(session
);
137 proc
->connections
= CFSetCreateMutable(kCFAllocatorDefault
, 0, NULL
);
139 proc
->authTokens
= CFBagCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeBagCallBacks
);
140 check(proc
->authTokens
!= NULL
);
142 proc
->dispatch_queue
= dispatch_queue_create(NULL
, DISPATCH_QUEUE_SERIAL
);
143 check(proc
->dispatch_queue
!= NULL
);
145 // to have at least some code URL just for case later methods fail
146 int retval
= proc_pidpath(proc
->auditInfo
.pid
, proc
->code_url
, sizeof(proc
->code_url
));
148 os_log_error(AUTHD_LOG
, "process: PID %d pidpathfailed %d", proc
->auditInfo
.pid
, retval
);
151 CFMutableDictionaryRef codeDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
152 CFDataRef auditToken
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (UInt8
*)&auditInfo
->opaqueToken
, sizeof(auditInfo
->opaqueToken
), kCFAllocatorNull
);
154 CFDictionarySetValue(codeDict
, kSecGuestAttributeAudit
, auditToken
);
155 CFReleaseNull(auditToken
);
157 CFReleaseSafe(codeDict
);
158 os_log_error(AUTHD_LOG
, "process: PID %d failed to create audit token", proc
->auditInfo
.pid
);
162 status
= SecCodeCopyGuestWithAttributes(NULL
, codeDict
, kSecCSDefaultFlags
, &codeRef
);
163 CFReleaseSafe(codeDict
);
165 os_log_error(AUTHD_LOG
, "process: PID %d failed to create code ref %d", proc
->auditInfo
.pid
, (int)status
);
170 status
= SecCodeCopyPath(codeRef
, kSecCSDefaultFlags
, &code_url
);
171 if (status
== errSecSuccess
) {
172 CFURLGetFileSystemRepresentation(code_url
, true, (UInt8
*)proc
->code_url
, sizeof(proc
->code_url
));
174 os_log_error(AUTHD_LOG
, "process: PID %d failed to get path %d", proc
->auditInfo
.pid
, (int)status
);
177 status
= SecCodeCheckValidity(codeRef
, kSecCSDefaultFlags
, NULL
);
178 require_noerr_action(status
, done
, os_log_error(AUTHD_LOG
, "process: PID %d SecCodeCheckValidity failed with %d", proc
->auditInfo
.pid
, (int)status
));
180 status
= SecCodeCopySigningInformation(codeRef
, kSecCSRequirementInformation
, &code_info
);
181 require_noerr_action(status
, done
, os_log_debug(AUTHD_LOG
, "process: PID %d SecCodeCopySigningInformation failed with %d", proc
->auditInfo
.pid
, (int)status
));
183 CFTypeRef value
= NULL
;
184 if (CFDictionaryGetValueIfPresent(code_info
, kSecCodeInfoDesignatedRequirement
, (const void**)&value
)) {
185 if (CFGetTypeID(value
) == SecRequirementGetTypeID()) {
186 SecRequirementCopyData((SecRequirementRef
)value
, kSecCSDefaultFlags
, &proc
->code_requirement_data
);
187 if (proc
->code_requirement_data
) {
188 SecRequirementCreateWithData(proc
->code_requirement_data
, kSecCSDefaultFlags
, &proc
->code_requirement
);
194 if (CFDictionaryGetValueIfPresent(code_info
, kSecCodeInfoIdentifier
, &value
)) {
195 if (CFGetTypeID(value
) == CFStringGetTypeID()) {
196 proc
->code_identifier
= _copy_cf_string(value
, NULL
);
201 if (CFDictionaryGetValueIfPresent(code_info
, kSecCodeInfoEntitlementsDict
, &value
)) {
202 if (CFGetTypeID(value
) == CFDictionaryGetTypeID()) {
203 proc
->code_entitlements
= CFDictionaryCreateCopy(kCFAllocatorDefault
, value
);
208 // This is the clownfish supported way to check for a Mac App Store or B&I signed build
209 // AppStore apps must have resource envelope 2. Check with spctl -a -t exec -vv <path>
210 CFStringRef firstPartyRequirement
= CFSTR("anchor apple");
211 CFStringRef appStoreRequirement
= CFSTR("anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9] exists");
212 SecRequirementRef secRequirementRef
= NULL
;
213 status
= SecRequirementCreateWithString(firstPartyRequirement
, kSecCSDefaultFlags
, &secRequirementRef
);
214 if (status
== errSecSuccess
) {
215 proc
->firstPartySigned
= process_verify_requirement(proc
, codeRef
, secRequirementRef
);
216 CFReleaseNull(secRequirementRef
);
218 status
= SecRequirementCreateWithString(appStoreRequirement
, kSecCSDefaultFlags
, &secRequirementRef
);
219 if (status
== errSecSuccess
) {
220 proc
->appStoreSigned
= process_verify_requirement(proc
, codeRef
, secRequirementRef
);
221 CFReleaseSafe(secRequirementRef
);
223 os_log_debug(AUTHD_LOG
, "process: PID %d created (sid=%i) %{public}s", proc
->auditInfo
.pid
, proc
->auditInfo
.asid
, proc
->code_url
);
226 CFReleaseSafe(code_info
);
227 CFReleaseSafe(code_url
);
228 CFReleaseSafe(codeRef
);
233 process_get_key(process_t proc
)
235 return &proc
->auditInfo
;
239 process_get_uid(process_t proc
)
241 assert(proc
); // marked non-null
242 return proc
->auditInfo
.euid
;
246 process_get_pid(process_t proc
)
248 assert(proc
); // marked non-null
249 return proc
->auditInfo
.pid
;
252 int32_t process_get_generation(process_t proc
)
254 assert(proc
); // marked non-null
255 return proc
->auditInfo
.tid
;
259 process_get_session_id(process_t proc
)
261 assert(proc
); // marked non-null
262 return proc
->auditInfo
.asid
;
266 process_get_session(process_t proc
)
268 assert(proc
); // marked non-null
269 return proc
->session
;
273 process_get_audit_info(process_t proc
)
275 return &proc
->auditInfo
;
279 process_get_code_url(process_t proc
)
281 return proc
->code_url
;
285 process_add_auth_token(process_t proc
, auth_token_t auth
)
287 dispatch_sync(proc
->dispatch_queue
, ^{
288 CFBagAddValue(proc
->authTokens
, auth
);
289 if (CFBagGetCountOfValue(proc
->authTokens
, auth
) == 1) {
290 auth_token_add_process(auth
, proc
);
296 process_remove_auth_token(process_t proc
, auth_token_t auth
, uint32_t flags
)
298 dispatch_sync(proc
->dispatch_queue
, ^{
299 bool destroy
= false;
300 bool creator
= auth_token_is_creator(auth
, proc
);
301 CFIndex count
= auth_token_get_process_count(auth
);
303 // if we are the last ones associated with this auth token or the caller passed in the kAuthorizationFlagDestroyRights
304 // then we break the link between the process and auth token. If another process holds a reference
305 // then kAuthorizationFlagDestroyRights will only break the link and not destroy the auth token
306 // <rdar://problem/14553640>
308 (flags
& kAuthorizationFlagDestroyRights
))
314 // If we created this token and someone else is holding a reference to it
315 // don't destroy the link until they have freed the authorization ref
316 // instead set the zombie state on the auth_token
318 if (CFBagGetCountOfValue(proc
->authTokens
, auth
) == 1) {
319 auth_token_set_state(auth
, auth_token_state_zombie
);
329 CFBagRemoveValue(proc
->authTokens
, auth
);
330 if (!CFBagContainsValue(proc
->authTokens
, auth
)) {
331 auth_token_remove_process(auth
, proc
);
333 if ((count
== 1) && auth_token_check_state(auth
, auth_token_state_registered
)) {
334 server_unregister_auth_token(auth
);
339 // destroy all eligible zombies
340 _destroy_zombie_tokens(proc
);
345 process_find_copy_auth_token(process_t proc
, const AuthorizationBlob
* blob
)
347 __block CFTypeRef auth
= NULL
;
348 dispatch_sync(proc
->dispatch_queue
, ^{
349 _cf_bag_iterate(proc
->authTokens
, ^bool(CFTypeRef value
) {
350 auth_token_t iter
= (auth_token_t
)value
;
351 if (memcmp(blob
, auth_token_get_blob(iter
), sizeof(AuthorizationBlob
)) == 0) {
359 return (auth_token_t
)auth
;
363 process_get_auth_token_count(process_t proc
)
365 __block CFIndex count
= 0;
366 dispatch_sync(proc
->dispatch_queue
, ^{
367 count
= CFBagGetCount(proc
->authTokens
);
373 process_add_connection(process_t proc
, connection_t conn
)
375 __block CFIndex count
= 0;
376 dispatch_sync(proc
->dispatch_queue
, ^{
377 CFSetAddValue(proc
->connections
, conn
);
378 count
= CFSetGetCount(proc
->connections
);
384 process_remove_connection(process_t proc
, connection_t conn
)
386 __block CFIndex count
= 0;
387 dispatch_sync(proc
->dispatch_queue
, ^{
388 CFSetRemoveValue(proc
->connections
, conn
);
389 count
= CFSetGetCount(proc
->connections
);
395 process_get_connection_count(process_t proc
)
397 __block CFIndex count
= 0;
398 dispatch_sync(proc
->dispatch_queue
, ^{
399 count
= CFSetGetCount(proc
->connections
);
405 process_copy_entitlement_value(process_t proc
, const char * entitlement
)
407 CFTypeRef value
= NULL
;
408 require(entitlement
!= NULL
, done
);
410 CFStringRef key
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, entitlement
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
411 if (proc
->code_entitlements
&& key
&& (CFDictionaryGetValueIfPresent(proc
->code_entitlements
, key
, &value
))) {
421 process_has_entitlement(process_t proc
, const char * entitlement
)
423 bool entitled
= false;
424 require(entitlement
!= NULL
, done
);
426 CFStringRef key
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, entitlement
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
427 CFTypeRef value
= NULL
;
428 if (proc
->code_entitlements
&& key
&& (CFDictionaryGetValueIfPresent(proc
->code_entitlements
, key
, &value
))) {
429 if (CFGetTypeID(value
) == CFBooleanGetTypeID()) {
430 entitled
= CFBooleanGetValue(value
);
440 process_has_entitlement_for_right(process_t proc
, const char * right
)
442 bool entitled
= false;
443 require(right
!= NULL
, done
);
445 CFTypeRef rights
= NULL
;
446 if (proc
->code_entitlements
&& CFDictionaryGetValueIfPresent(proc
->code_entitlements
, CFSTR("com.apple.private.AuthorizationServices"), &rights
)) {
447 if (CFGetTypeID(rights
) == CFArrayGetTypeID()) {
448 CFStringRef key
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, right
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
449 require(key
!= NULL
, done
);
451 CFIndex count
= CFArrayGetCount(rights
);
452 for (CFIndex i
= 0; i
< count
; i
++) {
453 if (CFEqual(CFArrayGetValueAtIndex(rights
, i
), key
)) {
467 process_get_identifier(process_t proc
)
469 return proc
->code_identifier
;
473 process_get_requirement_data(process_t proc
)
475 return proc
->code_requirement_data
;
479 process_get_requirement(process_t proc
)
481 return proc
->code_requirement
;
484 bool process_verify_requirement(process_t proc
, SecCodeRef codeRef
, SecRequirementRef requirment
)
486 OSStatus status
= SecCodeCheckValidity(codeRef
, kSecCSDefaultFlags
, requirment
);
487 if (status
!= errSecSuccess
) {
488 os_log_debug(AUTHD_LOG
, "process: PID %d code requirement check failed (%d)", proc
->auditInfo
.pid
, (int)status
);
490 return (status
== errSecSuccess
);
493 // Returns true if the process was signed by B&I or the Mac App Store
494 bool process_apple_signed(process_t proc
) {
495 return (proc
->firstPartySigned
|| proc
->appStoreSigned
);
498 // Returns true if the process was signed by B&I
499 bool process_firstparty_signed(process_t proc
) {
500 return proc
->firstPartySigned
;
503 mach_port_t
process_get_bootstrap(process_t proc
)
505 return proc
->bootstrap
;
508 bool process_set_bootstrap(process_t proc
, mach_port_t bootstrap
)
510 if (bootstrap
!= MACH_PORT_NULL
) {
511 if (proc
->bootstrap
!= MACH_PORT_NULL
) {
512 mach_port_deallocate(mach_task_self(), proc
->bootstrap
);
514 proc
->bootstrap
= bootstrap
;