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