1/* Copyright (c) 2012 Apple Inc. All rights reserved. */
2
3#include "ccaudit.h"
4#include "debugging.h"
5#include "process.h"
6#include "authtoken.h"
7
8#include <Security/Authorization.h>
9#include <Security/AuthorizationPlugin.h>
10#include <bsm/libbsm.h>
11
12
13struct _ccaudit_s {
14    __AUTH_BASE_STRUCT_HEADER__;
15
16    int fd;
17    int32_t event;
18
19    auth_token_t auth;
20    process_t proc;
21    audit_info_s auditInfo;
22    au_tid_t tid;
23};
24
25static void
26_ccaudit_finalizer(CFTypeRef value)
27{
28    ccaudit_t ccaudit = (ccaudit_t)value;
29
30    CFReleaseSafe(ccaudit->auth);
31    CFReleaseSafe(ccaudit->proc);
32}
33
34AUTH_TYPE_INSTANCE(ccaudit,
35                   .init = NULL,
36                   .copy = NULL,
37                   .finalize = _ccaudit_finalizer,
38                   .equal = NULL,
39                   .hash = NULL,
40                   .copyFormattingDesc = NULL,
41                   .copyDebugDesc = NULL
42                   );
43
44static CFTypeID ccaudit_get_type_id() {
45    static CFTypeID type_id = _kCFRuntimeNotATypeID;
46    static dispatch_once_t onceToken;
47
48    dispatch_once(&onceToken, ^{
49        type_id = _CFRuntimeRegisterClass(&_auth_type_ccaudit);
50    });
51
52    return type_id;
53}
54
55ccaudit_t
56ccaudit_create(process_t proc, auth_token_t auth, int32_t event)
57{
58    ccaudit_t ccaudit = NULL;
59
60    require(auth != NULL, done);
61
62    ccaudit = (ccaudit_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, ccaudit_get_type_id(), AUTH_CLASS_SIZE(ccaudit), NULL);
63    require(ccaudit != NULL, done);
64
65    ccaudit->auth = (auth_token_t)CFRetain(auth);
66    ccaudit->proc = (process_t)CFRetain(proc);
67    ccaudit->fd = -1;
68    ccaudit->event = event;
69
70    ccaudit->auditInfo = *auth_token_get_audit_info(auth);
71    ccaudit->tid.port = ccaudit->auditInfo.tid;
72
73done:
74    return ccaudit;
75}
76
77static bool _enabled()
78{
79    static dispatch_once_t onceToken;
80    static bool enabled = false;
81
82    dispatch_once(&onceToken, ^{
83        int acond = au_get_state();
84        switch (acond) {
85            case AUC_NOAUDIT:
86                break;
87            case AUC_AUDITING:
88                enabled = true;
89                break;
90            default:
91                LOGE("ccaudit: error checking auditing status (%d)", acond);
92        }
93    });
94
95    return enabled;
96}
97
98static bool _open(ccaudit_t ccaudit)
99{
100    if (!_enabled()) {
101        return false;
102    }
103
104    if (-1 != ccaudit->fd)
105        return true;
106
107    if ((ccaudit->fd = au_open()) < 0) {
108        LOGE("ccaudit: au_open() failed (%s)", strerror(errno));
109        return false;
110    }
111
112    return true;
113}
114
115static void _close(ccaudit_t ccaudit)
116{
117    if (-1 != ccaudit->fd) {
118        int err = au_close(ccaudit->fd, AU_TO_WRITE, (short)ccaudit->event);
119        ccaudit->fd = -1;
120        if (err < 0) {
121            LOGE("ccaudit: au_close() failed; record not committed");
122        }
123    }
124}
125
126static bool _write(ccaudit_t ccaudit, token_t * token, const char * name)
127{
128    const char *tokenName = name ?  name : "<unidentified>";
129    if (NULL == token)
130    {
131        LOGE("ccaudit: invalid '%s' token", tokenName);
132        return false;
133    }
134    if (au_write(ccaudit->fd, token) < 0) {
135        LOGE("ccaudit: error writing '%s' token (%s)", tokenName, strerror(errno));
136        return false;
137    }
138    return true;
139}
140
141static bool _subject(ccaudit_t ccaudit)
142{
143    token_t * token = au_to_subject32(ccaudit->auditInfo.auid, ccaudit->auditInfo.euid, ccaudit->auditInfo.egid,
144                                      ccaudit->auditInfo.ruid, ccaudit->auditInfo.rgid, ccaudit->auditInfo.pid, ccaudit->auditInfo.asid, &ccaudit->tid);
145    return _write(ccaudit, token, "subject");
146}
147
148void ccaudit_log_authorization(ccaudit_t ccaudit, const char * right, OSStatus err)
149{
150
151    if (!_open(ccaudit)) {
152        return;
153    }
154    char buf[PATH_MAX+1];
155
156    _subject(ccaudit);
157    _write(ccaudit, au_to_text(right), "right");
158    snprintf(buf, sizeof(buf), "client %s", process_get_code_url(ccaudit->proc));
159    _write(ccaudit, au_to_text(buf), "Authorization client");
160    snprintf(buf, sizeof(buf), "creator %s", auth_token_get_code_url(ccaudit->auth));
161    _write(ccaudit, au_to_text(buf), "Authorization creator");
162
163    if (auth_token_least_privileged(ccaudit->auth)) {
164        _write(ccaudit, au_to_text("least-privilege"), "least-privilege");
165    }
166
167    if (err == errAuthorizationSuccess) {
168        _write(ccaudit, au_to_return32(0, 0), "return");
169    } else {
170        _write(ccaudit, au_to_return32(EPERM, (uint32_t)err), "return");
171    }
172
173    _close(ccaudit);
174}
175
176void ccaudit_log_success(ccaudit_t ccaudit, credential_t cred, const char * right)
177{
178
179    if (!_open(ccaudit)) {
180        return;
181    }
182    char buf[PATH_MAX+1];
183
184    _subject(ccaudit);
185    _write(ccaudit, au_to_text(right), "right");
186    _write(ccaudit, au_to_arg32(1, "known UID ", auth_token_get_uid(ccaudit->auth)), "authenticator");
187    snprintf(buf, sizeof(buf), "authenticated as %s", credential_get_name(cred));
188    _write(ccaudit, au_to_arg32(2, buf, credential_get_uid(cred)), "target");
189    _write(ccaudit, au_to_return32(0, 0), "return");
190
191    _close(ccaudit);
192}
193
194void ccaudit_log_failure(ccaudit_t ccaudit, const char * credName, const char * right)
195{
196
197    if (!_open(ccaudit)) {
198        return;
199    }
200    _subject(ccaudit);
201    _write(ccaudit, au_to_text(right), "right");
202    _write(ccaudit, au_to_arg32(1, "authenticated as ", auth_token_get_uid(ccaudit->auth)), "authenticator");
203
204    if (NULL == credName) {
205        _write(ccaudit, au_to_text("<unknown user>"), "target username");
206    } else {
207        _write(ccaudit, au_to_text(credName), "target username");
208    }
209    _write(ccaudit, au_to_return32(EPERM, (uint32_t)errAuthorizationDenied), "return");
210
211    _close(ccaudit);
212}
213
214void ccaudit_log_mechanism(ccaudit_t ccaudit, const char * right, const char * mech, uint32_t status, const char * interrupted)
215{
216
217    if (!_open(ccaudit)) {
218        return;
219    }
220    char buf[PATH_MAX+1];
221
222    _subject(ccaudit);
223    _write(ccaudit, au_to_text(right), "right");
224    snprintf(buf, sizeof(buf), "mechanism %s", mech);
225    _write(ccaudit, au_to_text(buf), "mechanism");
226
227    if (interrupted) {
228        _write(ccaudit, au_to_text(interrupted), "interrupt");
229    }
230
231    if (status == kAuthorizationResultAllow) {
232        _write(ccaudit, au_to_return32(0, 0), "return");
233    } else {
234        _write(ccaudit, au_to_return32(EPERM, (uint32_t)status), "return");
235    }
236
237    _close(ccaudit);
238}
239
240void ccaudit_log(ccaudit_t ccaudit, const char * right, const char * msg, OSStatus err)
241{
242    if (!_open(ccaudit)) {
243        return;
244    }
245
246    _subject(ccaudit);
247    _write(ccaudit, au_to_text(right), "right");
248
249    if (msg) {
250        _write(ccaudit, au_to_text(msg), "evaluation error");
251    }
252
253    if (err == errAuthorizationSuccess) {
254        _write(ccaudit, au_to_return32(0, 0), "return");
255    } else {
256        _write(ccaudit, au_to_return32(EPERM, (uint32_t)err), "return");
257    }
258
259    _close(ccaudit);
260}
261