1/* Copyright (c) 2012 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
16static Boolean AuthTokenEqualCallBack(const void *value1, const void *value2)
17{
18    return (*(uint64_t*)value1) == (*(uint64_t*)value2);
19}
20
21static 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
33const CFDictionaryKeyCallBacks kAuthTokenKeyCallBacks = {
34    .version = 0,
35    .retain = NULL,
36    .release = NULL,
37    .copyDescription = NULL,
38    .equal = &AuthTokenEqualCallBack,
39    .hash = &AuthTokenHashCallBack
40};
41
42struct _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
70static 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
92static 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
101static 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
109static CFHashCode
110_auth_token_hash(CFTypeRef value)
111{
112    auth_token_t auth = (auth_token_t)value;
113    return *(CFHashCode*)&auth->blob;
114}
115
116AUTH_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
126static 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
137static 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
179done:
180    return auth;
181}
182
183auth_token_t
184auth_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
210done:
211    return auth;
212}
213
214auth_token_t
215auth_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 (%i)", auth->auditInfo.pid, 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
256done:
257    CFReleaseSafe(code_Ref);
258    CFReleaseSafe(code_url);
259    return auth;
260}
261
262bool
263auth_token_get_sandboxed(auth_token_t auth)
264{
265    return auth->sandboxed;
266}
267
268const char *
269auth_token_get_code_url(auth_token_t auth)
270{
271    return auth->code_url;
272}
273
274const void *
275auth_token_get_key(auth_token_t auth)
276{
277    return &auth->blob;
278}
279
280auth_items_t
281auth_token_get_context(auth_token_t auth)
282{
283    return auth->context;
284}
285
286bool
287auth_token_least_privileged(auth_token_t auth)
288{
289    return auth->least_privileged;
290}
291
292uid_t
293auth_token_get_uid(auth_token_t auth)
294{
295    return auth ? auth->auditInfo.euid : (uid_t)-2;
296}
297
298pid_t
299auth_token_get_pid(auth_token_t auth)
300{
301    return auth ? auth->auditInfo.pid : -1;
302}
303
304session_t
305auth_token_get_session(auth_token_t auth)
306{
307    return auth->session;
308}
309
310const AuthorizationBlob *
311auth_token_get_blob(auth_token_t auth)
312{
313    return &auth->blob;
314}
315
316const audit_info_s *
317auth_token_get_audit_info(auth_token_t auth)
318{
319    return &auth->auditInfo;
320}
321
322mach_port_t
323auth_token_get_creator_bootstrap(auth_token_t auth)
324{
325    return auth->creator_bootstrap_port;
326}
327
328CFIndex
329auth_token_add_process(auth_token_t auth, process_t proc)
330{
331    __block CFIndex count = 0;
332    dispatch_sync(auth->dispatch_queue, ^{
333        CFSetAddValue(auth->processes, proc);
334        count = CFSetGetCount(auth->processes);
335    });
336    return count;
337}
338
339CFIndex
340auth_token_remove_process(auth_token_t auth, process_t proc)
341{
342    __block CFIndex count = 0;
343    dispatch_sync(auth->dispatch_queue, ^{
344        if (auth->creator == proc) {
345            auth->creator = NULL;
346        }
347        CFSetRemoveValue(auth->processes, proc);
348        count = CFSetGetCount(auth->processes);
349    });
350    return count;
351}
352
353CFIndex
354auth_token_get_process_count(auth_token_t auth)
355{
356    __block CFIndex count = 0;
357    dispatch_sync(auth->dispatch_queue, ^{
358        count = CFSetGetCount(auth->processes);
359    });
360    return count;
361}
362
363void
364auth_token_set_credential(auth_token_t auth, credential_t cred)
365{
366    dispatch_sync(auth->dispatch_queue, ^{
367        CFSetSetValue(auth->credentials, cred);
368    });
369}
370
371bool
372auth_token_credentials_iterate(auth_token_t auth, credential_iterator_t iter)
373{
374    __block bool result = false;
375
376    dispatch_sync(auth->dispatch_queue, ^{
377        CFIndex count = CFSetGetCount(auth->credentials);
378        CFTypeRef values[count];
379        CFSetGetValues(auth->credentials, values);
380        for (CFIndex i = 0; i < count; i++) {
381            credential_t cred = (credential_t)values[i];
382            result = iter(cred);
383            if (!result) {
384                break;
385            }
386        }
387    });
388
389    return result;
390}
391
392void
393auth_token_set_right(auth_token_t auth, credential_t right)
394{
395    dispatch_sync(auth->dispatch_queue, ^{
396        CFSetSetValue(auth->authorized_rights, right);
397    });
398}
399
400bool
401auth_token_rights_iterate(auth_token_t auth, credential_iterator_t iter)
402{
403    __block bool result = false;
404
405    dispatch_sync(auth->dispatch_queue, ^{
406        CFIndex count = CFSetGetCount(auth->authorized_rights);
407        CFTypeRef values[count];
408        CFSetGetValues(auth->authorized_rights, values);
409        for (CFIndex i = 0; i < count; i++) {
410            credential_t right = (credential_t)values[i];
411            result = iter(right);
412            if (!result) {
413                break;
414            }
415        }
416    });
417
418    return result;
419}
420
421CFTypeRef
422auth_token_copy_entitlement_value(auth_token_t auth, const char * entitlement)
423{
424    __block CFTypeRef value = NULL;
425    dispatch_sync(auth->dispatch_queue, ^{
426        if (auth->creator) {
427            value = process_copy_entitlement_value(auth->creator, entitlement);
428        }
429    });
430
431    return value;
432}
433
434bool
435auth_token_has_entitlement(auth_token_t auth, const char * entitlement)
436{
437    __block bool entitled = false;
438
439    dispatch_sync(auth->dispatch_queue, ^{
440        if (auth->creator) {
441            entitled = process_has_entitlement(auth->creator, entitlement);
442        }
443    });
444
445    return entitled;
446}
447
448bool
449auth_token_has_entitlement_for_right(auth_token_t auth, const char * right)
450{
451    __block bool entitled = false;
452
453    dispatch_sync(auth->dispatch_queue, ^{
454        if (auth->creator) {
455            entitled = process_has_entitlement_for_right(auth->creator, right);
456        }
457    });
458
459    return entitled;
460}
461
462credential_t
463auth_token_get_credential(auth_token_t auth)
464{
465    dispatch_sync(auth->dispatch_queue, ^{
466        if (auth->credential == NULL) {
467            auth->credential = credential_create(auth->auditInfo.euid);
468        }
469    });
470
471    return auth->credential;
472}
473
474bool
475auth_token_apple_signed(auth_token_t auth)
476{
477    return auth->appleSigned;
478}
479
480bool auth_token_is_creator(auth_token_t auth, process_t proc)
481{
482    __block bool creator = false;
483    if (proc) {
484        dispatch_sync(auth->dispatch_queue, ^{
485            if (auth->creator == proc) {
486                creator = true;
487            }
488        });
489    }
490    return creator;
491}
492
493void auth_token_set_state(auth_token_t auth, auth_token_state_t state)
494{
495    auth->state |= state;
496}
497
498void auth_token_clear_state(auth_token_t auth, auth_token_state_t state)
499{
500    auth->state &= ~state;
501}
502
503auth_token_state_t auth_token_get_state(auth_token_t auth)
504{
505    return auth->state;
506}
507
508bool auth_token_check_state(auth_token_t auth, auth_token_state_t state)
509{
510    if (state) {
511        return (auth->state & state) != 0;
512    } else {
513        return auth->state == 0;
514    }
515}
516