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