dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / auditd_plugins / binfile / binfile.c
blob5b3c2fa93e477451cab8591b4b06717ae1edc99a
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24 * write binary audit records directly to a file.
27 #define DEBUG 0
29 #if DEBUG
30 #define DPRINT(x) { (void) fprintf x; }
31 #else
32 #define DPRINT(x)
33 #endif
36 * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close()
37 * implement a replacable library for use by auditd; they are a
38 * project private interface and may change without notice.
42 #include <assert.h>
43 #include <bsm/audit.h>
44 #include <bsm/audit_record.h>
45 #include <bsm/libbsm.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <libintl.h>
49 #include <netdb.h>
50 #include <pthread.h>
51 #include <secdb.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <sys/param.h>
57 #include <sys/types.h>
58 #include <time.h>
59 #include <tzfile.h>
60 #include <unistd.h>
61 #include <sys/vfs.h>
62 #include <limits.h>
63 #include <syslog.h>
64 #include <security/auditd.h>
65 #include <audit_plugin.h>
67 #define AUDIT_DATE_SZ 14
68 #define AUDIT_FNAME_SZ 2 * AUDIT_DATE_SZ + 2 + MAXHOSTNAMELEN
70 /* per-directory status */
71 #define SOFT_SPACE 0 /* minfree or less space available */
72 #define PLENTY_SPACE 1 /* more than minfree available */
73 #define SPACE_FULL 2 /* out of space */
75 #define AVAIL_MIN 50 /* If there are less that this number */
76 /* of blocks avail, the filesystem is */
77 /* presumed full. */
79 #define ALLHARD_DELAY 20 /* Call audit_warn(allhard) every 20 seconds */
81 /* minimum reasonable size in bytes to roll over an audit file */
82 #define FSIZE_MIN 512000
85 * The directory list is a circular linked list. It is pointed into by
86 * activeDir. Each element contains the pointer to the next
87 * element, the directory pathname, a flag for how much space there is
88 * in the directory's filesystem, and a file handle. Since a new
89 * directory list can be created from auditd_plugin_open() while the
90 * current list is in use, activeDir is protected by log_mutex.
92 typedef struct dirlist_s dirlist_t;
93 struct dirlist_s {
94 dirlist_t *dl_next;
95 int dl_space;
96 int dl_flags;
97 char *dl_dirname;
98 char *dl_filename; /* file name (not path) if open */
99 int dl_fd; /* file handle, -1 unless open */
102 * Defines for dl_flags
104 #define SOFT_WARNED 0x0001 /* already did soft warning for this dir */
105 #define HARD_WARNED 0x0002 /* already did hard warning for this dir */
107 #if DEBUG
108 static FILE *dbfp; /* debug file */
109 #endif
111 static pthread_mutex_t log_mutex;
112 static int binfile_is_open = 0;
114 static int minfree = -1;
115 static int minfreeblocks; /* minfree in blocks */
117 static dirlist_t *lastOpenDir = NULL; /* last activeDir */
118 static dirlist_t *activeDir = NULL; /* to be current directory */
119 static dirlist_t *startdir; /* first dir in the ring */
120 static int activeCount = 0; /* number of dirs in the ring */
122 static int openNewFile = 0; /* need to open a new file */
123 static int hung_count = 0; /* count of audit_warn hard */
125 /* flag from audit_plugin_open to audit_plugin_close */
126 static int am_open = 0;
127 /* preferred dir state */
128 static int fullness_state = PLENTY_SPACE;
131 * These are used to implement a maximum size for the auditing
132 * file. binfile_maxsize is set via the 'p_fsize' parameter to the
133 * audit_binfile plugin.
135 static uint_t binfile_cursize = 0;
136 static uint_t binfile_maxsize = 0;
138 static int open_log(dirlist_t *);
140 static void
141 freedirlist(dirlist_t *head)
143 dirlist_t *n1, *n2;
145 * Free up the old directory list if any
147 if (head != NULL) {
148 n1 = head;
149 do {
150 n2 = n1->dl_next;
151 free(n1->dl_dirname);
152 free(n1->dl_filename);
153 free(n1);
154 n1 = n2;
155 } while (n1 != head);
159 dirlist_t *
160 dupdirnode(dirlist_t *node_orig)
162 dirlist_t *node_new;
164 if ((node_new = calloc(1, sizeof (dirlist_t))) == NULL) {
165 return (NULL);
168 if (node_orig->dl_dirname != NULL &&
169 (node_new->dl_dirname = strdup(node_orig->dl_dirname)) == NULL ||
170 node_orig->dl_filename != NULL &&
171 (node_new->dl_filename = strdup(node_orig->dl_filename)) == NULL) {
172 freedirlist(node_new);
173 return (NULL);
176 node_new->dl_next = node_new;
177 node_new->dl_space = node_orig->dl_space;
178 node_new->dl_flags = node_orig->dl_flags;
179 node_new->dl_fd = node_orig->dl_fd;
181 return (node_new);
185 * add to a linked list of directories available for writing
188 static int
189 growauditlist(dirlist_t **listhead, char *dirlist,
190 dirlist_t *endnode, int *count)
192 dirlist_t *node;
193 char *bs, *be;
194 dirlist_t **node_p;
195 char *dirname;
196 char *remainder;
198 DPRINT((dbfp, "binfile: dirlist=%s\n", dirlist));
200 if (*listhead == NULL)
201 node_p = listhead;
202 else
203 node_p = &(endnode->dl_next);
205 node = NULL;
206 while ((dirname = strtok_r(dirlist, ",", &remainder)) != NULL) {
207 dirlist = NULL;
209 DPRINT((dbfp, "binfile: p_dir = %s\n", dirname));
211 (*count)++;
212 node = malloc(sizeof (dirlist_t));
213 if (node == NULL)
214 return (AUDITD_NO_MEMORY);
216 node->dl_flags = 0;
217 node->dl_filename = NULL;
218 node->dl_fd = -1;
219 node->dl_space = PLENTY_SPACE;
221 node->dl_dirname = malloc((unsigned)strlen(dirname) + 1);
222 if (node->dl_dirname == NULL)
223 return (AUDITD_NO_MEMORY);
225 bs = dirname;
226 while ((*bs == ' ') || (*bs == '\t')) /* trim blanks */
227 bs++;
228 be = bs + strlen(bs) - 1;
229 while (be > bs) { /* trim trailing blanks */
230 if ((*bs != ' ') && (*bs != '\t'))
231 break;
232 be--;
234 *(be + 1) = '\0';
235 (void) strlcpy(node->dl_dirname, bs, AUDIT_FNAME_SZ);
237 if (*listhead != NULL)
238 node->dl_next = *listhead;
239 else
240 node->dl_next = node;
241 *node_p = node;
242 node_p = &(node->dl_next);
245 return (0);
249 * create a linked list of directories available for writing
251 * if a list already exists, the two are compared and the new one is
252 * used only if it is different than the old.
254 * returns -2 for new or changed list, 0 for unchanged list and -1 for
255 * error. (Positive returns are for AUDITD_<error code> values)
258 static int
259 loadauditlist(char *dirstr, char *minfreestr)
261 dirlist_t *n1, *n2;
262 dirlist_t *listhead = NULL;
263 dirlist_t *thisdir;
264 int node_count = 0;
265 int rc;
266 int temp_minfree = 0;
268 static dirlist_t *activeList = NULL; /* directory list */
270 DPRINT((dbfp, "binfile: Loading audit list from audit service "
271 "(audit_binfile)\n"));
273 if (dirstr == NULL || minfreestr == NULL) {
274 DPRINT((dbfp, "binfile: internal error"));
275 return (-1);
277 if ((rc = growauditlist(&listhead, dirstr, NULL, &node_count)) != 0) {
278 return (rc);
280 if (node_count == 0) {
282 * there was a problem getting the directory
283 * list or remote host info from the audit_binfile
284 * configuration even though auditd thought there was
285 * at least 1 good entry
287 DPRINT((dbfp, "binfile: "
288 "problem getting directory / libpath list "
289 "from audit_binfile configuration.\n"));
290 return (-1);
293 #if DEBUG
294 /* print out directory list */
295 if (listhead != NULL) {
296 (void) fprintf(dbfp, "Directory list:\n\t%s\n",
297 listhead->dl_dirname);
298 thisdir = listhead->dl_next;
300 while (thisdir != listhead) {
301 (void) fprintf(dbfp, "\t%s\n", thisdir->dl_dirname);
302 thisdir = thisdir->dl_next;
305 #endif /* DEBUG */
307 thisdir = listhead;
309 /* See if the list has changed (rc = 0 if no change, else 1) */
310 rc = 0;
311 if (node_count == activeCount) {
312 n1 = listhead;
313 n2 = activeList;
314 do {
315 if (strcmp(n1->dl_dirname, n2->dl_dirname) != 0) {
316 DPRINT((dbfp,
317 "binfile: new dirname = %s\n"
318 "binfile: old dirname = %s\n",
319 n1->dl_dirname,
320 n2->dl_dirname));
321 rc = -2;
322 break;
324 n1 = n1->dl_next;
325 n2 = n2->dl_next;
326 } while ((n1 != listhead) && (n2 != activeList));
327 } else {
328 DPRINT((dbfp, "binfile: dir counts differs\n"
329 "binfile: old dir count = %d\n"
330 "binfile: new dir count = %d\n",
331 activeCount, node_count));
332 rc = -2;
334 if (rc == -2) {
335 (void) pthread_mutex_lock(&log_mutex);
336 DPRINT((dbfp, "loadauditlist: close / open audit.log(4)\n"));
337 if (open_log(listhead) == 0) {
338 openNewFile = 1; /* try again later */
339 } else {
340 openNewFile = 0;
342 freedirlist(activeList); /* old list */
343 activeList = listhead; /* new list */
344 activeDir = startdir = thisdir;
345 activeCount = node_count;
346 (void) pthread_mutex_unlock(&log_mutex);
347 } else {
348 freedirlist(listhead);
351 /* Get the minfree value. */
352 temp_minfree = atoi(minfreestr);
354 if ((temp_minfree < 0) || (temp_minfree > 100))
355 temp_minfree = 0;
357 if (minfree != temp_minfree) {
358 DPRINT((dbfp, "minfree: old = %d, new = %d\n",
359 minfree, temp_minfree));
360 rc = -2; /* data change */
361 minfree = temp_minfree;
364 return (rc);
368 * getauditdate - get the current time (GMT) and put it in the form
369 * yyyymmddHHMMSS .
371 static void
372 getauditdate(char *date)
374 struct timeval tp;
375 struct timezone tzp;
376 struct tm tm;
378 (void) gettimeofday(&tp, &tzp);
379 tm = *gmtime(&tp.tv_sec);
381 * NOTE: if we want to use gmtime, we have to be aware that the
382 * structure only keeps the year as an offset from TM_YEAR_BASE.
383 * I have used TM_YEAR_BASE in this code so that if they change
384 * this base from 1900 to 2000, it will hopefully mean that this
385 * code does not have to change. TM_YEAR_BASE is defined in
386 * tzfile.h .
388 (void) sprintf(date, "%.4d%.2d%.2d%.2d%.2d%.2d",
389 tm.tm_year + TM_YEAR_BASE, tm.tm_mon + 1, tm.tm_mday,
390 tm.tm_hour, tm.tm_min, tm.tm_sec);
396 * write_file_token - put the file token into the audit log
398 static int
399 write_file_token(int fd, char *name)
401 adr_t adr; /* xdr ptr */
402 struct timeval tv; /* time now */
403 char for_adr[AUDIT_FNAME_SZ + AUDIT_FNAME_SZ]; /* plenty of room */
404 char token_id;
405 short i;
407 (void) gettimeofday(&tv, NULL);
408 i = strlen(name) + 1;
409 adr_start(&adr, for_adr);
410 #ifdef _LP64
411 token_id = AUT_OTHER_FILE64;
412 adr_char(&adr, &token_id, 1);
413 adr_int64(&adr, (int64_t *)& tv, 2);
414 #else
415 token_id = AUT_OTHER_FILE32;
416 adr_char(&adr, &token_id, 1);
417 adr_int32(&adr, (int32_t *)& tv, 2);
418 #endif
420 adr_short(&adr, &i, 1);
421 adr_char(&adr, name, i);
423 if (write(fd, for_adr, adr_count(&adr)) < 0) {
424 DPRINT((dbfp, "binfile: Bad write\n"));
425 return (errno);
427 return (0);
431 * close_log - close the file if open. Also put the name of the
432 * new log file in the trailer, and rename the old file
433 * to oldname. The caller must hold log_mutext while calling
434 * close_log since any change to activeDir is a complete redo
435 * of all it points to.
436 * arguments -
437 * oldname - the new name for the file to be closed
438 * newname - the name of the new log file (for the trailer)
440 static void
441 close_log(dirlist_t **lastOpenDir_ptr, char *oname, char *newname)
443 char auditdate[AUDIT_DATE_SZ+1];
444 char *name;
445 char oldname[AUDIT_FNAME_SZ+1];
446 dirlist_t *currentdir = *lastOpenDir_ptr;
448 if ((currentdir == NULL) || (currentdir->dl_fd == -1))
449 return;
451 * If oldname is blank, we were called by auditd_plugin_close()
452 * instead of by open_log, so we need to update our name.
454 (void) strlcpy(oldname, oname, AUDIT_FNAME_SZ);
456 if (strcmp(oldname, "") == 0) {
457 getauditdate(auditdate);
459 assert(currentdir->dl_filename != NULL);
461 (void) strlcpy(oldname, currentdir->dl_filename,
462 AUDIT_FNAME_SZ);
464 name = strrchr(oldname, '/') + 1;
465 (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate,
466 AUDIT_DATE_SZ);
469 * Write the trailer record and rename and close the file.
470 * If any of the write, rename, or close fail, ignore it
471 * since there is not much else we can do and the next open()
472 * will trigger the necessary full directory logic.
474 * newname is "" if binfile is being closed down.
476 (void) write_file_token(currentdir->dl_fd, newname);
477 if (currentdir->dl_fd >= 0) {
478 (void) fsync(currentdir->dl_fd);
479 (void) close(currentdir->dl_fd);
481 currentdir->dl_fd = -1;
482 (void) rename(currentdir->dl_filename, oldname);
484 DPRINT((dbfp, "binfile: Log closed %s\n", oldname));
486 freedirlist(currentdir);
487 *lastOpenDir_ptr = NULL;
492 * open_log - open a new file in the current directory. If a
493 * file is already open, close it.
495 * return 1 if ok, 0 if all directories are full.
497 * lastOpenDir - used to get the oldfile name (and change it),
498 * to close the oldfile.
500 * The caller must hold log_mutex while calling open_log.
503 static int
504 open_log(dirlist_t *current_dir)
506 char auditdate[AUDIT_DATE_SZ + 1];
507 char oldname[AUDIT_FNAME_SZ + 1] = "";
508 char newname[AUDIT_FNAME_SZ + 1];
509 char *name; /* pointer into oldname */
510 int opened = 0;
511 int error = 0;
512 int newfd = 0;
514 static char host[MAXHOSTNAMELEN + 1] = "";
515 /* previous directory with open log file */
517 if (host[0] == '\0')
518 (void) gethostname(host, MAXHOSTNAMELEN);
520 /* Get a filename which does not already exist */
521 while (!opened) {
522 getauditdate(auditdate);
523 (void) snprintf(newname, AUDIT_FNAME_SZ,
524 "%s/%s.not_terminated.%s",
525 current_dir->dl_dirname, auditdate, host);
526 newfd = open(newname,
527 O_RDWR | O_APPEND | O_CREAT | O_EXCL, 0640);
528 if (newfd < 0) {
529 switch (errno) {
530 case EEXIST:
531 DPRINT((dbfp,
532 "open_log says duplicate for %s "
533 "(will try another)\n", newname));
534 (void) sleep(1);
535 break;
536 case ENOENT: {
537 /* invalid path */
538 char *msg;
539 (void) asprintf(&msg,
540 gettext("No such p_dir: %s\n"),
541 current_dir->dl_dirname);
542 DPRINT((dbfp,
543 "open_log says about %s: %s\n",
544 newname, strerror(errno)));
545 __audit_syslog("audit_binfile.so",
546 LOG_CONS | LOG_NDELAY,
547 LOG_DAEMON, LOG_ERR, msg);
548 free(msg);
549 current_dir = current_dir->dl_next;
550 return (0);
552 default:
553 /* open failed */
554 DPRINT((dbfp,
555 "open_log says full for %s: %s\n",
556 newname, strerror(errno)));
557 current_dir->dl_space = SPACE_FULL;
558 current_dir = current_dir->dl_next;
559 return (0);
560 } /* switch */
561 } else
562 opened = 1;
563 } /* while */
566 * When we get here, we have opened our new log file.
567 * Now we need to update the name of the old file to
568 * store in this file's header. lastOpenDir may point
569 * to current_dir if the list is only one entry long and
570 * there is only one list.
572 if ((lastOpenDir != NULL) && (lastOpenDir->dl_filename != NULL)) {
573 (void) strlcpy(oldname, lastOpenDir->dl_filename,
574 AUDIT_FNAME_SZ);
575 name = (char *)strrchr(oldname, '/') + 1;
577 (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate,
578 AUDIT_DATE_SZ);
580 close_log(&lastOpenDir, oldname, newname);
582 error = write_file_token(newfd, oldname);
583 if (error) {
584 /* write token failed */
585 (void) close(newfd);
587 current_dir->dl_space = SPACE_FULL;
588 current_dir->dl_fd = -1;
589 free(current_dir->dl_filename);
590 current_dir->dl_filename = NULL;
591 current_dir = current_dir->dl_next;
592 return (0);
593 } else {
594 if (current_dir->dl_filename != NULL) {
595 free(current_dir->dl_filename);
597 current_dir->dl_filename = strdup(newname);
598 current_dir->dl_fd = newfd;
600 if (lastOpenDir == NULL) {
601 freedirlist(lastOpenDir);
602 if ((lastOpenDir = dupdirnode(current_dir)) == NULL) {
603 __audit_syslog("audit_binfile.so",
604 LOG_CONS | LOG_NDELAY,
605 LOG_DAEMON, LOG_ERR, gettext("no memory"));
606 return (0);
608 DPRINT((dbfp, "open_log created new lastOpenDir "
609 "(%s, %s [fd: %d])\n",
610 lastOpenDir->dl_dirname == NULL ? "" :
611 lastOpenDir->dl_dirname,
612 lastOpenDir->dl_filename == NULL ? "" :
613 lastOpenDir->dl_filename, lastOpenDir->dl_fd));
617 * New file opened, so reset file size statistic (used
618 * to ensure audit log does not grow above size limit
619 * set by p_fsize).
621 binfile_cursize = 0;
623 (void) __logpost(newname);
625 DPRINT((dbfp, "binfile: Log opened: %s\n", newname));
626 return (1);
630 #define IGNORE_SIZE 8192
632 * spacecheck - determine whether the given directory's filesystem
633 * has the at least the space requested. Also set the space
634 * value in the directory list structure. If the caller
635 * passes other than PLENTY_SPACE or SOFT_SPACE, the caller should
636 * ignore the return value. Otherwise, 0 = less than the
637 * requested space is available, 1 = at least the requested space
638 * is available.
640 * log_mutex must be held by the caller
642 * -1 is returned if stat fails
644 * IGNORE_SIZE is one page (Sol 9 / 10 timeframe) and is the default
645 * buffer size written for Sol 9 and earlier. To keep the same accuracy
646 * for the soft limit check as before, spacecheck checks for space
647 * remaining IGNORE_SIZE bytes. This reduces the number of statvfs()
648 * calls and related math.
650 * globals -
651 * minfree - the soft limit, i.e., the % of filesystem to reserve
653 static int
654 spacecheck(dirlist_t *thisdir, int test_limit, size_t next_buf_size)
656 struct statvfs sb;
657 static int ignore_size = 0;
659 ignore_size += next_buf_size;
661 if ((test_limit == PLENTY_SPACE) && (ignore_size < IGNORE_SIZE))
662 return (1);
664 assert(thisdir != NULL);
666 if (statvfs(thisdir->dl_dirname, &sb) < 0) {
667 thisdir->dl_space = SPACE_FULL;
668 minfreeblocks = AVAIL_MIN;
669 return (-1);
670 } else {
671 minfreeblocks = ((minfree * sb.f_blocks) / 100) + AVAIL_MIN;
673 if (sb.f_bavail < AVAIL_MIN)
674 thisdir->dl_space = SPACE_FULL;
675 else if (sb.f_bavail > minfreeblocks) {
676 thisdir->dl_space = fullness_state = PLENTY_SPACE;
677 ignore_size = 0;
678 } else
679 thisdir->dl_space = SOFT_SPACE;
681 if (thisdir->dl_space == PLENTY_SPACE)
682 return (1);
684 return (thisdir->dl_space == test_limit);
688 * Parses p_fsize value and contains it within the range FSIZE_MIN and
689 * INT_MAX so using uints won't cause an undetected overflow of
690 * INT_MAX. Defaults to 0 if the value is invalid or is missing.
692 static void
693 save_maxsize(char *maxsize)
696 * strtol() returns a long which could be larger than int so
697 * store here for sanity checking first
699 long proposed_maxsize;
701 if (maxsize != NULL) {
703 * There is no explicit error return from strtol() so
704 * we may need to depend on the value of errno.
706 errno = 0;
707 proposed_maxsize = strtol(maxsize, (char **)NULL, 10);
710 * If sizeof(long) is greater than sizeof(int) on this
711 * platform, proposed_maxsize might be greater than
712 * INT_MAX without it being reported as ERANGE.
714 if ((errno == ERANGE) ||
715 ((proposed_maxsize != 0) &&
716 (proposed_maxsize < FSIZE_MIN)) ||
717 (proposed_maxsize > INT_MAX)) {
718 binfile_maxsize = 0;
719 DPRINT((dbfp, "binfile: p_fsize parameter out of "
720 "range: %s\n", maxsize));
722 * Inform administrator of the error via
723 * syslog
725 __audit_syslog("audit_binfile.so",
726 LOG_CONS | LOG_NDELAY,
727 LOG_DAEMON, LOG_ERR,
728 gettext("p_fsize parameter out of range\n"));
729 } else {
730 binfile_maxsize = proposed_maxsize;
732 } else { /* p_fsize string was not present */
733 binfile_maxsize = 0;
736 DPRINT((dbfp, "binfile: set maxsize to %d\n", binfile_maxsize));
740 * auditd_plugin() writes a buffer to the currently open file. The
741 * global "openNewFile" is used to force a new log file for cases such
742 * as the initial open, when minfree is reached, the p_fsize value is
743 * exceeded or the current file system fills up, and "audit -s" with
744 * changed parameters. For "audit -n" a new log file is opened
745 * immediately in auditd_plugin_open().
747 * This function manages one or more audit directories as follows:
749 * If the current open file is in a directory that has not
750 * reached the soft limit, write the input data and return.
752 * Scan the list of directories for one which has not reached
753 * the soft limit; if one is found, write and return. Such
754 * a writable directory is in "PLENTY_SPACE" state.
756 * Scan the list of directories for one which has not reached
757 * the hard limit; if one is found, write and return. This
758 * directory in in "SOFT_SPACE" state.
760 * Oh, and if a write fails, handle it like a hard space limit.
762 * audit_warn (via __audit_dowarn()) is used to alert an operator
763 * at various levels of fullness.
765 /* ARGSUSED */
766 auditd_rc_t
767 auditd_plugin(const char *input, size_t in_len, uint64_t sequence, char **error)
769 auditd_rc_t rc = AUDITD_FAIL;
770 int open_status;
771 size_t out_len;
772 /* avoid excess audit_warnage */
773 static int allsoftfull_warning = 0;
774 static int allhard_pause = 0;
775 static struct timeval next_allhard;
776 struct timeval now;
777 #if DEBUG
778 int statrc;
779 static char *last_file_written_to = NULL;
780 static uint64_t last_sequence = 0;
781 static uint64_t write_count = 0;
783 if ((last_sequence > 0) && (sequence != last_sequence + 1))
784 (void) fprintf(dbfp,
785 "binfile: buffer sequence=%llu but prev=%llu=n",
786 sequence, last_sequence);
787 last_sequence = sequence;
789 (void) fprintf(dbfp, "binfile: input seq=%llu, len=%d\n",
790 sequence, in_len);
791 #endif
792 *error = NULL;
794 * lock is for activeDir, referenced by open_log() and close_log()
796 (void) pthread_mutex_lock(&log_mutex);
799 * If this would take us over the maximum size, open a new
800 * file, unless maxsize is 0, in which case growth of the
801 * audit log is unrestricted.
803 if ((binfile_maxsize != 0) &&
804 ((binfile_cursize + in_len) > binfile_maxsize)) {
805 DPRINT((dbfp, "binfile: maxsize exceeded, opening new audit "
806 "file.\n"));
807 openNewFile = 1;
810 while (rc == AUDITD_FAIL) {
811 open_status = 1;
812 if (openNewFile) {
813 open_status = open_log(activeDir);
814 if (open_status == 1) /* ok */
815 openNewFile = 0;
818 * consider "space ok" return and error return the same;
819 * a -1 means spacecheck couldn't check for space.
821 #if !DEBUG
822 if ((open_status == 1) &&
823 (spacecheck(activeDir, fullness_state, in_len) != 0)) {
824 #else
825 if ((open_status == 1) &&
826 (statrc = spacecheck(activeDir, fullness_state,
827 in_len) != 0)) {
828 DPRINT((dbfp, "binfile: returned from spacecheck\n"));
830 * The last copy of last_file_written_to is
831 * never free'd, so there will be one open
832 * memory reference on exit. It's debug only.
834 if ((last_file_written_to != NULL) &&
835 (strcmp(last_file_written_to,
836 activeDir->dl_filename) != 0)) {
837 DPRINT((dbfp, "binfile: now writing to %s\n",
838 activeDir->dl_filename));
839 free(last_file_written_to);
841 DPRINT((dbfp, "binfile: finished some debug stuff\n"));
842 last_file_written_to =
843 strdup(activeDir->dl_filename);
844 #endif
845 out_len = write(activeDir->dl_fd, input, in_len);
846 DPRINT((dbfp, "binfile: finished the write\n"));
848 binfile_cursize += out_len;
850 if (out_len == in_len) {
851 DPRINT((dbfp,
852 "binfile: write_count=%llu, sequence=%llu,"
853 " l=%u\n",
854 ++write_count, sequence, out_len));
855 allsoftfull_warning = 0;
856 activeDir->dl_flags = 0;
858 rc = AUDITD_SUCCESS;
859 break;
860 } else if (!(activeDir->dl_flags & HARD_WARNED)) {
861 DPRINT((dbfp,
862 "binfile: write failed, sequence=%llu, "
863 "l=%u\n", sequence, out_len));
864 DPRINT((dbfp, "hard warning sent.\n"));
865 __audit_dowarn("hard", activeDir->dl_dirname,
868 activeDir->dl_flags |= HARD_WARNED;
870 } else {
871 DPRINT((dbfp, "binfile: statrc=%d, fullness_state=%d\n",
872 statrc, fullness_state));
873 if (!(activeDir->dl_flags & SOFT_WARNED) &&
874 (activeDir->dl_space == SOFT_SPACE)) {
875 DPRINT((dbfp, "soft warning sent\n"));
876 __audit_dowarn("soft",
877 activeDir->dl_dirname, 0);
878 activeDir->dl_flags |= SOFT_WARNED;
880 if (!(activeDir->dl_flags & HARD_WARNED) &&
881 (activeDir->dl_space == SPACE_FULL)) {
882 DPRINT((dbfp, "hard warning sent.\n"));
883 __audit_dowarn("hard",
884 activeDir->dl_dirname, 0);
885 activeDir->dl_flags |= HARD_WARNED;
888 DPRINT((dbfp, "binfile: activeDir=%s, next=%s\n",
889 activeDir->dl_dirname, activeDir->dl_next->dl_dirname));
891 activeDir = activeDir->dl_next;
892 openNewFile = 1;
894 if (activeDir == startdir) { /* full circle */
895 if (fullness_state == PLENTY_SPACE) { /* once */
896 fullness_state = SOFT_SPACE;
897 if (allsoftfull_warning == 0) {
898 allsoftfull_warning++;
899 __audit_dowarn("allsoft", "", 0);
901 } else { /* full circle twice */
902 if ((hung_count > 0) && !allhard_pause) {
903 allhard_pause = 1;
904 (void) gettimeofday(&next_allhard,
905 NULL);
906 next_allhard.tv_sec += ALLHARD_DELAY;
909 if (allhard_pause) {
910 (void) gettimeofday(&now, NULL);
911 if (now.tv_sec >= next_allhard.tv_sec) {
912 allhard_pause = 0;
913 __audit_dowarn("allhard", "",
914 ++hung_count);
916 } else {
917 __audit_dowarn("allhard", "",
918 ++hung_count);
920 minfreeblocks = AVAIL_MIN;
921 rc = AUDITD_RETRY;
922 *error = strdup(gettext(
923 "all partitions full\n"));
924 (void) __logpost("");
928 (void) pthread_mutex_unlock(&log_mutex);
930 return (rc);
935 * It may be called multiple times as auditd handles SIGHUP and SIGUSR1
936 * corresponding to the audit(1M) flags -s and -n
938 * kvlist is NULL only if auditd caught a SIGUSR1 (audit -n), so after the first
939 * time open is called; the reason is -s if kvlist != NULL and -n otherwise.
942 auditd_rc_t
943 auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error)
945 int rc = 0;
946 int status;
947 int reason;
948 char *dirlist;
949 char *minfree;
950 char *maxsize;
951 kva_t *kv;
953 *error = NULL;
954 *ret_list = NULL;
955 kv = (kva_t *)kvlist;
957 if (am_open) {
958 if (kvlist == NULL)
959 reason = 1; /* audit -n */
960 else
961 reason = 2; /* audit -s */
962 } else {
963 reason = 0; /* initial open */
964 #if DEBUG
965 dbfp = __auditd_debug_file_open();
966 #endif
968 DPRINT((dbfp, "binfile: am_open=%d, reason=%d\n", am_open, reason));
970 am_open = 1;
972 if (kvlist == NULL) {
973 dirlist = NULL;
974 minfree = NULL;
975 maxsize = NULL;
976 } else {
977 dirlist = kva_match(kv, "p_dir");
978 minfree = kva_match(kv, "p_minfree");
979 maxsize = kva_match(kv, "p_fsize");
981 switch (reason) {
982 case 0: /* initial open */
983 if (!binfile_is_open)
984 (void) pthread_mutex_init(&log_mutex, NULL);
985 binfile_is_open = 1;
986 openNewFile = 1;
987 /* FALLTHRU */
988 case 2: /* audit -s */
989 /* handle p_fsize parameter */
990 save_maxsize(maxsize);
992 fullness_state = PLENTY_SPACE;
993 status = loadauditlist(dirlist, minfree);
995 if (status == -1) {
996 (void) __logpost("");
997 *error = strdup(gettext("no directories configured"));
998 return (AUDITD_RETRY);
999 } else if (status == AUDITD_NO_MEMORY) {
1000 (void) __logpost("");
1001 *error = strdup(gettext("no memory"));
1002 return (status);
1003 } else { /* status is 0 or -2 (no change or changed) */
1004 hung_count = 0;
1005 DPRINT((dbfp, "binfile: loadauditlist returned %d\n",
1006 status));
1008 break;
1009 case 1: /* audit -n */
1010 (void) pthread_mutex_lock(&log_mutex);
1011 if (open_log(activeDir) == 1) /* ok */
1012 openNewFile = 0;
1013 (void) pthread_mutex_unlock(&log_mutex);
1014 break;
1017 rc = AUDITD_SUCCESS;
1018 *ret_list = NULL;
1020 return (rc);
1023 auditd_rc_t
1024 auditd_plugin_close(char **error)
1026 *error = NULL;
1028 (void) pthread_mutex_lock(&log_mutex);
1029 close_log(&lastOpenDir, "", "");
1030 freedirlist(activeDir);
1031 activeDir = NULL;
1032 (void) pthread_mutex_unlock(&log_mutex);
1034 DPRINT((dbfp, "binfile: closed\n"));
1036 (void) __logpost("");
1038 if (binfile_is_open) {
1039 (void) pthread_mutex_destroy(&log_mutex);
1040 binfile_is_open = 0;
1041 #if DEBUG
1042 } else {
1043 (void) fprintf(dbfp,
1044 "auditd_plugin_close() called when already closed.");
1045 #endif
1047 am_open = 0;
1048 return (AUDITD_SUCCESS);