preprocessor cleanup: __sparc
[unleashed/tickless.git] / usr / src / lib / auditd_plugins / binfile / binfile.c
bloba1ffbd91aa7ff5f50e643d655dd3c089f48927c8
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;
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) {
695 * strtol() returns a long which could be larger than int so
696 * store here for sanity checking first
698 long proposed_maxsize;
700 if (maxsize != NULL) {
702 * There is no explicit error return from strtol() so
703 * we may need to depend on the value of errno.
705 errno = 0;
706 proposed_maxsize = strtol(maxsize, (char **)NULL, 10);
709 * If sizeof(long) is greater than sizeof(int) on this
710 * platform, proposed_maxsize might be greater than
711 * INT_MAX without it being reported as ERANGE.
713 if ((errno == ERANGE) ||
714 ((proposed_maxsize != 0) &&
715 (proposed_maxsize < FSIZE_MIN)) ||
716 (proposed_maxsize > INT_MAX)) {
717 binfile_maxsize = 0;
718 DPRINT((dbfp, "binfile: p_fsize parameter out of "
719 "range: %s\n", maxsize));
721 * Inform administrator of the error via
722 * syslog
724 __audit_syslog("audit_binfile.so",
725 LOG_CONS | LOG_NDELAY,
726 LOG_DAEMON, LOG_ERR,
727 gettext("p_fsize parameter out of range\n"));
728 } else {
729 binfile_maxsize = proposed_maxsize;
731 } else { /* p_fsize string was not present */
732 binfile_maxsize = 0;
735 DPRINT((dbfp, "binfile: set maxsize to %d\n", binfile_maxsize));
739 * auditd_plugin() writes a buffer to the currently open file. The
740 * global "openNewFile" is used to force a new log file for cases such
741 * as the initial open, when minfree is reached, the p_fsize value is
742 * exceeded or the current file system fills up, and "audit -s" with
743 * changed parameters. For "audit -n" a new log file is opened
744 * immediately in auditd_plugin_open().
746 * This function manages one or more audit directories as follows:
748 * If the current open file is in a directory that has not
749 * reached the soft limit, write the input data and return.
751 * Scan the list of directories for one which has not reached
752 * the soft limit; if one is found, write and return. Such
753 * a writable directory is in "PLENTY_SPACE" state.
755 * Scan the list of directories for one which has not reached
756 * the hard limit; if one is found, write and return. This
757 * directory in in "SOFT_SPACE" state.
759 * Oh, and if a write fails, handle it like a hard space limit.
761 * audit_warn (via __audit_dowarn()) is used to alert an operator
762 * at various levels of fullness.
764 /* ARGSUSED */
765 auditd_rc_t
766 auditd_plugin(const char *input, size_t in_len, uint64_t sequence, char **error)
768 auditd_rc_t rc = AUDITD_FAIL;
769 int open_status;
770 size_t out_len;
771 /* avoid excess audit_warnage */
772 static int allsoftfull_warning = 0;
773 static int allhard_pause = 0;
774 static struct timeval next_allhard;
775 struct timeval now;
776 #if DEBUG
777 int statrc;
778 static char *last_file_written_to = NULL;
779 static uint64_t last_sequence = 0;
780 static uint64_t write_count = 0;
782 if ((last_sequence > 0) && (sequence != last_sequence + 1))
783 (void) fprintf(dbfp,
784 "binfile: buffer sequence=%llu but prev=%llu=n",
785 sequence, last_sequence);
786 last_sequence = sequence;
788 (void) fprintf(dbfp, "binfile: input seq=%llu, len=%d\n",
789 sequence, in_len);
790 #endif
791 *error = NULL;
793 * lock is for activeDir, referenced by open_log() and close_log()
795 (void) pthread_mutex_lock(&log_mutex);
798 * If this would take us over the maximum size, open a new
799 * file, unless maxsize is 0, in which case growth of the
800 * audit log is unrestricted.
802 if ((binfile_maxsize != 0) &&
803 ((binfile_cursize + in_len) > binfile_maxsize)) {
804 DPRINT((dbfp, "binfile: maxsize exceeded, opening new audit "
805 "file.\n"));
806 openNewFile = 1;
809 while (rc == AUDITD_FAIL) {
810 open_status = 1;
811 if (openNewFile) {
812 open_status = open_log(activeDir);
813 if (open_status == 1) /* ok */
814 openNewFile = 0;
817 * consider "space ok" return and error return the same;
818 * a -1 means spacecheck couldn't check for space.
820 #if !DEBUG
821 if ((open_status == 1) &&
822 (spacecheck(activeDir, fullness_state, in_len) != 0)) {
823 #else
824 if ((open_status == 1) &&
825 (statrc = spacecheck(activeDir, fullness_state,
826 in_len) != 0)) {
827 DPRINT((dbfp, "binfile: returned from spacecheck\n"));
829 * The last copy of last_file_written_to is
830 * never free'd, so there will be one open
831 * memory reference on exit. It's debug only.
833 if ((last_file_written_to != NULL) &&
834 (strcmp(last_file_written_to,
835 activeDir->dl_filename) != 0)) {
836 DPRINT((dbfp, "binfile: now writing to %s\n",
837 activeDir->dl_filename));
838 free(last_file_written_to);
840 DPRINT((dbfp, "binfile: finished some debug stuff\n"));
841 last_file_written_to =
842 strdup(activeDir->dl_filename);
843 #endif
844 out_len = write(activeDir->dl_fd, input, in_len);
845 DPRINT((dbfp, "binfile: finished the write\n"));
847 binfile_cursize += out_len;
849 if (out_len == in_len) {
850 DPRINT((dbfp,
851 "binfile: write_count=%llu, sequence=%llu,"
852 " l=%u\n",
853 ++write_count, sequence, out_len));
854 allsoftfull_warning = 0;
855 activeDir->dl_flags = 0;
857 rc = AUDITD_SUCCESS;
858 break;
859 } else if (!(activeDir->dl_flags & HARD_WARNED)) {
860 DPRINT((dbfp,
861 "binfile: write failed, sequence=%llu, "
862 "l=%u\n", sequence, out_len));
863 DPRINT((dbfp, "hard warning sent.\n"));
864 __audit_dowarn("hard", activeDir->dl_dirname,
867 activeDir->dl_flags |= HARD_WARNED;
869 } else {
870 DPRINT((dbfp, "binfile: statrc=%d, fullness_state=%d\n",
871 statrc, fullness_state));
872 if (!(activeDir->dl_flags & SOFT_WARNED) &&
873 (activeDir->dl_space == SOFT_SPACE)) {
874 DPRINT((dbfp, "soft warning sent\n"));
875 __audit_dowarn("soft",
876 activeDir->dl_dirname, 0);
877 activeDir->dl_flags |= SOFT_WARNED;
879 if (!(activeDir->dl_flags & HARD_WARNED) &&
880 (activeDir->dl_space == SPACE_FULL)) {
881 DPRINT((dbfp, "hard warning sent.\n"));
882 __audit_dowarn("hard",
883 activeDir->dl_dirname, 0);
884 activeDir->dl_flags |= HARD_WARNED;
887 DPRINT((dbfp, "binfile: activeDir=%s, next=%s\n",
888 activeDir->dl_dirname, activeDir->dl_next->dl_dirname));
890 activeDir = activeDir->dl_next;
891 openNewFile = 1;
893 if (activeDir == startdir) { /* full circle */
894 if (fullness_state == PLENTY_SPACE) { /* once */
895 fullness_state = SOFT_SPACE;
896 if (allsoftfull_warning == 0) {
897 allsoftfull_warning++;
898 __audit_dowarn("allsoft", "", 0);
900 } else { /* full circle twice */
901 if ((hung_count > 0) && !allhard_pause) {
902 allhard_pause = 1;
903 (void) gettimeofday(&next_allhard,
904 NULL);
905 next_allhard.tv_sec += ALLHARD_DELAY;
908 if (allhard_pause) {
909 (void) gettimeofday(&now, NULL);
910 if (now.tv_sec >= next_allhard.tv_sec) {
911 allhard_pause = 0;
912 __audit_dowarn("allhard", "",
913 ++hung_count);
915 } else {
916 __audit_dowarn("allhard", "",
917 ++hung_count);
919 minfreeblocks = AVAIL_MIN;
920 rc = AUDITD_RETRY;
921 *error = strdup(gettext(
922 "all partitions full\n"));
923 (void) __logpost("");
927 (void) pthread_mutex_unlock(&log_mutex);
929 return (rc);
934 * It may be called multiple times as auditd handles SIGHUP and SIGUSR1
935 * corresponding to the audit(1M) flags -s and -n
937 * kvlist is NULL only if auditd caught a SIGUSR1 (audit -n), so after the first
938 * time open is called; the reason is -s if kvlist != NULL and -n otherwise.
941 auditd_rc_t
942 auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error)
944 int rc = 0;
945 int status;
946 int reason;
947 char *dirlist;
948 char *minfree;
949 char *maxsize;
950 kva_t *kv;
952 *error = NULL;
953 *ret_list = NULL;
954 kv = (kva_t *)kvlist;
956 if (am_open) {
957 if (kvlist == NULL)
958 reason = 1; /* audit -n */
959 else
960 reason = 2; /* audit -s */
961 } else {
962 reason = 0; /* initial open */
963 #if DEBUG
964 dbfp = __auditd_debug_file_open();
965 #endif
967 DPRINT((dbfp, "binfile: am_open=%d, reason=%d\n", am_open, reason));
969 am_open = 1;
971 if (kvlist == NULL) {
972 dirlist = NULL;
973 minfree = NULL;
974 maxsize = NULL;
975 } else {
976 dirlist = kva_match(kv, "p_dir");
977 minfree = kva_match(kv, "p_minfree");
978 maxsize = kva_match(kv, "p_fsize");
980 switch (reason) {
981 case 0: /* initial open */
982 if (!binfile_is_open)
983 (void) pthread_mutex_init(&log_mutex, NULL);
984 binfile_is_open = 1;
985 openNewFile = 1;
986 /* FALLTHRU */
987 case 2: /* audit -s */
988 /* handle p_fsize parameter */
989 save_maxsize(maxsize);
991 fullness_state = PLENTY_SPACE;
992 status = loadauditlist(dirlist, minfree);
994 if (status == -1) {
995 (void) __logpost("");
996 *error = strdup(gettext("no directories configured"));
997 return (AUDITD_RETRY);
998 } else if (status == AUDITD_NO_MEMORY) {
999 (void) __logpost("");
1000 *error = strdup(gettext("no memory"));
1001 return (status);
1002 } else { /* status is 0 or -2 (no change or changed) */
1003 hung_count = 0;
1004 DPRINT((dbfp, "binfile: loadauditlist returned %d\n",
1005 status));
1007 break;
1008 case 1: /* audit -n */
1009 (void) pthread_mutex_lock(&log_mutex);
1010 if (open_log(activeDir) == 1) /* ok */
1011 openNewFile = 0;
1012 (void) pthread_mutex_unlock(&log_mutex);
1013 break;
1016 rc = AUDITD_SUCCESS;
1017 *ret_list = NULL;
1019 return (rc);
1022 auditd_rc_t
1023 auditd_plugin_close(char **error)
1025 *error = NULL;
1027 (void) pthread_mutex_lock(&log_mutex);
1028 close_log(&lastOpenDir, "", "");
1029 freedirlist(activeDir);
1030 activeDir = NULL;
1031 (void) pthread_mutex_unlock(&log_mutex);
1033 DPRINT((dbfp, "binfile: closed\n"));
1035 (void) __logpost("");
1037 if (binfile_is_open) {
1038 (void) pthread_mutex_destroy(&log_mutex);
1039 binfile_is_open = 0;
1040 #if DEBUG
1041 } else {
1042 (void) fprintf(dbfp,
1043 "auditd_plugin_close() called when already closed.");
1044 #endif
1046 am_open = 0;
1047 return (AUDITD_SUCCESS);