1/*
2 *  ccaudit_extensions.cpp
3 *  securityd
4 *
5 *  Created by G H on 3/24/09.
6 *  Copyright (c) 2009 Apple Inc. All Rights Reserved.
7 *
8 */
9
10#include <errno.h>
11#include <assert.h>
12#include <stdio.h>                  // vsnprintf()
13#include <stdarg.h>                 // va_start(), et al.
14#include <syslog.h>
15#include <string.h>                 // memcpy()
16#include <bsm/audit_uevents.h>      // AUE_ssauth*
17#include <bsm/libbsm.h>
18#include <security_utilities/errors.h>
19#include <security_utilities/ccaudit.h>
20#include "ccaudit_extensions.h"
21
22namespace Security
23{
24
25namespace CommonCriteria
26{
27
28namespace Securityd
29{
30
31//
32// AuditLogger
33//
34AuditLogger::AuditLogger(const audit_token_t *srcToken, short auEvent)
35    : mAuditFd(-1), mEvent(auEvent), mClientInfoSet(false)
36{
37    setClientInfo(srcToken);
38}
39
40AuditLogger::AuditLogger(const AuditToken &srcToken, short auEvent)
41    : mAuditFd(-1), mEvent(auEvent), mClientInfoSet(false)
42{
43    setClientInfo(srcToken);
44}
45
46AuditLogger::~AuditLogger()
47{
48    close();
49}
50
51bool
52AuditLogger::open()
53{
54    if (-1 != mAuditFd)
55        return true;
56
57    // @@@  use audit_get_cond() when it's available
58    int acond = au_get_state();
59    switch (acond)
60    {
61        case AUC_NOAUDIT:
62            return false;
63        case AUC_AUDITING:
64            break;
65        default:
66            logInternalError("error checking auditing status (%d)", acond);
67            UnixError::throwMe(acond);  // assume it's a Unix error
68    }
69    if ((mAuditFd = au_open()) < 0)
70    {
71        logInternalError("au_open() failed (%s)", strerror(errno));
72        UnixError::throwMe(errno);
73    }
74    return true;
75}
76
77void
78AuditLogger::close(bool writeLog/* = true*/)
79{
80    if (-1 != mAuditFd)
81    {
82        int keep = writeLog == true ?  AU_TO_WRITE : AU_TO_NO_WRITE;
83        int error = au_close(mAuditFd, keep, mEvent);
84        mAuditFd = -1;
85        if (writeLog == true && error < 0)
86        {
87            logInternalError("au_close() failed; record not committed");
88            UnixError::throwMe(error);
89        }
90    }
91}
92
93void
94AuditLogger::setClientInfo(const audit_token_t *srcToken)
95{
96    assert(srcToken);
97    audit_token_to_au32(*srcToken, &mAuditId, &mEuid, &mEgid, &mRuid, &mRgid, &mPid, &mAuditSessionId, &mOldTerminalId);
98
99    mTerminalId.at_type = AU_IPv4;
100    mTerminalId.at_addr[0] = mOldTerminalId.machine;
101    mTerminalId.at_port = mOldTerminalId.port;
102
103    mClientInfoSet = true;
104}
105
106void
107AuditLogger::setClientInfo(const AuditToken &srcToken)
108{
109    mAuditId = srcToken.auditId();
110    mEuid = srcToken.euid();
111    mEgid = srcToken.egid();
112    mRuid = srcToken.ruid();
113    mRgid = srcToken.rgid();
114    mPid = srcToken.pid();
115    mAuditSessionId = srcToken.sessionId();
116    memcpy(&mOldTerminalId, &(srcToken.terminalId()), sizeof(mOldTerminalId));
117
118    mTerminalId.at_type = AU_IPv4;
119    mTerminalId.at_addr[0] = mOldTerminalId.machine;
120    mTerminalId.at_port = mOldTerminalId.port;
121
122    mClientInfoSet = true;
123}
124
125void
126AuditLogger::writeToken(token_t *token, const char *name)
127{
128    const char *tokenName = name ? name : "<unidentified>";
129    if (NULL == token)
130    {
131        logInternalError("Invalid '%s' token", tokenName);
132        close();
133        UnixError::throwMe(EPERM);      // per audit_submit()
134    }
135    if (au_write(mAuditFd, token) < 0)
136    {
137        logInternalError("Error writing '%s' token (%s)", tokenName, strerror(errno));
138        close();
139        UnixError::throwMe(errno);
140    }
141}
142
143void
144AuditLogger::writeSubject()
145{
146    assert(mClientInfoSet);
147
148    token_t *token;
149
150    // @@@  terminal ID is not carried in the audit trailer nowadays, but
151    // this code should be harmless: it replicates the current logic in
152    // audit_submit()
153    if (AU_IPv4 == mTerminalId.at_type)
154        token = au_to_subject32(mAuditId, mEuid, mEgid, mRuid, mRgid, mPid, mAuditSessionId, &mOldTerminalId);
155    else
156        token = au_to_subject_ex(mAuditId, mEuid, mEgid, mRuid, mRgid, mPid, mAuditSessionId, &mTerminalId);
157    writeToken(token, "subject");
158}
159
160void
161AuditLogger::writeReturn(char status, int reterr)
162{
163    writeToken(au_to_return32(status, reterr), "return");
164}
165
166void
167AuditLogger::logSuccess()
168{
169    if (false == open())
170        return;
171    writeCommon();
172    writeReturn(0, 0);
173    close();
174}
175
176void
177AuditLogger::logFailure(const char *errMsg, int errcode)
178{
179    if (false == open())
180        return;
181    writeCommon();
182    if (errMsg)
183        writeToken(au_to_text(errMsg), "evaluation error");
184    writeReturn(EPERM, errcode);
185    close();
186}
187
188// cribbed from audit_submit()
189void
190AuditLogger::logInternalError(const char *fmt, ...)
191{
192    va_list ap;
193    char text[MAX_AUDITSTRING_LEN];
194
195    if (fmt != NULL)
196    {
197        int error = errno;
198        va_start(ap, fmt);
199        (void)vsnprintf(text, MAX_AUDITSTRING_LEN, fmt, ap);
200        va_end(ap);
201        syslog(LOG_AUTH | LOG_ERR, "%s", text);
202        errno = error;
203    }
204}
205
206//
207// KeychainAuthLogger
208//
209const char *KeychainAuthLogger::sysKCAuthStr = "System keychain authorization";
210const char *KeychainAuthLogger::unknownKCStr = "<unknown keychain>";
211const char *KeychainAuthLogger::unknownItemStr = "<unknown item>";
212
213KeychainAuthLogger::KeychainAuthLogger(const audit_token_t *srcToken, short auEvent)
214    : AuditLogger(srcToken, auEvent), mDatabase(unknownKCStr),
215      mItem(unknownItemStr)
216{
217}
218
219KeychainAuthLogger::KeychainAuthLogger(const AuditToken &srcToken, short auEvent)
220    : AuditLogger(srcToken, auEvent), mDatabase(unknownKCStr),
221      mItem(unknownItemStr)
222{
223}
224
225KeychainAuthLogger::KeychainAuthLogger(const audit_token_t *srcToken, short auEvent, const char *database, const char *item)
226    : AuditLogger(srcToken, auEvent)
227{
228    setDbName(database);
229    setItemName(item);
230}
231
232KeychainAuthLogger::KeychainAuthLogger(const AuditToken &srcToken, short auEvent, const char *database, const char *item)
233    : AuditLogger(srcToken, auEvent)
234{
235    setDbName(database);
236    setItemName(item);
237}
238
239void
240KeychainAuthLogger::setDbName(const char *database)
241{
242    mDatabase = database ? database : unknownKCStr;
243}
244
245void
246KeychainAuthLogger::setItemName(const char *item)
247{
248    mItem = item ? item : unknownItemStr;
249}
250
251void
252KeychainAuthLogger::writeCommon()
253{
254    writeSubject();
255    writeToken(au_to_text(sysKCAuthStr), sysKCAuthStr);
256    writeToken(au_to_text(mDatabase.c_str()), "keychain");
257    writeToken(au_to_text(mItem.c_str()), "keychain item");
258}
259
260
261//
262// RightLogger
263//
264const char *RightLogger::unknownRightStr = "<unknown right>";
265
266void
267RightLogger::setRight(const string &rightName)
268{
269    mRight.clear();
270    mRight = rightName;
271}
272
273void
274RightLogger::setRight(const char *rightName)
275{
276    if (rightName)      // NULL bad for string class and au_to_text()
277    {
278        string tmpStr(rightName);   // setRight() takes a string&
279        setRight(tmpStr);
280    }
281}
282
283
284//
285// AuthMechLogger
286//
287const char *AuthMechLogger::unknownMechStr = "<unknown mechanism>";
288const char *AuthMechLogger::mechStr = "mechanism ";
289
290AuthMechLogger::AuthMechLogger(const AuditToken &srcToken, short auEvent)
291    : AuditLogger(srcToken, auEvent), RightLogger(),
292      mEvaluatingMechanism(false), mCurrentMechanism(unknownMechStr)
293{
294}
295
296AuthMechLogger::AuthMechLogger(const audit_token_t *srcToken, short auEvent)
297    : AuditLogger(srcToken, auEvent), RightLogger(),
298      mEvaluatingMechanism(false), mCurrentMechanism(unknownMechStr)
299{
300}
301
302void
303AuthMechLogger::setCurrentMechanism(const char *mech)
304{
305    mCurrentMechanism.clear();
306    if (NULL == mech)
307    {
308        mEvaluatingMechanism = false;
309    }
310    else
311    {
312        mCurrentMechanism = mech;
313        mEvaluatingMechanism = true;
314    }
315}
316
317void
318AuthMechLogger::writeCommon()
319{
320    writeSubject();
321    writeToken(au_to_text(mRight.c_str()), "right");
322    if (true == mEvaluatingMechanism)
323    {
324        string tmpStr = mechStr;    // mechStr includes a trailing space
325        tmpStr += mCurrentMechanism;
326        writeToken(au_to_text(tmpStr.c_str()), "mechanism");
327    }
328}
329
330void
331AuthMechLogger::logInterrupt(const char *msg)
332{
333    if (false == open())
334        return;
335    writeCommon();
336    if (msg)
337        writeToken(au_to_text(msg), "interrupt");
338    writeReturn(0, 0);
339    close();
340}
341
342//
343// RightAuthenticationLogger
344//
345const char *RightAuthenticationLogger::unknownUserStr = "<unknown user>";
346const char *RightAuthenticationLogger::unknownClientStr = "<unknown client>";
347const char *RightAuthenticationLogger::unknownAuthCreatorStr = "<unknown creator>";
348const char *RightAuthenticationLogger::authenticatorStr = "known UID ";
349const char *RightAuthenticationLogger::clientStr = "client ";
350const char *RightAuthenticationLogger::authCreatorStr = "creator ";
351const char *RightAuthenticationLogger::authenticatedAsStr = "authenticated as ";
352const char *RightAuthenticationLogger::leastPrivStr = "least-privilege";
353
354RightAuthenticationLogger::RightAuthenticationLogger(const AuditToken &srcToken, short auEvent)
355    : AuditLogger(srcToken, auEvent), RightLogger()
356{
357}
358
359RightAuthenticationLogger::RightAuthenticationLogger(const audit_token_t *srcToken, short auEvent)
360    : AuditLogger(srcToken, auEvent), RightLogger()
361{
362}
363
364void
365RightAuthenticationLogger::writeCommon()
366{
367    writeSubject();
368    writeToken(au_to_text(mRight.c_str()), "right");
369}
370
371void
372RightAuthenticationLogger::logSuccess(uid_t authenticator, uid_t target, const char *targetName)
373{
374    if (false == open())
375        return;
376    writeCommon();
377
378    // au_to_arg32() is really meant for auditing syscall arguments;
379    // we're slightly abusing it to get descriptive strings for free.
380    writeToken(au_to_arg32(1, authenticatorStr, authenticator), "authenticator");
381    string tmpStr(authenticatedAsStr);
382    // targetName shouldn't be NULL on a successful authentication, but allow
383    // for programmer screwups
384    tmpStr += targetName ? targetName : unknownUserStr;
385    writeToken(au_to_arg32(2, tmpStr.c_str(), target), "target");
386    writeReturn(0, 0);
387    close();
388}
389
390void
391RightAuthenticationLogger::logAuthorizationResult(const char *client, const char *authCreator, int errcode)
392{
393    if (false == open())
394        return;
395    writeCommon();
396    string tmpStr(clientStr);
397    tmpStr += client ? client : unknownClientStr;
398    writeToken(au_to_text(tmpStr.c_str()), "Authorization client");
399    tmpStr.clear();
400    tmpStr = authCreatorStr;
401    tmpStr += authCreator ? authCreator : unknownAuthCreatorStr;
402    writeToken(au_to_text(tmpStr.c_str()), "Authorization creator");
403    if (errAuthorizationSuccess == errcode)
404        writeReturn(0, 0);
405    else
406        writeReturn(EPERM, errcode);
407    close();
408}
409
410void
411RightAuthenticationLogger::logLeastPrivilege(uid_t userId, bool isAuthorizingUser)
412{
413    if (false == open())
414        return;
415    writeCommon();
416    writeToken(au_to_text(leastPrivStr), leastPrivStr);
417    writeReturn(0, 0);
418    close();
419}
420
421void
422RightAuthenticationLogger::logFailure(uid_t authenticator, const char *targetName)
423{
424    if (false == open())
425        return;
426    writeCommon();
427    writeToken(au_to_arg32(1, authenticatorStr, authenticator), "authenticator");
428    if (NULL == targetName)
429        writeToken(au_to_text(unknownUserStr), "target username");
430    else
431        writeToken(au_to_text(targetName), "target username");
432    // @@@  EAUTH more appropriate, but !defined for _POSIX_C_SOURCE
433    writeReturn(EPERM, errAuthorizationDenied);
434    close();
435}
436
437}   // namespace Securityd
438
439}   // namespace CommonCriteria
440
441}   // namespace Security
442