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>
16 __AUTH_BASE_STRUCT_HEADER__
;
18 audit_info_s auditInfo
;
22 CFMutableBagRef authTokens
;
23 dispatch_queue_t dispatch_queue
;
25 CFMutableSetRef connections
;
28 char code_url
[PATH_MAX
+1];
29 char * code_identifier
;
30 CFDataRef code_requirement_data
;
31 SecRequirementRef code_requirement
;
32 CFDictionaryRef code_entitlements
;
34 mach_port_t bootstrap
;
40 _unregister_auth_tokens(const void *value
, void *context
)
42 auth_token_t auth
= (auth_token_t
)value
;
43 process_t proc
= (process_t
)context
;
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
);
52 _destroy_zombie_tokens(process_t proc
)
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
);
66 _process_finalize(CFTypeRef value
)
68 process_t proc
= (process_t
)value
;
70 LOGV("process[%i]: deallocated %p", proc
->auditInfo
.pid
, proc
);
72 dispatch_barrier_sync(proc
->dispatch_queue
, ^{
73 CFBagApplyFunction(proc
->authTokens
, _unregister_auth_tokens
, proc
);
76 session_remove_process(proc
->session
, proc
);
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
);
92 AUTH_TYPE_INSTANCE(process
,
95 .finalize
= _process_finalize
,
98 .copyFormattingDesc
= NULL
,
102 static CFTypeID
process_get_type_id() {
103 static CFTypeID type_id
= _kCFRuntimeNotATypeID
;
104 static dispatch_once_t onceToken
;
106 dispatch_once(&onceToken
, ^{
107 type_id
= _CFRuntimeRegisterClass(&_auth_type_process
);
114 process_create(const audit_info_s
* auditInfo
, session_t session
)
116 OSStatus status
= errSecSuccess
;
117 process_t proc
= NULL
;
118 CFDictionaryRef code_info
= NULL
;
119 CFURLRef code_url
= NULL
;
121 require(session
!= NULL
, done
);
122 require(auditInfo
!= NULL
, done
);
124 proc
= (process_t
)_CFRuntimeCreateInstance(kCFAllocatorDefault
, process_get_type_id(), AUTH_CLASS_SIZE(process
), NULL
);
125 require(proc
!= NULL
, done
);
127 proc
->auditInfo
= *auditInfo
;
129 proc
->session
= (session_t
)CFRetain(session
);
131 proc
->connections
= CFSetCreateMutable(kCFAllocatorDefault
, 0, NULL
);
133 proc
->authTokens
= CFBagCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeBagCallBacks
);
134 check(proc
->authTokens
!= NULL
);
136 proc
->dispatch_queue
= dispatch_queue_create(NULL
, DISPATCH_QUEUE_SERIAL
);
137 check(proc
->dispatch_queue
!= NULL
);
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
);
147 LOGE("process[%i]: failed to create code ref %i", proc
->auditInfo
.pid
, status
);
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
));
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
);
166 if (SecCodeCopyPath(proc
->codeRef
, kSecCSDefaultFlags
, &code_url
) == errSecSuccess
) {
167 CFURLGetFileSystemRepresentation(code_url
, true, (UInt8
*)proc
->code_url
, sizeof(proc
->code_url
));
170 if (CFDictionaryGetValueIfPresent(code_info
, kSecCodeInfoIdentifier
, &value
)) {
171 if (CFGetTypeID(value
) == CFStringGetTypeID()) {
172 proc
->code_identifier
= _copy_cf_string(value
, NULL
);
177 if (CFDictionaryGetValueIfPresent(code_info
, kSecCodeInfoEntitlementsDict
, &value
)) {
178 if (CFGetTypeID(value
) == CFDictionaryGetTypeID()) {
179 proc
->code_entitlements
= CFDictionaryCreateCopy(kCFAllocatorDefault
, value
);
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
);
191 CFReleaseSafe(secRequirementRef
);
193 LOGV("process[%i]: created (sid=%i) %s %p", proc
->auditInfo
.pid
, proc
->auditInfo
.asid
, proc
->code_url
, proc
);
196 CFReleaseSafe(code_info
);
197 CFReleaseSafe(code_url
);
202 process_get_key(process_t proc
)
204 return &proc
->auditInfo
;
208 process_get_uid(process_t proc
)
210 return proc
? proc
->auditInfo
.euid
: (uid_t
)-2;
214 process_get_pid(process_t proc
)
216 return proc
? proc
->auditInfo
.pid
: -1;
219 int32_t process_get_generation(process_t proc
)
221 return proc
->auditInfo
.tid
;
225 process_get_session_id(process_t proc
)
227 return proc
? proc
->auditInfo
.asid
: -1;
231 process_get_session(process_t proc
)
233 return proc
->session
;
237 process_get_audit_info(process_t proc
)
239 return &proc
->auditInfo
;
243 process_get_code(process_t proc
)
245 return proc
->codeRef
;
249 process_get_code_url(process_t proc
)
251 return proc
->code_url
;
255 process_add_auth_token(process_t proc
, auth_token_t auth
)
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
);
266 process_remove_auth_token(process_t proc
, auth_token_t auth
, AuthorizationFlags flags
)
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
);
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>
278 (flags
& kAuthorizationFlagDestroyRights
))
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
288 if (CFBagGetCountOfValue(proc
->authTokens
, auth
) == 1) {
289 auth_token_set_state(auth
, auth_token_state_zombie
);
299 CFBagRemoveValue(proc
->authTokens
, auth
);
300 if (!CFBagContainsValue(proc
->authTokens
, auth
)) {
301 auth_token_remove_process(auth
, proc
);
303 if ((count
== 1) && auth_token_check_state(auth
, auth_token_state_registered
)) {
304 server_unregister_auth_token(auth
);
309 // destroy all eligible zombies
310 _destroy_zombie_tokens(proc
);
315 process_find_copy_auth_token(process_t proc
, const AuthorizationBlob
* blob
)
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) {
329 return (auth_token_t
)auth
;
333 process_get_auth_token_count(process_t proc
)
335 __block CFIndex count
= 0;
336 dispatch_sync(proc
->dispatch_queue
, ^{
337 count
= CFBagGetCount(proc
->authTokens
);
343 process_add_connection(process_t proc
, connection_t conn
)
345 __block CFIndex count
= 0;
346 dispatch_sync(proc
->dispatch_queue
, ^{
347 CFSetAddValue(proc
->connections
, conn
);
348 count
= CFSetGetCount(proc
->connections
);
354 process_remove_connection(process_t proc
, connection_t conn
)
356 __block CFIndex count
= 0;
357 dispatch_sync(proc
->dispatch_queue
, ^{
358 CFSetRemoveValue(proc
->connections
, conn
);
359 count
= CFSetGetCount(proc
->connections
);
365 process_get_connection_count(process_t proc
)
367 __block CFIndex count
= 0;
368 dispatch_sync(proc
->dispatch_queue
, ^{
369 count
= CFSetGetCount(proc
->connections
);
375 process_copy_entitlement_value(process_t proc
, const char * entitlement
)
377 CFTypeRef value
= NULL
;
378 require(entitlement
!= NULL
, done
);
380 CFStringRef key
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, entitlement
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
381 if (proc
->code_entitlements
&& key
&& (CFDictionaryGetValueIfPresent(proc
->code_entitlements
, key
, &value
))) {
391 process_has_entitlement(process_t proc
, const char * entitlement
)
393 bool entitled
= false;
394 require(entitlement
!= NULL
, done
);
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
);
410 process_has_entitlement_for_right(process_t proc
, const char * right
)
412 bool entitled
= false;
413 require(right
!= NULL
, done
);
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
);
421 CFIndex count
= CFArrayGetCount(rights
);
422 for (CFIndex i
= 0; i
< count
; i
++) {
423 if (CFEqual(CFArrayGetValueAtIndex(rights
, i
), key
)) {
437 process_get_identifier(process_t proc
)
439 return proc
->code_identifier
;
443 process_get_requirement_data(process_t proc
)
445 return proc
->code_requirement_data
;
449 process_get_requirement(process_t proc
)
451 return proc
->code_requirement
;
454 bool process_verify_requirment(process_t proc
, SecRequirementRef requirment
)
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
);
460 return (status
== errSecSuccess
);
463 // Returns true if the process was signed by B&I or the Mac App Store
464 bool process_apple_signed(process_t proc
) {
465 return proc
->appleSigned
;
468 mach_port_t
process_get_bootstrap(process_t proc
)
470 return proc
->bootstrap
;
473 bool process_set_bootstrap(process_t proc
, mach_port_t bootstrap
)
475 if (bootstrap
!= MACH_PORT_NULL
) {
476 if (proc
->bootstrap
!= MACH_PORT_NULL
) {
477 mach_port_deallocate(mach_task_self(), proc
->bootstrap
);
479 proc
->bootstrap
= bootstrap
;