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