]> git.saurik.com Git - apple/security.git/blob - OSX/authd/process.c
Security-59754.41.1.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 #include <security_utilities/simulatecrash_assert.h>
17
18 AUTHD_DEFINE_LOG
19
20 struct _process_s {
21 __AUTH_BASE_STRUCT_HEADER__;
22
23 audit_info_s auditInfo;
24
25 session_t session;
26
27 CFMutableBagRef authTokens;
28 dispatch_queue_t dispatch_queue;
29
30 CFMutableSetRef connections;
31
32 char code_url[PATH_MAX+1];
33 char * code_identifier;
34 CFDataRef code_requirement_data;
35 SecRequirementRef code_requirement;
36 CFDictionaryRef code_entitlements;
37
38 mach_port_t bootstrap;
39
40 bool appStoreSigned;
41 bool firstPartySigned;
42 };
43
44 static void
45 _unregister_auth_tokens(const void *value, void *context)
46 {
47 auth_token_t auth = (auth_token_t)value;
48 process_t proc = (process_t)context;
49
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);
53 }
54 }
55
56 static void
57 _destroy_zombie_tokens(process_t proc)
58 {
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);
65 }
66 return true;
67 });
68 }
69
70 static void
71 _process_finalize(CFTypeRef value)
72 {
73 process_t proc = (process_t)value;
74
75 os_log_debug(AUTHD_LOG, "process deallocated");
76
77 dispatch_barrier_sync(proc->dispatch_queue, ^{
78 CFBagApplyFunction(proc->authTokens, _unregister_auth_tokens, proc);
79 });
80
81 session_remove_process(proc->session, proc);
82
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;
94 }
95 }
96
97 AUTH_TYPE_INSTANCE(process,
98 .init = NULL,
99 .copy = NULL,
100 .finalize = _process_finalize,
101 .equal = NULL,
102 .hash = NULL,
103 .copyFormattingDesc = NULL,
104 .copyDebugDesc = NULL
105 );
106
107 static CFTypeID process_get_type_id() {
108 static CFTypeID type_id = _kCFRuntimeNotATypeID;
109 static dispatch_once_t onceToken;
110
111 dispatch_once(&onceToken, ^{
112 type_id = _CFRuntimeRegisterClass(&_auth_type_process);
113 });
114
115 return type_id;
116 }
117
118 process_t
119 process_create(const audit_info_s * auditInfo, session_t session)
120 {
121 OSStatus status = errSecSuccess;
122 process_t proc = NULL;
123 CFDictionaryRef code_info = NULL;
124 CFURLRef code_url = NULL;
125 SecCodeRef codeRef = NULL;
126
127 require(session != NULL, done);
128 require(auditInfo != NULL, done);
129
130 proc = (process_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, process_get_type_id(), AUTH_CLASS_SIZE(process), NULL);
131 require(proc != NULL, done);
132
133 proc->auditInfo = *auditInfo;
134
135 proc->session = (session_t)CFRetain(session);
136
137 proc->connections = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL);
138
139 proc->authTokens = CFBagCreateMutable(kCFAllocatorDefault, 0, &kCFTypeBagCallBacks);
140 check(proc->authTokens != NULL);
141
142 proc->dispatch_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
143 check(proc->dispatch_queue != NULL);
144
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));
147 if ( retval <= 0 ) {
148 os_log_error(AUTHD_LOG, "process: PID %d pidpathfailed %d", proc->auditInfo.pid, retval);
149 }
150
151 CFMutableDictionaryRef codeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
152 CFDataRef auditToken = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (UInt8 *)&auditInfo->opaqueToken, sizeof(auditInfo->opaqueToken), kCFAllocatorNull);
153 if (auditToken) {
154 CFDictionarySetValue(codeDict, kSecGuestAttributeAudit, auditToken);
155 CFReleaseNull(auditToken);
156 } else {
157 CFReleaseSafe(codeDict);
158 os_log_error(AUTHD_LOG, "process: PID %d failed to create audit token", proc->auditInfo.pid);
159 CFReleaseNull(proc);
160 goto done;
161 }
162 status = SecCodeCopyGuestWithAttributes(NULL, codeDict, kSecCSDefaultFlags, &codeRef);
163 CFReleaseSafe(codeDict);
164 if (status) {
165 os_log_error(AUTHD_LOG, "process: PID %d failed to create code ref %d", proc->auditInfo.pid, (int)status);
166 CFReleaseNull(proc);
167 goto done;
168 }
169
170 status = SecCodeCopyPath(codeRef, kSecCSDefaultFlags, &code_url);
171 if (status == errSecSuccess) {
172 CFURLGetFileSystemRepresentation(code_url, true, (UInt8*)proc->code_url, sizeof(proc->code_url));
173 } else {
174 os_log_error(AUTHD_LOG, "process: PID %d failed to get path %d", proc->auditInfo.pid, (int)status);
175 }
176
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));
179
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));
182
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);
189 }
190 }
191 value = NULL;
192 }
193
194 if (CFDictionaryGetValueIfPresent(code_info, kSecCodeInfoIdentifier, &value)) {
195 if (CFGetTypeID(value) == CFStringGetTypeID()) {
196 proc->code_identifier = _copy_cf_string(value, NULL);
197 }
198 value = NULL;
199 }
200
201 if (CFDictionaryGetValueIfPresent(code_info, kSecCodeInfoEntitlementsDict, &value)) {
202 if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
203 proc->code_entitlements = CFDictionaryCreateCopy(kCFAllocatorDefault, value);
204 }
205 value = NULL;
206 }
207
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);
217 }
218 status = SecRequirementCreateWithString(appStoreRequirement, kSecCSDefaultFlags, &secRequirementRef);
219 if (status == errSecSuccess) {
220 proc->appStoreSigned = process_verify_requirement(proc, codeRef, secRequirementRef);
221 CFReleaseSafe(secRequirementRef);
222 }
223 os_log_debug(AUTHD_LOG, "process: PID %d created (sid=%i) %{public}s", proc->auditInfo.pid, proc->auditInfo.asid, proc->code_url);
224
225 done:
226 CFReleaseSafe(code_info);
227 CFReleaseSafe(code_url);
228 CFReleaseSafe(codeRef);
229 return proc;
230 }
231
232 const void *
233 process_get_key(process_t proc)
234 {
235 return &proc->auditInfo;
236 }
237
238 uid_t
239 process_get_uid(process_t proc)
240 {
241 assert(proc); // marked non-null
242 return proc->auditInfo.euid;
243 }
244
245 pid_t
246 process_get_pid(process_t proc)
247 {
248 assert(proc); // marked non-null
249 return proc->auditInfo.pid;
250 }
251
252 int32_t process_get_generation(process_t proc)
253 {
254 assert(proc); // marked non-null
255 return proc->auditInfo.tid;
256 }
257
258 session_id_t
259 process_get_session_id(process_t proc)
260 {
261 assert(proc); // marked non-null
262 return proc->auditInfo.asid;
263 }
264
265 session_t
266 process_get_session(process_t proc)
267 {
268 assert(proc); // marked non-null
269 return proc->session;
270 }
271
272 const audit_info_s *
273 process_get_audit_info(process_t proc)
274 {
275 return &proc->auditInfo;
276 }
277
278 const char *
279 process_get_code_url(process_t proc)
280 {
281 return proc->code_url;
282 }
283
284 void
285 process_add_auth_token(process_t proc, auth_token_t auth)
286 {
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);
291 }
292 });
293 }
294
295 void
296 process_remove_auth_token(process_t proc, auth_token_t auth, uint32_t flags)
297 {
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);
302
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>
307 if ((count == 1) ||
308 (flags & kAuthorizationFlagDestroyRights))
309 {
310 destroy = true;
311 goto done;
312 }
313
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
317 if (creator) {
318 if (CFBagGetCountOfValue(proc->authTokens, auth) == 1) {
319 auth_token_set_state(auth, auth_token_state_zombie);
320 } else {
321 destroy = true;
322 }
323 } else {
324 destroy = true;
325 }
326
327 done:
328 if (destroy) {
329 CFBagRemoveValue(proc->authTokens, auth);
330 if (!CFBagContainsValue(proc->authTokens, auth)) {
331 auth_token_remove_process(auth, proc);
332
333 if ((count == 1) && auth_token_check_state(auth, auth_token_state_registered)) {
334 server_unregister_auth_token(auth);
335 }
336 }
337 }
338
339 // destroy all eligible zombies
340 _destroy_zombie_tokens(proc);
341 });
342 }
343
344 auth_token_t
345 process_find_copy_auth_token(process_t proc, const AuthorizationBlob * blob)
346 {
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) {
352 auth = iter;
353 CFRetain(auth);
354 return false;
355 }
356 return true;
357 });
358 });
359 return (auth_token_t)auth;
360 }
361
362 CFIndex
363 process_get_auth_token_count(process_t proc)
364 {
365 __block CFIndex count = 0;
366 dispatch_sync(proc->dispatch_queue, ^{
367 count = CFBagGetCount(proc->authTokens);
368 });
369 return count;
370 }
371
372 CFIndex
373 process_add_connection(process_t proc, connection_t conn)
374 {
375 __block CFIndex count = 0;
376 dispatch_sync(proc->dispatch_queue, ^{
377 CFSetAddValue(proc->connections, conn);
378 count = CFSetGetCount(proc->connections);
379 });
380 return count;
381 }
382
383 CFIndex
384 process_remove_connection(process_t proc, connection_t conn)
385 {
386 __block CFIndex count = 0;
387 dispatch_sync(proc->dispatch_queue, ^{
388 CFSetRemoveValue(proc->connections, conn);
389 count = CFSetGetCount(proc->connections);
390 });
391 return count;
392 }
393
394 CFIndex
395 process_get_connection_count(process_t proc)
396 {
397 __block CFIndex count = 0;
398 dispatch_sync(proc->dispatch_queue, ^{
399 count = CFSetGetCount(proc->connections);
400 });
401 return count;
402 }
403
404 CFTypeRef
405 process_copy_entitlement_value(process_t proc, const char * entitlement)
406 {
407 CFTypeRef value = NULL;
408 require(entitlement != NULL, done);
409
410 CFStringRef key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, entitlement, kCFStringEncodingUTF8, kCFAllocatorNull);
411 if (proc->code_entitlements && key && (CFDictionaryGetValueIfPresent(proc->code_entitlements, key, &value))) {
412 CFRetainSafe(value);
413 }
414 CFReleaseSafe(key);
415
416 done:
417 return value;
418 }
419
420 bool
421 process_has_entitlement(process_t proc, const char * entitlement)
422 {
423 bool entitled = false;
424 require(entitlement != NULL, done);
425
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);
431 }
432 }
433 CFReleaseSafe(key);
434
435 done:
436 return entitled;
437 }
438
439 bool
440 process_has_entitlement_for_right(process_t proc, const char * right)
441 {
442 bool entitled = false;
443 require(right != NULL, done);
444
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);
450
451 CFIndex count = CFArrayGetCount(rights);
452 for (CFIndex i = 0; i < count; i++) {
453 if (CFEqual(CFArrayGetValueAtIndex(rights, i), key)) {
454 entitled = true;
455 break;
456 }
457 }
458 CFReleaseSafe(key);
459 }
460 }
461
462 done:
463 return entitled;
464 }
465
466 const char *
467 process_get_identifier(process_t proc)
468 {
469 return proc->code_identifier;
470 }
471
472 CFDataRef
473 process_get_requirement_data(process_t proc)
474 {
475 return proc->code_requirement_data;
476 }
477
478 SecRequirementRef
479 process_get_requirement(process_t proc)
480 {
481 return proc->code_requirement;
482 }
483
484 bool process_verify_requirement(process_t proc, SecCodeRef codeRef, SecRequirementRef requirment)
485 {
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);
489 }
490 return (status == errSecSuccess);
491 }
492
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);
496 }
497
498 // Returns true if the process was signed by B&I
499 bool process_firstparty_signed(process_t proc) {
500 return proc->firstPartySigned;
501 }
502
503 mach_port_t process_get_bootstrap(process_t proc)
504 {
505 return proc->bootstrap;
506 }
507
508 bool process_set_bootstrap(process_t proc, mach_port_t bootstrap)
509 {
510 if (bootstrap != MACH_PORT_NULL) {
511 if (proc->bootstrap != MACH_PORT_NULL) {
512 mach_port_deallocate(mach_task_self(), proc->bootstrap);
513 }
514 proc->bootstrap = bootstrap;
515 return true;
516 }
517 return false;
518 }
519