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