]> git.saurik.com Git - apple/security.git/blob - OSX/authd/authtoken.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / authd / authtoken.c
1 /* Copyright (c) 2012-2013 Apple Inc. All Rights Reserved. */
2
3 #include "authtoken.h"
4 #include "authd_private.h"
5 #include "process.h"
6 #include "authitems.h"
7 #include "debugging.h"
8 #include "authutilities.h"
9 #include "server.h"
10
11 #include <CommonCrypto/CommonRandomSPI.h>
12 #include <Security/Authorization.h>
13 #include <Security/SecBase.h>
14 #include <sandbox.h>
15
16 static Boolean AuthTokenEqualCallBack(const void *value1, const void *value2)
17 {
18 return (*(uint64_t*)value1) == (*(uint64_t*)value2);
19 }
20
21 static CFHashCode AuthTokenHashCallBack(const void *value)
22 {
23 // CFHashCode hash;
24 // AuthorizationBlob* blob = (AuthorizationBlob*)value;
25 // hash = blob->data[1];
26 // hash <<= 32;
27 // hash |= blob->data[0];
28 // return hash;
29 //quick 64 bit aligned version
30 return *((CFHashCode*)((AuthorizationBlob*)value)->data);
31 }
32
33 const CFDictionaryKeyCallBacks kAuthTokenKeyCallBacks = {
34 .version = 0,
35 .retain = NULL,
36 .release = NULL,
37 .copyDescription = NULL,
38 .equal = &AuthTokenEqualCallBack,
39 .hash = &AuthTokenHashCallBack
40 };
41
42 struct _auth_token_s {
43 __AUTH_BASE_STRUCT_HEADER__;
44
45 AuthorizationBlob blob;
46 auth_token_state_t state;
47 audit_info_s auditInfo;
48 dispatch_queue_t dispatch_queue;
49
50 CFMutableSetRef processes;
51
52 session_t session;
53 process_t creator; // weak reference, used for entitlement checking
54 mach_port_t creator_bootstrap_port;
55
56 auth_items_t context;
57
58 CFMutableSetRef credentials;
59 CFMutableSetRef authorized_rights;
60
61 bool least_privileged;
62 bool appleSigned;
63
64 bool sandboxed;
65 char * code_url;
66
67 credential_t credential;
68 };
69
70 static void
71 _auth_token_finalize(CFTypeRef value)
72 {
73 auth_token_t auth = (auth_token_t)value;
74 LOGV("authtoken: deallocated %p", auth);
75
76 dispatch_barrier_sync(auth->dispatch_queue, ^{});
77
78 dispatch_release(auth->dispatch_queue);
79 CFReleaseSafe(auth->session);
80 CFReleaseSafe(auth->processes);
81 CFReleaseSafe(auth->context);
82 CFReleaseSafe(auth->credentials);
83 CFReleaseSafe(auth->authorized_rights);
84 free_safe(auth->code_url);
85 CFReleaseSafe(auth->credential);
86
87 if (auth->creator_bootstrap_port != MACH_PORT_NULL) {
88 mach_port_deallocate(mach_task_self(), auth->creator_bootstrap_port);
89 }
90 }
91
92 static Boolean
93 _auth_token_equal(CFTypeRef value1, CFTypeRef value2)
94 {
95 auth_token_t auth1 = (auth_token_t)value1;
96 auth_token_t auth2 = (auth_token_t)value2;
97
98 return memcmp(&auth1->blob, &auth2->blob, sizeof(AuthorizationBlob)) == 0;
99 }
100
101 static CFStringRef
102 _auth_token_copy_description(CFTypeRef value)
103 {
104 auth_token_t auth = (auth_token_t)value;
105 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("auth_token: %p, uid=%i, pid=%i, processes=%li least_privileged=%i"),
106 auth, auth->auditInfo.euid, auth->auditInfo.pid, CFSetGetCount(auth->processes), auth->least_privileged);
107 }
108
109 static CFHashCode
110 _auth_token_hash(CFTypeRef value)
111 {
112 auth_token_t auth = (auth_token_t)value;
113 return *(CFHashCode*)&auth->blob;
114 }
115
116 AUTH_TYPE_INSTANCE(auth_token,
117 .init = NULL,
118 .copy = NULL,
119 .finalize = _auth_token_finalize,
120 .equal = _auth_token_equal,
121 .hash = _auth_token_hash,
122 .copyFormattingDesc = NULL,
123 .copyDebugDesc = _auth_token_copy_description
124 );
125
126 static CFTypeID auth_token_get_type_id() {
127 static CFTypeID type_id = _kCFRuntimeNotATypeID;
128 static dispatch_once_t onceToken;
129
130 dispatch_once(&onceToken, ^{
131 type_id = _CFRuntimeRegisterClass(&_auth_type_auth_token);
132 });
133
134 return type_id;
135 }
136
137 static auth_token_t
138 _auth_token_create(const audit_info_s * auditInfo, bool operateAsLeastPrivileged)
139 {
140 #if __LLP64__
141 __Check_Compile_Time(sizeof(CFHashCode) == sizeof(AuthorizationBlob));
142 #endif
143
144 auth_token_t auth = (auth_token_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_token_get_type_id(), AUTH_CLASS_SIZE(auth_token), NULL);
145 require(auth != NULL, done);
146
147 if (CCRandomCopyBytes(kCCRandomDefault, auth->blob.data, sizeof(auth->blob.data)) != kCCSuccess) {
148 LOGE("authtoken[%i]: failed to generate blob", auditInfo->pid);
149 CFReleaseNull(auth);
150 goto done;
151 }
152
153 auth->context = auth_items_create();
154 auth->auditInfo = *auditInfo;
155 auth->least_privileged = operateAsLeastPrivileged;
156
157 auth->dispatch_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
158 check(auth->dispatch_queue != NULL);
159
160 auth->credentials = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
161 auth->authorized_rights = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
162 auth->processes = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL);
163 auth->creator_bootstrap_port = MACH_PORT_NULL;
164
165 if (sandbox_check(auth->auditInfo.pid, "authorization-right-obtain", SANDBOX_CHECK_NO_REPORT) != 0)
166 auth->sandboxed = true;
167 else
168 auth->sandboxed = false;
169
170 #if DEBUG
171 CFHashCode code = AuthTokenHashCallBack(&auth->blob);
172 if (memcmp(&code, auth->blob.data, sizeof(auth->blob.data)) != 0) {
173 LOGD("authtoken[%i]: blob = %x%01x", auth->auditInfo.pid, auth->blob.data[1], auth->blob.data[0]);
174 LOGD("authtoken[%i]: hash = %lx", auth->auditInfo.pid, code);
175 assert(false);
176 }
177 #endif
178
179 done:
180 return auth;
181 }
182
183 auth_token_t
184 auth_token_create(process_t proc, bool operateAsLeastPrivileged)
185 {
186 auth_token_t auth = NULL;
187 require(proc != NULL, done);
188
189 auth = _auth_token_create(process_get_audit_info(proc), operateAsLeastPrivileged);
190 require(auth != NULL, done);
191
192 auth->creator = proc;
193 auth->session = (session_t)CFRetain(process_get_session(proc));
194 auth->code_url = _copy_string(process_get_code_url(proc));
195 auth->appleSigned = process_apple_signed(proc);
196 auth->creator_bootstrap_port = process_get_bootstrap(proc);
197 // This line grabs a reference to the send right to the bootstrap (our right to send to the bootstrap)
198 // This makes it critical to use the same call in reverse as we are only getting a ref to one right,
199 // but deallocate will free a ref to all 5 rights.
200 if (auth->creator_bootstrap_port != MACH_PORT_NULL) {
201 kern_return_t error_code = mach_port_mod_refs(mach_task_self(), auth->creator_bootstrap_port, MACH_PORT_RIGHT_SEND, 1);
202 if (error_code != KERN_SUCCESS) {
203 // If no reference to the mach port right can be obtained, we don't hold the copy, so mark it NULL again!
204 auth->creator_bootstrap_port = MACH_PORT_NULL;
205 }
206 }
207
208 LOGV("authtoken[%i]: created %p", auth->auditInfo.pid, auth);
209
210 done:
211 return auth;
212 }
213
214 auth_token_t
215 auth_token_create_with_audit_info(const audit_info_s* info, bool operateAsLeastPrivileged)
216 {
217 OSStatus status = errSecSuccess;
218 SecCodeRef code_Ref = NULL;
219 CFURLRef code_url = NULL;
220
221 auth_token_t auth = NULL;
222 require(info != NULL, done);
223
224 auth = _auth_token_create(info, operateAsLeastPrivileged);
225 require(auth != NULL, done);
226
227 auth->session = server_find_copy_session(info->asid, true);
228 if (auth->session == NULL) {
229 LOGV("authtoken[%i]: failed to create session", auth->auditInfo.pid);
230 CFReleaseNull(auth);
231 goto done;
232 }
233
234 CFMutableDictionaryRef codeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
235 CFNumberRef codePid = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &auth->auditInfo.pid);
236 CFDictionarySetValue(codeDict, kSecGuestAttributePid, codePid);
237 status = SecCodeCopyGuestWithAttributes(NULL, codeDict, kSecCSDefaultFlags, &code_Ref);
238 CFReleaseSafe(codeDict);
239 CFReleaseSafe(codePid);
240
241 if (status) {
242 LOGV("authtoken[%i]: failed to create code ref (%d)", auth->auditInfo.pid, (int)status);
243 CFReleaseNull(auth);
244 goto done;
245 }
246
247 if (SecCodeCopyPath(code_Ref, kSecCSDefaultFlags, &code_url) == errSecSuccess) {
248 auth->code_url = calloc(1u, PATH_MAX+1);
249 if (auth->code_url) {
250 CFURLGetFileSystemRepresentation(code_url, true, (UInt8*)auth->code_url, PATH_MAX);
251 }
252 }
253
254 LOGV("authtoken[%i]: created %p for %s", auth->auditInfo.pid, auth, auth->code_url);
255
256 done:
257 CFReleaseSafe(code_Ref);
258 CFReleaseSafe(code_url);
259 return auth;
260 }
261
262 bool
263 auth_token_get_sandboxed(auth_token_t auth)
264 {
265 return auth->sandboxed;
266 }
267
268 const char *
269 auth_token_get_code_url(auth_token_t auth)
270 {
271 return auth->code_url;
272 }
273
274 const void *
275 auth_token_get_key(auth_token_t auth)
276 {
277 return &auth->blob;
278 }
279
280 auth_items_t
281 auth_token_get_context(auth_token_t auth)
282 {
283 return auth->context;
284 }
285
286 bool
287 auth_token_least_privileged(auth_token_t auth)
288 {
289 return auth->least_privileged;
290 }
291
292 uid_t
293 auth_token_get_uid(auth_token_t auth)
294 {
295 assert(auth); // marked non-null
296 return auth->auditInfo.euid;
297 }
298
299 pid_t
300 auth_token_get_pid(auth_token_t auth)
301 {
302 assert(auth); // marked non-null
303 return auth->auditInfo.pid;
304 }
305
306 session_t
307 auth_token_get_session(auth_token_t auth)
308 {
309 return auth->session;
310 }
311
312 const AuthorizationBlob *
313 auth_token_get_blob(auth_token_t auth)
314 {
315 return &auth->blob;
316 }
317
318 const audit_info_s *
319 auth_token_get_audit_info(auth_token_t auth)
320 {
321 return &auth->auditInfo;
322 }
323
324 mach_port_t
325 auth_token_get_creator_bootstrap(auth_token_t auth)
326 {
327 return auth->creator_bootstrap_port;
328 }
329
330 CFIndex
331 auth_token_add_process(auth_token_t auth, process_t proc)
332 {
333 __block CFIndex count = 0;
334 dispatch_sync(auth->dispatch_queue, ^{
335 CFSetAddValue(auth->processes, proc);
336 count = CFSetGetCount(auth->processes);
337 });
338 return count;
339 }
340
341 CFIndex
342 auth_token_remove_process(auth_token_t auth, process_t proc)
343 {
344 __block CFIndex count = 0;
345 dispatch_sync(auth->dispatch_queue, ^{
346 if (auth->creator == proc) {
347 auth->creator = NULL;
348 }
349 CFSetRemoveValue(auth->processes, proc);
350 count = CFSetGetCount(auth->processes);
351 });
352 return count;
353 }
354
355 CFIndex
356 auth_token_get_process_count(auth_token_t auth)
357 {
358 __block CFIndex count = 0;
359 dispatch_sync(auth->dispatch_queue, ^{
360 count = CFSetGetCount(auth->processes);
361 });
362 return count;
363 }
364
365 void
366 auth_token_set_credential(auth_token_t auth, credential_t cred)
367 {
368 dispatch_sync(auth->dispatch_queue, ^{
369 CFSetSetValue(auth->credentials, cred);
370 });
371 }
372
373 bool
374 auth_token_credentials_iterate(auth_token_t auth, credential_iterator_t iter)
375 {
376 __block bool result = false;
377
378 dispatch_sync(auth->dispatch_queue, ^{
379 CFIndex count = CFSetGetCount(auth->credentials);
380 CFTypeRef values[count];
381 CFSetGetValues(auth->credentials, values);
382 for (CFIndex i = 0; i < count; i++) {
383 credential_t cred = (credential_t)values[i];
384 result = iter(cred);
385 if (!result) {
386 break;
387 }
388 }
389 });
390
391 return result;
392 }
393
394 void
395 auth_token_set_right(auth_token_t auth, credential_t right)
396 {
397 dispatch_sync(auth->dispatch_queue, ^{
398 CFSetSetValue(auth->authorized_rights, right);
399 });
400 }
401
402 bool
403 auth_token_rights_iterate(auth_token_t auth, credential_iterator_t iter)
404 {
405 __block bool result = false;
406
407 dispatch_sync(auth->dispatch_queue, ^{
408 CFIndex count = CFSetGetCount(auth->authorized_rights);
409 CFTypeRef values[count];
410 CFSetGetValues(auth->authorized_rights, values);
411 for (CFIndex i = 0; i < count; i++) {
412 credential_t right = (credential_t)values[i];
413 result = iter(right);
414 if (!result) {
415 break;
416 }
417 }
418 });
419
420 return result;
421 }
422
423 CFTypeRef
424 auth_token_copy_entitlement_value(auth_token_t auth, const char * entitlement)
425 {
426 __block CFTypeRef value = NULL;
427 dispatch_sync(auth->dispatch_queue, ^{
428 if (auth->creator) {
429 value = process_copy_entitlement_value(auth->creator, entitlement);
430 }
431 });
432
433 return value;
434 }
435
436 bool
437 auth_token_has_entitlement(auth_token_t auth, const char * entitlement)
438 {
439 __block bool entitled = false;
440
441 dispatch_sync(auth->dispatch_queue, ^{
442 if (auth->creator) {
443 entitled = process_has_entitlement(auth->creator, entitlement);
444 }
445 });
446
447 return entitled;
448 }
449
450 bool
451 auth_token_has_entitlement_for_right(auth_token_t auth, const char * right)
452 {
453 __block bool entitled = false;
454
455 dispatch_sync(auth->dispatch_queue, ^{
456 if (auth->creator) {
457 entitled = process_has_entitlement_for_right(auth->creator, right);
458 }
459 });
460
461 return entitled;
462 }
463
464 credential_t
465 auth_token_get_credential(auth_token_t auth)
466 {
467 dispatch_sync(auth->dispatch_queue, ^{
468 if (auth->credential == NULL) {
469 auth->credential = credential_create(auth->auditInfo.euid);
470 }
471 });
472
473 return auth->credential;
474 }
475
476 bool
477 auth_token_apple_signed(auth_token_t auth)
478 {
479 return auth->appleSigned;
480 }
481
482 bool auth_token_is_creator(auth_token_t auth, process_t proc)
483 {
484 assert(proc); // marked non-null
485 __block bool creator = false;
486 dispatch_sync(auth->dispatch_queue, ^{
487 if (auth->creator == proc) {
488 creator = true;
489 }
490 });
491 return creator;
492 }
493
494 void auth_token_set_state(auth_token_t auth, auth_token_state_t state)
495 {
496 auth->state |= state;
497 }
498
499 void auth_token_clear_state(auth_token_t auth, auth_token_state_t state)
500 {
501 auth->state &= ~state;
502 }
503
504 auth_token_state_t auth_token_get_state(auth_token_t auth)
505 {
506 return auth->state;
507 }
508
509 bool auth_token_check_state(auth_token_t auth, auth_token_state_t state)
510 {
511 if (state) {
512 return (auth->state & state) != 0;
513 } else {
514 return auth->state == 0;
515 }
516 }