2 * Copyright (c) 2008 Apple Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
29 * $P4: //depot/projects/trustedbsd/openbsm/libauditd/auditd_lib.c#1 $
32 #include <sys/param.h>
34 #include <config/config.h>
36 #include <sys/dirent.h>
37 #include <sys/mount.h>
38 #include <sys/socket.h>
39 #ifdef HAVE_FULL_QUEUE_H
40 #include <sys/queue.h>
41 #else /* !HAVE_FULL_QUEUE_H */
42 #include <compat/queue.h>
43 #endif /* !HAVE_FULL_QUEUE_H */
48 #include <netinet/in.h>
50 #include <bsm/audit.h>
51 #include <bsm/audit_uevents.h>
52 #include <bsm/auditd_lib.h>
53 #include <bsm/libbsm.h>
67 #ifndef __BSM_INTERNAL_NOTIFY_KEY
68 #define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change"
69 #endif /* __BSM_INTERNAL_NOTIFY_KEY */
70 #endif /* __APPLE__ */
73 * XXX This is temporary until this is moved to <bsm/audit.h> and shared with
76 #ifndef AUDIT_HARD_LIMIT_FREE_BLOCKS
77 #define AUDIT_HARD_LIMIT_FREE_BLOCKS 4
84 TAILQ_ENTRY(dir_ent) dirs;
87 static TAILQ_HEAD(, dir_ent) dir_q;
88 static int minval = -1;
90 static char *auditd_errmsg[] = {
91 "no error", /* ADE_NOERR ( 0) */
92 "could not parse audit_control(5) file", /* ADE_PARSE ( 1) */
93 "auditon(2) failed", /* ADE_AUDITON ( 2) */
94 "malloc(3) failed", /* ADE_NOMEM ( 3) */
95 "all audit log directories over soft limit", /* ADE_SOFTLIM ( 4) */
96 "all audit log directories over hard limit", /* ADE_HARDLIM ( 5) */
97 "could not create file name string", /* ADE_STRERR ( 6) */
98 "could not open audit record", /* ADE_AU_OPEN ( 7) */
99 "could not close audit record", /* ADE_AU_CLOSE ( 8) */
100 "could not set active audit session state", /* ADE_SETAUDIT ( 9) */
101 "auditctl(2) failed (trail still swapped)", /* ADE_ACTL (10) */
102 "auditctl(2) failed (trail not swapped)", /* ADE_ACTLERR (11) */
103 "could not swap audit trail file", /* ADE_SWAPERR (12) */
104 "could not rename crash recovery file", /* ADE_RENAME (13) */
105 "could not read 'current' link file", /* ADE_READLINK (14) */
106 "could not create 'current' link file", /* ADE_SYMLINK (15) */
107 "invalid argument", /* ADE_INVAL (16) */
108 "could not resolve hostname to address", /* ADE_GETADDR (17) */
109 "address family not supported", /* ADE_ADDRFAM (18) */
112 #define MAXERRCODE (sizeof(auditd_errmsg) / sizeof(auditd_errmsg[0]))
114 #define NA_EVENT_STR_SIZE 25
115 #define POL_STR_SIZE 128
119 * Look up and return the error string for the given audit error code.
122 auditd_strerror(int errcode)
126 if (idx < 0 || idx > (int)MAXERRCODE)
127 return ("Invalid auditd error code");
129 return (auditd_errmsg[idx]);
134 * Free our local list of directory names and init list
139 struct dir_ent *d1, *d2;
141 d1 = TAILQ_FIRST(&dir_q);
143 d2 = TAILQ_NEXT(d1, dirs);
152 * Concat the directory name to the given file name.
153 * XXX We should affix the hostname also
156 affixdir(char *name, struct dir_ent *dirent)
161 * Sanity check on file name.
163 if (strlen(name) != (FILENAME_LEN - 1)) {
168 asprintf(&fn, "%s/%s", dirent->dirname, name);
173 * Insert the directory entry in the list by the way they are ordered in
174 * audit_control(5). Move the entries that are over the soft and hard limits
178 insert_orderly(struct dir_ent *denew)
182 TAILQ_FOREACH(dep, &dir_q, dirs) {
183 if (dep->softlim == 1 && denew->softlim == 0) {
184 TAILQ_INSERT_BEFORE(dep, denew, dirs);
187 if (dep->hardlim == 1 && denew->hardlim == 0) {
188 TAILQ_INSERT_BEFORE(dep, denew, dirs);
192 TAILQ_INSERT_TAIL(&dir_q, denew, dirs);
196 * Get the host from audit_control(5) and set it in the audit kernel
197 * information. Return:
198 * ADE_NOERR on success.
199 * ADE_PARSE error parsing audit_control(5).
200 * ADE_AUDITON error getting/setting auditon(2) value.
201 * ADE_GETADDR error getting address info for host.
202 * ADE_ADDRFAM un-supported address family.
205 auditd_set_host(void)
207 char hoststr[MAXHOSTNAMELEN];
208 struct sockaddr_in6 *sin6;
209 struct sockaddr_in *sin;
210 struct addrinfo *res;
211 struct auditinfo_addr aia;
212 int error, ret = ADE_NOERR;
214 if (getachost(hoststr, MAXHOSTNAMELEN) != 0) {
219 * To maintain reverse compatability with older audit_control
220 * files, simply drop a warning if the host parameter has not
221 * been set. However, we will explicitly disable the
222 * generation of extended audit header by passing in a zeroed
225 bzero(&aia, sizeof(aia));
226 aia.ai_termid.at_type = AU_IPv4;
227 error = auditon(A_SETKAUDIT, &aia, sizeof(aia));
228 if (error < 0 && errno != ENOSYS)
232 error = getaddrinfo(hoststr, NULL, NULL, &res);
234 return (ADE_GETADDR);
235 switch (res->ai_family) {
237 sin6 = (struct sockaddr_in6 *) res->ai_addr;
238 bcopy(&sin6->sin6_addr.s6_addr,
239 &aia.ai_termid.at_addr[0], sizeof(struct in6_addr));
240 aia.ai_termid.at_type = AU_IPv6;
244 sin = (struct sockaddr_in *) res->ai_addr;
245 bcopy(&sin->sin_addr.s_addr,
246 &aia.ai_termid.at_addr[0], sizeof(struct in_addr));
247 aia.ai_termid.at_type = AU_IPv4;
251 /* Un-supported address family in host parameter. */
252 errno = EAFNOSUPPORT;
253 return (ADE_ADDRFAM);
256 if (auditon(A_SETKAUDIT, &aia, sizeof(aia)) < 0)
263 * Get the min percentage of free blocks from audit_control(5) and that
264 * value in the kernel. Return:
265 * ADE_NOERR on success,
266 * ADE_PARSE error parsing audit_control(5),
267 * ADE_AUDITON error getting/setting auditon(2) value.
270 auditd_set_minfree(void)
274 if (getacmin(&minval) != 0)
277 if (auditon(A_GETQCTRL, &qctrl, sizeof(qctrl)) != 0)
278 return (ADE_AUDITON);
280 if (qctrl.aq_minfree != minval) {
281 qctrl.aq_minfree = minval;
282 if (auditon(A_SETQCTRL, &qctrl, sizeof(qctrl)) != 0)
283 return (ADE_AUDITON);
290 * Parses the "dir" entry in audit_control(5) into an ordered list. Also, will
291 * set the minfree value if not already set. Arguments include function
292 * pointers to audit_warn functions for soft and hard limits. Returns:
293 * ADE_NOERR on success,
294 * ADE_PARSE error parsing audit_control(5),
295 * ADE_AUDITON error getting/setting auditon(2) value,
296 * ADE_NOMEM error allocating memory,
297 * ADE_SOFTLIM if all the directories are over the soft limit,
298 * ADE_HARDLIM if all the directories are over the hard limit,
301 auditd_read_dirs(int (*warn_soft)(char *), int (*warn_hard)(char *))
303 char cur_dir[MAXNAMLEN];
304 struct dir_ent *dirent;
312 if (minval == -1 && (err = auditd_set_minfree()) != 0)
316 * Init directory q. Force a re-read of the file the next time.
322 * Read the list of directories into an ordered linked list
323 * admin's preference, then those over soft limit and, finally,
324 * those over the hard limit.
326 * XXX We should use the reentrant interfaces once they are
329 while (getacdir(cur_dir, MAXNAMLEN) >= 0) {
330 if (statfs(cur_dir, &sfs) < 0)
331 continue; /* XXX should warn */
332 soft = (sfs.f_bfree < (sfs.f_blocks / (100 / minval))) ? 1 : 0;
333 hard = (sfs.f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) ? 1 : 0;
336 (*warn_soft)(cur_dir);
341 (*warn_hard)(cur_dir);
344 dirent = (struct dir_ent *) malloc(sizeof(struct dir_ent));
347 dirent->softlim = soft;
348 dirent->hardlim = hard;
349 dirent->dirname = (char *) malloc(MAXNAMLEN);
350 if (dirent->dirname == NULL) {
354 strlcpy(dirent->dirname, cur_dir, MAXNAMLEN);
355 insert_orderly(dirent);
360 return (ADE_HARDLIM);
362 return (ADE_SOFTLIM);
367 auditd_close_dirs(void)
375 * Process the audit event file, obtaining a class mapping for each event, and
376 * set that mapping into the kernel. Return:
377 * n number of event mappings that were successfully processed,
378 * ADE_NOMEM if there was an error allocating memory.
381 auditd_set_evcmap(void)
383 au_event_ent_t ev, *evp;
384 au_evclass_map_t evc_map;
389 * XXX There's a risk here that the BSM library will return NULL
390 * for an event when it can't properly map it to a class. In that
391 * case, we will not process any events beyond the one that failed,
392 * but should. We need a way to get a count of the events.
394 ev.ae_name = (char *)malloc(AU_EVENT_NAME_MAX);
395 ev.ae_desc = (char *)malloc(AU_EVENT_DESC_MAX);
396 if ((ev.ae_name == NULL) || (ev.ae_desc == NULL)) {
397 if (ev.ae_name != NULL)
403 * XXXRW: Currently we have no way to remove mappings from the kernel
404 * when they are removed from the file-based mappings.
408 while ((evp = getauevent_r(evp)) != NULL) {
409 evc_map.ec_number = evp->ae_number;
410 evc_map.ec_class = evp->ae_class;
411 if (auditon(A_SETCLASS, &evc_map, sizeof(au_evclass_map_t))
423 * Get the non-attributable event string and set the kernel mask. Return:
424 * ADE_NOERR on success,
425 * ADE_PARSE error parsing audit_control(5),
426 * ADE_AUDITON error setting the mask using auditon(2).
429 auditd_set_namask(void)
432 char naeventstr[NA_EVENT_STR_SIZE];
434 if ((getacna(naeventstr, NA_EVENT_STR_SIZE) != 0) ||
435 (getauditflagsbin(naeventstr, &aumask) != 0))
438 if (auditon(A_SETKMASK, &aumask, sizeof(au_mask_t)))
439 return (ADE_AUDITON);
445 * Set the audit control policy if a policy is configured in audit_control(5),
446 * implement the policy. However, if one isn't defined or if there is an error
447 * parsing the control file, set AUDIT_CNT to avoid leaving the system in a
448 * fragile state. Return:
449 * ADE_NOERR on success,
450 * ADE_PARSE error parsing audit_control(5),
451 * ADE_AUDITON error setting policy using auditon(2).
454 auditd_set_policy(void)
457 char polstr[POL_STR_SIZE];
459 if ((getacpol(polstr, POL_STR_SIZE) != 0) ||
460 (au_strtopol(polstr, &policy) != 0)) {
462 if (auditon(A_SETPOLICY, &policy, sizeof(policy)))
463 return (ADE_AUDITON);
467 if (auditon(A_SETPOLICY, &policy, sizeof(policy)))
468 return (ADE_AUDITON);
474 * Set trail rotation size. Return:
475 * ADE_NOERR on success,
476 * ADE_PARSE error parsing audit_control(5),
477 * ADE_AUDITON error setting file size using auditon(2).
480 auditd_set_fsize(void)
486 * Set trail rotation size.
488 if (getacfilesz(&filesz) != 0)
491 bzero(&au_fstat, sizeof(au_fstat));
492 au_fstat.af_filesz = filesz;
493 if (auditon(A_SETFSIZE, &au_fstat, sizeof(au_fstat)) < 0)
494 return (ADE_AUDITON);
500 * Create the new audit file with appropriate permissions and ownership. Try
501 * to clean up if something goes wrong.
504 open_trail(char *fname, gid_t gid)
508 fd = open(fname, O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP);
511 if (fchown(fd, -1, gid) < 0) {
522 * Create the new audit trail file, swap with existing audit file. Arguments
523 * include timestamp for the filename, a pointer to a string for returning the
524 * new file name, GID for trail file, and audit_warn function pointer for
525 * 'getacdir()' errors. Returns:
526 * ADE_NOERR on success,
527 * ADE_STRERR if the file name string could not be created,
528 * ADE_SWAPERR if the audit trail file could not be swapped,
529 * ADE_ACTL if the auditctl(2) call failed but file swap still
531 * ADE_ACTLERR if the auditctl(2) call failed and file swap failed.
532 * ADE_SYMLINK if symlink(2) failed updating the current link.
535 auditd_swap_trail(char *TS, char **newfile, gid_t gid,
536 int (*warn_getacdir)(char *))
538 char timestr[FILENAME_LEN];
540 struct dir_ent *dirent;
545 if (strlen(TS) != (TIMESTAMP_LEN - 1) ||
546 snprintf(timestr, FILENAME_LEN, "%s.%s", TS, NOT_TERMINATED) < 0) {
551 /* Try until we succeed. */
552 while ((dirent = TAILQ_FIRST(&dir_q))) {
555 if ((fn = affixdir(timestr, dirent)) == NULL)
559 * Create and open the file; then close and pass to the
560 * kernel if all went well.
562 fd = open_trail(fn, gid);
564 error = auditctl(fn);
567 * auditctl failed setting log file.
580 * auditctl() failed but still
581 * successful. Return errno and "soft"
592 * Tell the administrator about lack of permissions for dir.
594 if (warn_getacdir != NULL)
595 (*warn_getacdir)(dirent->dirname);
599 return (ADE_ACTLERR);
601 return (ADE_SWAPERR);
605 * Mask calling process from being audited. Returns:
606 * ADE_NOERR on success,
607 * ADE_SETAUDIT if setaudit(2) fails.
610 auditd_prevent_audit(void)
615 * To prevent event feedback cycles and avoid audit becoming stalled if
616 * auditing is suspended we mask this processes events from being
617 * audited. We allow the uid, tid, and mask fields to be implicitly
618 * set to zero, but do set the audit session ID to the PID.
620 * XXXRW: Is there more to it than this?
622 bzero(&ai, sizeof(ai));
623 ai.ai_asid = getpid();
624 if (setaudit(&ai) != 0)
625 return (ADE_SETAUDIT);
630 * Generate and submit audit record for audit startup or shutdown. The event
631 * argument can be AUE_audit_recovery, AUE_audit_startup or
632 * AUE_audit_shutdown. The path argument will add a path token, if not NULL.
634 * AUE_NOERR on success,
635 * ADE_NOMEM if memory allocation fails,
636 * ADE_AU_OPEN if au_open(3) fails,
637 * ADE_AU_CLOSE if au_close(3) fails.
640 auditd_gen_record(int event, char *path)
647 struct auditinfo_addr aia;
649 if (event == AUE_audit_startup)
650 asprintf(&autext, "%s::Audit startup", getprogname());
651 else if (event == AUE_audit_shutdown)
652 asprintf(&autext, "%s::Audit shutdown", getprogname());
653 else if (event == AUE_audit_recovery)
654 asprintf(&autext, "%s::Audit recovery", getprogname());
660 if ((aufd = au_open()) == -1) {
662 return (ADE_AU_OPEN);
664 bzero(&aia, sizeof(aia));
665 uid = getuid(); pid = getpid();
666 if ((tok = au_to_subject32_ex(uid, geteuid(), getegid(), uid, getgid(),
667 pid, pid, &aia.ai_termid)) != NULL)
669 if ((tok = au_to_text(autext)) != NULL)
672 if (path != NULL && (tok = au_to_path(path)) != NULL)
674 if ((tok = au_to_return32(0, 0)) != NULL)
676 if (au_close(aufd, 1, event) == -1)
677 return (ADE_AU_CLOSE);
683 * Check for a 'current' symlink and do crash recovery, if needed. Create a new
684 * 'current' symlink. The argument 'curfile' is the file the 'current' symlink
685 * should point to. Returns:
686 * ADE_NOERR on success,
687 * ADE_AU_OPEN if au_open(3) fails,
688 * ADE_AU_CLOSE if au_close(3) fails.
689 * ADE_RENAME if error renaming audit trail file,
690 * ADE_READLINK if error reading the 'current' link,
691 * ADE_SYMLINK if error creating 'current' link.
694 auditd_new_curlink(char *curfile)
700 char recoveredname[MAXPATHLEN];
701 char newname[MAXPATHLEN];
704 * Check to see if audit was shutdown properly. If not, clean up,
705 * recover previous audit trail file, and generate audit record.
707 len = readlink(AUDIT_CURRENT_LINK, recoveredname, MAXPATHLEN - 1);
709 /* 'current' exist but is it pointing at a valid file? */
710 recoveredname[len++] = '\0';
711 if (stat(recoveredname, &sb) == 0) {
712 /* Yes, rename it to a crash recovery file. */
713 strlcpy(newname, recoveredname, MAXPATHLEN);
715 if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) {
716 strlcpy(ptr, CRASH_RECOVERY, TIMESTAMP_LEN);
717 if (rename(recoveredname, newname) != 0)
725 /* 'current' symlink is (now) invalid so remove it. */
726 (void) unlink(AUDIT_CURRENT_LINK);
728 /* Note the crash recovery in current audit trail */
729 err = auditd_gen_record(AUE_audit_recovery, path);
734 if (len < 0 && errno != ENOENT)
735 return (ADE_READLINK);
737 if (symlink(curfile, AUDIT_CURRENT_LINK) != 0)
738 return (ADE_SYMLINK);
744 * Do just what we need to quickly start auditing. Assume no system logging or
750 audit_quick_start(void)
755 char TS[TIMESTAMP_LEN];
758 * Mask auditing of this process.
760 if (auditd_prevent_audit() != 0)
764 * Read audit_control and get log directories.
766 err = auditd_read_dirs(NULL, NULL);
767 if (err != ADE_NOERR && err != ADE_SOFTLIM)
771 * Create a new audit trail log.
773 if (getTSstr(tt, TS, TIMESTAMP_LEN) != 0)
775 err = auditd_swap_trail(TS, &newfile, getgid(), NULL);
776 if (err != ADE_NOERR && err != ADE_ACTL)
780 * Add the current symlink and recover from crash, if needed.
782 if (auditd_new_curlink(newfile) != 0)
786 * At this point auditing has started so generate audit start-up record.
788 if (auditd_gen_record(AUE_audit_startup, NULL) != 0)
792 * Configure the audit controls.
794 (void) auditd_set_evcmap();
795 (void) auditd_set_namask();
796 (void) auditd_set_policy();
797 (void) auditd_set_fsize();
798 (void) auditd_set_minfree();
799 (void) auditd_set_host();
805 * Shut down auditing quickly. Assumes that is only called on system shutdown.
811 audit_quick_stop(void)
817 char oldname[MAXPATHLEN];
818 char newname[MAXPATHLEN];
819 char TS[TIMESTAMP_LEN];
822 * Auditing already disabled?
824 if (auditon(A_GETCOND, &cond, sizeof(cond)) < 0)
826 if (cond == AUC_DISABLED)
830 * Generate audit shutdown record.
832 (void) auditd_gen_record(AUE_audit_shutdown, NULL);
835 * Shutdown auditing in the kernel.
838 if (auditon(A_SETCOND, &cond, sizeof(cond)) != 0)
840 #ifdef __BSM_INTERNAL_NOTIFY_KEY
841 notify_post(__BSM_INTERNAL_NOTIFY_KEY);
845 * Rename last audit trail and remove 'current' link.
847 len = readlink(AUDIT_CURRENT_LINK, oldname, MAXPATHLEN - 1);
850 oldname[len++] = '\0';
852 if (getTSstr(tt, TS, TIMESTAMP_LEN) != 0)
855 strlcpy(newname, oldname, len);
857 if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) {
858 strlcpy(ptr, TS, TIMESTAMP_LEN);
859 if (rename(oldname, newname) != 0)
864 (void) unlink(AUDIT_CURRENT_LINK);