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