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