1/* Copyright (c) 2012 Apple Inc. All rights reserved. */
2
3#include "session.h"
4#include "process.h"
5#include "debugging.h"
6#include <dispatch/dispatch.h>
7#include <CoreFoundation/CoreFoundation.h>
8
9struct _session_s {
10    __AUTH_BASE_STRUCT_HEADER__;
11
12    CFMutableSetRef credentials;
13    CFMutableSetRef processes;
14    auditinfo_addr_t auditinfo;
15
16    dispatch_queue_t dispatch_queue;
17
18};
19
20static void
21_session_finalize(CFTypeRef value)
22{
23    session_t session = (session_t)value;
24
25    LOGV("session: %i deallocated %p", session->auditinfo.ai_asid, session);
26
27    // make sure queue is empty
28    dispatch_barrier_sync(session->dispatch_queue, ^{});
29
30    dispatch_release(session->dispatch_queue);
31    CFReleaseSafe(session->credentials);
32    CFReleaseSafe(session->processes);
33}
34
35AUTH_TYPE_INSTANCE(session,
36                   .init = NULL,
37                   .copy = NULL,
38                   .finalize = _session_finalize,
39                   .equal = NULL,
40                   .hash = NULL,
41                   .copyFormattingDesc = NULL,
42                   .copyDebugDesc = NULL
43                   );
44
45static CFTypeID session_get_type_id() {
46    static CFTypeID type_id = _kCFRuntimeNotATypeID;
47    static dispatch_once_t onceToken;
48
49    dispatch_once(&onceToken, ^{
50        type_id = _CFRuntimeRegisterClass(&_auth_type_session);
51    });
52
53    return type_id;
54}
55
56session_t
57session_create(session_id_t sid)
58{
59    session_t session = NULL;
60
61    session = (session_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, session_get_type_id(), AUTH_CLASS_SIZE(session), NULL);
62    require(session != NULL, done);
63
64    session->auditinfo.ai_asid = sid;
65
66    if (!session_update(session)) {
67        LOGE("session: failed to get session info");
68    }
69
70    session->dispatch_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
71    check(session->dispatch_queue != NULL);
72
73    session->credentials = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
74    session->processes = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL);
75
76    LOGV("session: %i created (uid=%i) %p", session->auditinfo.ai_asid, session->auditinfo.ai_auid, session);
77
78done:
79    return session;
80}
81
82bool session_update(session_t session)
83{
84    return auditon(A_GETSINFO_ADDR, &session->auditinfo, sizeof(session->auditinfo)) == 0;
85}
86
87uint64_t session_get_attributes(session_t session)
88{
89    session_update(session);
90
91    return session->auditinfo.ai_flags;
92}
93
94static void _set_attributes(session_t session, uint64_t flags)
95{
96    session->auditinfo.ai_flags = flags;
97    int32_t rc = setaudit_addr(&session->auditinfo, sizeof(session->auditinfo));
98    if (rc != 0) {
99        LOGV("session: failed to update session info (%d)", rc);
100    }
101}
102
103void session_set_attributes(session_t session, uint64_t flags)
104{
105    session_update(session);
106    _set_attributes(session,session->auditinfo.ai_flags | flags);
107}
108
109void session_clear_attributes(session_t session, uint64_t flags)
110{
111    session_update(session);
112    _set_attributes(session,session->auditinfo.ai_flags & ~flags);
113}
114
115
116const void *
117session_get_key(session_t session)
118{
119    return &session->auditinfo.ai_asid;
120}
121
122session_id_t
123session_get_id(session_t session)
124{
125    return session ? session->auditinfo.ai_asid : -1;
126}
127
128uid_t
129session_get_uid(session_t session)
130{
131    return session ? session->auditinfo.ai_auid : (uid_t)-2;
132}
133
134CFIndex
135session_add_process(session_t session, process_t proc)
136{
137    __block CFIndex count = 0;
138    dispatch_sync(session->dispatch_queue, ^{
139        CFSetAddValue(session->processes, proc);
140        count = CFSetGetCount(session->processes);
141    });
142    return count;
143}
144
145CFIndex
146session_remove_process(session_t session, process_t proc)
147{
148    __block CFIndex count = 0;
149    dispatch_sync(session->dispatch_queue, ^{
150        CFSetRemoveValue(session->processes, proc);
151        count = CFSetGetCount(session->processes);
152    });
153    return count;
154}
155
156CFIndex
157session_get_process_count(session_t session)
158{
159    __block CFIndex count = 0;
160    dispatch_sync(session->dispatch_queue, ^{
161        count = CFSetGetCount(session->processes);
162    });
163    return count;
164}
165
166void
167session_set_credential(session_t session, credential_t cred)
168{
169    if (!credential_get_valid(cred))
170        return;
171
172    dispatch_sync(session->dispatch_queue, ^{
173        CFSetSetValue(session->credentials, cred);
174    });
175}
176
177void
178session_credentials_purge(session_t session)
179{
180    session_credentials_iterate(session, ^bool(credential_t cred) {
181        if (!credential_get_valid(cred)) {
182            CFSetRemoveValue(session->credentials, cred);
183        }
184        return true;
185    });
186}
187
188bool
189session_credentials_iterate(session_t session, credential_iterator_t iter)
190{
191    __block bool result = false;
192
193    dispatch_sync(session->dispatch_queue, ^{
194        CFIndex count = CFSetGetCount(session->credentials);
195        CFTypeRef values[count];
196        CFSetGetValues(session->credentials, values);
197        for (CFIndex i = 0; i < count; i++) {
198            credential_t cred = (credential_t)values[i];
199            result = iter(cred);
200            if (!result) {
201                break;
202            }
203        }
204    });
205
206
207    return result;
208}
209