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