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