17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51a578a15Spaulson * Common Development and Distribution License (the "License").
61a578a15Spaulson * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22f8994074SJan Friedel * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate *
247c478bd9Sstevel@tonic-gate * write binary audit records directly to a file.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate #define DEBUG 0
287c478bd9Sstevel@tonic-gate
297c478bd9Sstevel@tonic-gate #if DEBUG
30dfc7be02SJan Friedel #define DPRINT(x) { (void) fprintf x; }
317c478bd9Sstevel@tonic-gate #else
327c478bd9Sstevel@tonic-gate #define DPRINT(x)
337c478bd9Sstevel@tonic-gate #endif
347c478bd9Sstevel@tonic-gate
357c478bd9Sstevel@tonic-gate /*
367c478bd9Sstevel@tonic-gate * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close()
377c478bd9Sstevel@tonic-gate * implement a replacable library for use by auditd; they are a
387c478bd9Sstevel@tonic-gate * project private interface and may change without notice.
397c478bd9Sstevel@tonic-gate *
407c478bd9Sstevel@tonic-gate */
417c478bd9Sstevel@tonic-gate
427c478bd9Sstevel@tonic-gate #include <assert.h>
437c478bd9Sstevel@tonic-gate #include <bsm/audit.h>
447c478bd9Sstevel@tonic-gate #include <bsm/audit_record.h>
457c478bd9Sstevel@tonic-gate #include <bsm/libbsm.h>
467c478bd9Sstevel@tonic-gate #include <errno.h>
477c478bd9Sstevel@tonic-gate #include <fcntl.h>
487c478bd9Sstevel@tonic-gate #include <libintl.h>
497c478bd9Sstevel@tonic-gate #include <netdb.h>
507c478bd9Sstevel@tonic-gate #include <pthread.h>
517c478bd9Sstevel@tonic-gate #include <secdb.h>
527c478bd9Sstevel@tonic-gate #include <signal.h>
537c478bd9Sstevel@tonic-gate #include <stdio.h>
547c478bd9Sstevel@tonic-gate #include <stdlib.h>
557c478bd9Sstevel@tonic-gate #include <string.h>
567c478bd9Sstevel@tonic-gate #include <sys/param.h>
577c478bd9Sstevel@tonic-gate #include <sys/types.h>
587c478bd9Sstevel@tonic-gate #include <time.h>
597c478bd9Sstevel@tonic-gate #include <tzfile.h>
607c478bd9Sstevel@tonic-gate #include <unistd.h>
617c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
62f7092930Spr #include <limits.h>
63f7092930Spr #include <syslog.h>
647c478bd9Sstevel@tonic-gate #include <security/auditd.h>
657c478bd9Sstevel@tonic-gate #include <audit_plugin.h>
667c478bd9Sstevel@tonic-gate
677c478bd9Sstevel@tonic-gate #define AUDIT_DATE_SZ 14
687c478bd9Sstevel@tonic-gate #define AUDIT_FNAME_SZ 2 * AUDIT_DATE_SZ + 2 + MAXHOSTNAMELEN
697c478bd9Sstevel@tonic-gate
707c478bd9Sstevel@tonic-gate /* per-directory status */
717c478bd9Sstevel@tonic-gate #define SOFT_SPACE 0 /* minfree or less space available */
727c478bd9Sstevel@tonic-gate #define PLENTY_SPACE 1 /* more than minfree available */
737c478bd9Sstevel@tonic-gate #define SPACE_FULL 2 /* out of space */
747c478bd9Sstevel@tonic-gate
757c478bd9Sstevel@tonic-gate #define AVAIL_MIN 50 /* If there are less that this number */
767c478bd9Sstevel@tonic-gate /* of blocks avail, the filesystem is */
777c478bd9Sstevel@tonic-gate /* presumed full. */
787c478bd9Sstevel@tonic-gate
791a578a15Spaulson #define ALLHARD_DELAY 20 /* Call audit_warn(allhard) every 20 seconds */
801a578a15Spaulson
81f7092930Spr /* minimum reasonable size in bytes to roll over an audit file */
82f7092930Spr #define FSIZE_MIN 512000
837c478bd9Sstevel@tonic-gate
847c478bd9Sstevel@tonic-gate /*
857c478bd9Sstevel@tonic-gate * The directory list is a circular linked list. It is pointed into by
867c478bd9Sstevel@tonic-gate * activeDir. Each element contains the pointer to the next
877c478bd9Sstevel@tonic-gate * element, the directory pathname, a flag for how much space there is
887c478bd9Sstevel@tonic-gate * in the directory's filesystem, and a file handle. Since a new
897c478bd9Sstevel@tonic-gate * directory list can be created from auditd_plugin_open() while the
907c478bd9Sstevel@tonic-gate * current list is in use, activeDir is protected by log_mutex.
917c478bd9Sstevel@tonic-gate */
927c478bd9Sstevel@tonic-gate typedef struct dirlist_s dirlist_t;
937c478bd9Sstevel@tonic-gate struct dirlist_s {
947c478bd9Sstevel@tonic-gate dirlist_t *dl_next;
957c478bd9Sstevel@tonic-gate int dl_space;
967c478bd9Sstevel@tonic-gate int dl_flags;
977c478bd9Sstevel@tonic-gate char *dl_dirname;
987c478bd9Sstevel@tonic-gate char *dl_filename; /* file name (not path) if open */
997c478bd9Sstevel@tonic-gate int dl_fd; /* file handle, -1 unless open */
1007c478bd9Sstevel@tonic-gate };
1017c478bd9Sstevel@tonic-gate /*
1027c478bd9Sstevel@tonic-gate * Defines for dl_flags
1037c478bd9Sstevel@tonic-gate */
1047c478bd9Sstevel@tonic-gate #define SOFT_WARNED 0x0001 /* already did soft warning for this dir */
1057c478bd9Sstevel@tonic-gate #define HARD_WARNED 0x0002 /* already did hard warning for this dir */
1067c478bd9Sstevel@tonic-gate
1077c478bd9Sstevel@tonic-gate #if DEBUG
1087c478bd9Sstevel@tonic-gate static FILE *dbfp; /* debug file */
1097c478bd9Sstevel@tonic-gate #endif
1107c478bd9Sstevel@tonic-gate
1117c478bd9Sstevel@tonic-gate static pthread_mutex_t log_mutex;
1127c478bd9Sstevel@tonic-gate static int binfile_is_open = 0;
1137c478bd9Sstevel@tonic-gate
1147c478bd9Sstevel@tonic-gate static int minfree = -1;
1157c478bd9Sstevel@tonic-gate static int minfreeblocks; /* minfree in blocks */
1167c478bd9Sstevel@tonic-gate
117f8994074SJan Friedel static dirlist_t *lastOpenDir = NULL; /* last activeDir */
118f8994074SJan Friedel static dirlist_t *activeDir = NULL; /* to be current directory */
1191a578a15Spaulson static dirlist_t *startdir; /* first dir in the ring */
1207c478bd9Sstevel@tonic-gate static int activeCount = 0; /* number of dirs in the ring */
1217c478bd9Sstevel@tonic-gate
1221a578a15Spaulson static int openNewFile = 0; /* need to open a new file */
1237c478bd9Sstevel@tonic-gate static int hung_count = 0; /* count of audit_warn hard */
1247c478bd9Sstevel@tonic-gate
1257c478bd9Sstevel@tonic-gate /* flag from audit_plugin_open to audit_plugin_close */
1267c478bd9Sstevel@tonic-gate static int am_open = 0;
1277c478bd9Sstevel@tonic-gate /* preferred dir state */
1287c478bd9Sstevel@tonic-gate static int fullness_state = PLENTY_SPACE;
1297c478bd9Sstevel@tonic-gate
130f7092930Spr /*
131f7092930Spr * These are used to implement a maximum size for the auditing
132f7092930Spr * file. binfile_maxsize is set via the 'p_fsize' parameter to the
133f7092930Spr * audit_binfile plugin.
134f7092930Spr */
135f7092930Spr static uint_t binfile_cursize = 0;
136f7092930Spr static uint_t binfile_maxsize = 0;
137f7092930Spr
1387c478bd9Sstevel@tonic-gate static int open_log(dirlist_t *);
1397c478bd9Sstevel@tonic-gate
1407c478bd9Sstevel@tonic-gate static void
freedirlist(dirlist_t * head)1417c478bd9Sstevel@tonic-gate freedirlist(dirlist_t *head)
1427c478bd9Sstevel@tonic-gate {
1437c478bd9Sstevel@tonic-gate dirlist_t *n1, *n2;
1447c478bd9Sstevel@tonic-gate /*
1457c478bd9Sstevel@tonic-gate * Free up the old directory list if any
1467c478bd9Sstevel@tonic-gate */
1477c478bd9Sstevel@tonic-gate if (head != NULL) {
1487c478bd9Sstevel@tonic-gate n1 = head;
1497c478bd9Sstevel@tonic-gate do {
1507c478bd9Sstevel@tonic-gate n2 = n1->dl_next;
1517c478bd9Sstevel@tonic-gate free(n1->dl_dirname);
1527c478bd9Sstevel@tonic-gate free(n1->dl_filename);
1537c478bd9Sstevel@tonic-gate free(n1);
1547c478bd9Sstevel@tonic-gate n1 = n2;
1557c478bd9Sstevel@tonic-gate } while (n1 != head);
1567c478bd9Sstevel@tonic-gate }
1577c478bd9Sstevel@tonic-gate }
1587c478bd9Sstevel@tonic-gate
159f8994074SJan Friedel dirlist_t *
dupdirnode(dirlist_t * node_orig)160f8994074SJan Friedel dupdirnode(dirlist_t *node_orig)
161f8994074SJan Friedel {
162f8994074SJan Friedel dirlist_t *node_new;
163f8994074SJan Friedel
164f8994074SJan Friedel if ((node_new = calloc(1, sizeof (dirlist_t))) == NULL) {
165f8994074SJan Friedel return (NULL);
166f8994074SJan Friedel }
167f8994074SJan Friedel
168f8994074SJan Friedel if (node_orig->dl_dirname != NULL &&
169f8994074SJan Friedel (node_new->dl_dirname = strdup(node_orig->dl_dirname)) == NULL ||
170f8994074SJan Friedel node_orig->dl_filename != NULL &&
171f8994074SJan Friedel (node_new->dl_filename = strdup(node_orig->dl_filename)) == NULL) {
172f8994074SJan Friedel freedirlist(node_new);
173f8994074SJan Friedel return (NULL);
174f8994074SJan Friedel }
175f8994074SJan Friedel
176f8994074SJan Friedel node_new->dl_next = node_new;
177f8994074SJan Friedel node_new->dl_space = node_orig->dl_space;
178f8994074SJan Friedel node_new->dl_flags = node_orig->dl_flags;
179f8994074SJan Friedel node_new->dl_fd = node_orig->dl_fd;
180f8994074SJan Friedel
181f8994074SJan Friedel return (node_new);
182f8994074SJan Friedel }
1837c478bd9Sstevel@tonic-gate
1847c478bd9Sstevel@tonic-gate /*
1857c478bd9Sstevel@tonic-gate * add to a linked list of directories available for writing
1867c478bd9Sstevel@tonic-gate *
1877c478bd9Sstevel@tonic-gate */
1887c478bd9Sstevel@tonic-gate static int
growauditlist(dirlist_t ** listhead,char * dirlist,dirlist_t * endnode,int * count)1897c478bd9Sstevel@tonic-gate growauditlist(dirlist_t **listhead, char *dirlist,
1907c478bd9Sstevel@tonic-gate dirlist_t *endnode, int *count)
1917c478bd9Sstevel@tonic-gate {
1927c478bd9Sstevel@tonic-gate dirlist_t *node;
1937c478bd9Sstevel@tonic-gate char *bs, *be;
1947c478bd9Sstevel@tonic-gate dirlist_t **node_p;
1957c478bd9Sstevel@tonic-gate char *dirname;
1967c478bd9Sstevel@tonic-gate char *remainder;
1977c478bd9Sstevel@tonic-gate
1987c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: dirlist=%s\n", dirlist));
1997c478bd9Sstevel@tonic-gate
2007c478bd9Sstevel@tonic-gate if (*listhead == NULL)
2017c478bd9Sstevel@tonic-gate node_p = listhead;
2027c478bd9Sstevel@tonic-gate else
2037c478bd9Sstevel@tonic-gate node_p = &(endnode->dl_next);
2047c478bd9Sstevel@tonic-gate
2057c478bd9Sstevel@tonic-gate node = NULL;
2067c478bd9Sstevel@tonic-gate while ((dirname = strtok_r(dirlist, ",", &remainder)) != NULL) {
2077c478bd9Sstevel@tonic-gate dirlist = NULL;
2087c478bd9Sstevel@tonic-gate
2097c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: p_dir = %s\n", dirname));
2107c478bd9Sstevel@tonic-gate
2117c478bd9Sstevel@tonic-gate (*count)++;
2127c478bd9Sstevel@tonic-gate node = malloc(sizeof (dirlist_t));
2137c478bd9Sstevel@tonic-gate if (node == NULL)
2147c478bd9Sstevel@tonic-gate return (AUDITD_NO_MEMORY);
2157c478bd9Sstevel@tonic-gate
2167c478bd9Sstevel@tonic-gate node->dl_flags = 0;
2177c478bd9Sstevel@tonic-gate node->dl_filename = NULL;
2187c478bd9Sstevel@tonic-gate node->dl_fd = -1;
2197c478bd9Sstevel@tonic-gate node->dl_space = PLENTY_SPACE;
2207c478bd9Sstevel@tonic-gate
2217c478bd9Sstevel@tonic-gate node->dl_dirname = malloc((unsigned)strlen(dirname) + 1);
2227c478bd9Sstevel@tonic-gate if (node->dl_dirname == NULL)
2237c478bd9Sstevel@tonic-gate return (AUDITD_NO_MEMORY);
2247c478bd9Sstevel@tonic-gate
2257c478bd9Sstevel@tonic-gate bs = dirname;
2267c478bd9Sstevel@tonic-gate while ((*bs == ' ') || (*bs == '\t')) /* trim blanks */
2277c478bd9Sstevel@tonic-gate bs++;
2287c478bd9Sstevel@tonic-gate be = bs + strlen(bs) - 1;
2297c478bd9Sstevel@tonic-gate while (be > bs) { /* trim trailing blanks */
2307c478bd9Sstevel@tonic-gate if ((*bs != ' ') && (*bs != '\t'))
2317c478bd9Sstevel@tonic-gate break;
2327c478bd9Sstevel@tonic-gate be--;
2337c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate *(be + 1) = '\0';
2357c478bd9Sstevel@tonic-gate (void) strlcpy(node->dl_dirname, bs, AUDIT_FNAME_SZ);
2367c478bd9Sstevel@tonic-gate
2377c478bd9Sstevel@tonic-gate if (*listhead != NULL)
2387c478bd9Sstevel@tonic-gate node->dl_next = *listhead;
2397c478bd9Sstevel@tonic-gate else
2407c478bd9Sstevel@tonic-gate node->dl_next = node;
2417c478bd9Sstevel@tonic-gate *node_p = node;
2427c478bd9Sstevel@tonic-gate node_p = &(node->dl_next);
2437c478bd9Sstevel@tonic-gate
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate return (0);
2467c478bd9Sstevel@tonic-gate }
2477c478bd9Sstevel@tonic-gate
2487c478bd9Sstevel@tonic-gate /*
2497c478bd9Sstevel@tonic-gate * create a linked list of directories available for writing
2507c478bd9Sstevel@tonic-gate *
2517c478bd9Sstevel@tonic-gate * if a list already exists, the two are compared and the new one is
2527c478bd9Sstevel@tonic-gate * used only if it is different than the old.
2537c478bd9Sstevel@tonic-gate *
2547c478bd9Sstevel@tonic-gate * returns -2 for new or changed list, 0 for unchanged list and -1 for
2557c478bd9Sstevel@tonic-gate * error. (Positive returns are for AUDITD_<error code> values)
2567c478bd9Sstevel@tonic-gate *
2577c478bd9Sstevel@tonic-gate */
2587c478bd9Sstevel@tonic-gate static int
loadauditlist(char * dirstr,char * minfreestr)2597c478bd9Sstevel@tonic-gate loadauditlist(char *dirstr, char *minfreestr)
2607c478bd9Sstevel@tonic-gate {
261f8994074SJan Friedel dirlist_t *n1, *n2;
2627c478bd9Sstevel@tonic-gate dirlist_t *listhead = NULL;
2637c478bd9Sstevel@tonic-gate dirlist_t *thisdir;
2647c478bd9Sstevel@tonic-gate int node_count = 0;
2657c478bd9Sstevel@tonic-gate int rc;
26664ea2d20SToomas Soome int temp_minfree = 0;
2677c478bd9Sstevel@tonic-gate
2687c478bd9Sstevel@tonic-gate static dirlist_t *activeList = NULL; /* directory list */
2697c478bd9Sstevel@tonic-gate
270f8994074SJan Friedel DPRINT((dbfp, "binfile: Loading audit list from audit service "
271f8994074SJan Friedel "(audit_binfile)\n"));
2727c478bd9Sstevel@tonic-gate
273f8994074SJan Friedel if (dirstr == NULL || minfreestr == NULL) {
274f8994074SJan Friedel DPRINT((dbfp, "binfile: internal error"));
2757c478bd9Sstevel@tonic-gate return (-1);
276f8994074SJan Friedel }
277f8994074SJan Friedel if ((rc = growauditlist(&listhead, dirstr, NULL, &node_count)) != 0) {
278f8994074SJan Friedel return (rc);
2797c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate if (node_count == 0) {
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate * there was a problem getting the directory
283f8994074SJan Friedel * list or remote host info from the audit_binfile
284f8994074SJan Friedel * configuration even though auditd thought there was
285f8994074SJan Friedel * at least 1 good entry
2867c478bd9Sstevel@tonic-gate */
2877c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: "
2887c478bd9Sstevel@tonic-gate "problem getting directory / libpath list "
289f8994074SJan Friedel "from audit_binfile configuration.\n"));
2907c478bd9Sstevel@tonic-gate return (-1);
2917c478bd9Sstevel@tonic-gate }
292f8994074SJan Friedel
2937c478bd9Sstevel@tonic-gate #if DEBUG
2947c478bd9Sstevel@tonic-gate /* print out directory list */
2957c478bd9Sstevel@tonic-gate if (listhead != NULL) {
296dfc7be02SJan Friedel (void) fprintf(dbfp, "Directory list:\n\t%s\n",
297dfc7be02SJan Friedel listhead->dl_dirname);
2987c478bd9Sstevel@tonic-gate thisdir = listhead->dl_next;
2997c478bd9Sstevel@tonic-gate
3007c478bd9Sstevel@tonic-gate while (thisdir != listhead) {
301dfc7be02SJan Friedel (void) fprintf(dbfp, "\t%s\n", thisdir->dl_dirname);
3027c478bd9Sstevel@tonic-gate thisdir = thisdir->dl_next;
3037c478bd9Sstevel@tonic-gate }
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate #endif /* DEBUG */
306f8994074SJan Friedel
3077c478bd9Sstevel@tonic-gate thisdir = listhead;
3087c478bd9Sstevel@tonic-gate
309f8994074SJan Friedel /* See if the list has changed (rc = 0 if no change, else 1) */
310f8994074SJan Friedel rc = 0;
3117c478bd9Sstevel@tonic-gate if (node_count == activeCount) {
3127c478bd9Sstevel@tonic-gate n1 = listhead;
3137c478bd9Sstevel@tonic-gate n2 = activeList;
3147c478bd9Sstevel@tonic-gate do {
3157c478bd9Sstevel@tonic-gate if (strcmp(n1->dl_dirname, n2->dl_dirname) != 0) {
3167c478bd9Sstevel@tonic-gate DPRINT((dbfp,
3177c478bd9Sstevel@tonic-gate "binfile: new dirname = %s\n"
3187c478bd9Sstevel@tonic-gate "binfile: old dirname = %s\n",
3197c478bd9Sstevel@tonic-gate n1->dl_dirname,
3207c478bd9Sstevel@tonic-gate n2->dl_dirname));
3217c478bd9Sstevel@tonic-gate rc = -2;
3227c478bd9Sstevel@tonic-gate break;
3237c478bd9Sstevel@tonic-gate }
3247c478bd9Sstevel@tonic-gate n1 = n1->dl_next;
3257c478bd9Sstevel@tonic-gate n2 = n2->dl_next;
3267c478bd9Sstevel@tonic-gate } while ((n1 != listhead) && (n2 != activeList));
3277c478bd9Sstevel@tonic-gate } else {
328f8994074SJan Friedel DPRINT((dbfp, "binfile: dir counts differs\n"
329f8994074SJan Friedel "binfile: old dir count = %d\n"
3307c478bd9Sstevel@tonic-gate "binfile: new dir count = %d\n",
3317c478bd9Sstevel@tonic-gate activeCount, node_count));
3327c478bd9Sstevel@tonic-gate rc = -2;
3337c478bd9Sstevel@tonic-gate }
3347c478bd9Sstevel@tonic-gate if (rc == -2) {
3357c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&log_mutex);
336*bbf21555SRichard Lowe DPRINT((dbfp, "loadauditlist: close / open audit.log(5)\n"));
3371a578a15Spaulson if (open_log(listhead) == 0) {
3387c478bd9Sstevel@tonic-gate openNewFile = 1; /* try again later */
3391a578a15Spaulson } else {
3401a578a15Spaulson openNewFile = 0;
3411a578a15Spaulson }
3427c478bd9Sstevel@tonic-gate freedirlist(activeList); /* old list */
3437c478bd9Sstevel@tonic-gate activeList = listhead; /* new list */
3441a578a15Spaulson activeDir = startdir = thisdir;
3457c478bd9Sstevel@tonic-gate activeCount = node_count;
3467c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&log_mutex);
347f8994074SJan Friedel } else {
3487c478bd9Sstevel@tonic-gate freedirlist(listhead);
349f8994074SJan Friedel }
350f8994074SJan Friedel
351f8994074SJan Friedel /* Get the minfree value. */
3527c478bd9Sstevel@tonic-gate if (minfreestr != NULL)
3537c478bd9Sstevel@tonic-gate temp_minfree = atoi(minfreestr);
3547c478bd9Sstevel@tonic-gate
3557c478bd9Sstevel@tonic-gate if ((temp_minfree < 0) || (temp_minfree > 100))
3567c478bd9Sstevel@tonic-gate temp_minfree = 0;
3577c478bd9Sstevel@tonic-gate
3587c478bd9Sstevel@tonic-gate if (minfree != temp_minfree) {
3597c478bd9Sstevel@tonic-gate DPRINT((dbfp, "minfree: old = %d, new = %d\n",
3607c478bd9Sstevel@tonic-gate minfree, temp_minfree));
3617c478bd9Sstevel@tonic-gate rc = -2; /* data change */
3627c478bd9Sstevel@tonic-gate minfree = temp_minfree;
3637c478bd9Sstevel@tonic-gate }
3647c478bd9Sstevel@tonic-gate
3657c478bd9Sstevel@tonic-gate return (rc);
3667c478bd9Sstevel@tonic-gate }
3677c478bd9Sstevel@tonic-gate
3687c478bd9Sstevel@tonic-gate /*
3697c478bd9Sstevel@tonic-gate * getauditdate - get the current time (GMT) and put it in the form
3707c478bd9Sstevel@tonic-gate * yyyymmddHHMMSS .
3717c478bd9Sstevel@tonic-gate */
3727c478bd9Sstevel@tonic-gate static void
getauditdate(char * date)3737c478bd9Sstevel@tonic-gate getauditdate(char *date)
3747c478bd9Sstevel@tonic-gate {
3757c478bd9Sstevel@tonic-gate struct timeval tp;
3767c478bd9Sstevel@tonic-gate struct timezone tzp;
3777c478bd9Sstevel@tonic-gate struct tm tm;
3787c478bd9Sstevel@tonic-gate
3797c478bd9Sstevel@tonic-gate (void) gettimeofday(&tp, &tzp);
3807c478bd9Sstevel@tonic-gate tm = *gmtime(&tp.tv_sec);
3817c478bd9Sstevel@tonic-gate /*
3827c478bd9Sstevel@tonic-gate * NOTE: if we want to use gmtime, we have to be aware that the
3837c478bd9Sstevel@tonic-gate * structure only keeps the year as an offset from TM_YEAR_BASE.
3847c478bd9Sstevel@tonic-gate * I have used TM_YEAR_BASE in this code so that if they change
3857c478bd9Sstevel@tonic-gate * this base from 1900 to 2000, it will hopefully mean that this
3867c478bd9Sstevel@tonic-gate * code does not have to change. TM_YEAR_BASE is defined in
3877c478bd9Sstevel@tonic-gate * tzfile.h .
3887c478bd9Sstevel@tonic-gate */
3897c478bd9Sstevel@tonic-gate (void) sprintf(date, "%.4d%.2d%.2d%.2d%.2d%.2d",
3901a578a15Spaulson tm.tm_year + TM_YEAR_BASE, tm.tm_mon + 1, tm.tm_mday,
3911a578a15Spaulson tm.tm_hour, tm.tm_min, tm.tm_sec);
3927c478bd9Sstevel@tonic-gate }
3937c478bd9Sstevel@tonic-gate
3947c478bd9Sstevel@tonic-gate
3957c478bd9Sstevel@tonic-gate
3967c478bd9Sstevel@tonic-gate /*
3977c478bd9Sstevel@tonic-gate * write_file_token - put the file token into the audit log
3987c478bd9Sstevel@tonic-gate */
3997c478bd9Sstevel@tonic-gate static int
write_file_token(int fd,char * name)4007c478bd9Sstevel@tonic-gate write_file_token(int fd, char *name)
4017c478bd9Sstevel@tonic-gate {
4027c478bd9Sstevel@tonic-gate adr_t adr; /* xdr ptr */
4037c478bd9Sstevel@tonic-gate struct timeval tv; /* time now */
4047c478bd9Sstevel@tonic-gate char for_adr[AUDIT_FNAME_SZ + AUDIT_FNAME_SZ]; /* plenty of room */
4057c478bd9Sstevel@tonic-gate char token_id;
4067c478bd9Sstevel@tonic-gate short i;
4077c478bd9Sstevel@tonic-gate
4087c478bd9Sstevel@tonic-gate (void) gettimeofday(&tv, (struct timezone *)0);
4097c478bd9Sstevel@tonic-gate i = strlen(name) + 1;
4107c478bd9Sstevel@tonic-gate adr_start(&adr, for_adr);
4117c478bd9Sstevel@tonic-gate #ifdef _LP64
4127c478bd9Sstevel@tonic-gate token_id = AUT_OTHER_FILE64;
4137c478bd9Sstevel@tonic-gate adr_char(&adr, &token_id, 1);
4147c478bd9Sstevel@tonic-gate adr_int64(&adr, (int64_t *)& tv, 2);
4157c478bd9Sstevel@tonic-gate #else
4167c478bd9Sstevel@tonic-gate token_id = AUT_OTHER_FILE32;
4177c478bd9Sstevel@tonic-gate adr_char(&adr, &token_id, 1);
4187c478bd9Sstevel@tonic-gate adr_int32(&adr, (int32_t *)& tv, 2);
4197c478bd9Sstevel@tonic-gate #endif
4207c478bd9Sstevel@tonic-gate
4217c478bd9Sstevel@tonic-gate adr_short(&adr, &i, 1);
4227c478bd9Sstevel@tonic-gate adr_char(&adr, name, i);
4237c478bd9Sstevel@tonic-gate
4247c478bd9Sstevel@tonic-gate if (write(fd, for_adr, adr_count(&adr)) < 0) {
4257c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: Bad write\n"));
4267c478bd9Sstevel@tonic-gate return (errno);
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate return (0);
4297c478bd9Sstevel@tonic-gate }
4307c478bd9Sstevel@tonic-gate
4317c478bd9Sstevel@tonic-gate /*
4327c478bd9Sstevel@tonic-gate * close_log - close the file if open. Also put the name of the
4337c478bd9Sstevel@tonic-gate * new log file in the trailer, and rename the old file
4347c478bd9Sstevel@tonic-gate * to oldname. The caller must hold log_mutext while calling
4357c478bd9Sstevel@tonic-gate * close_log since any change to activeDir is a complete redo
4367c478bd9Sstevel@tonic-gate * of all it points to.
4377c478bd9Sstevel@tonic-gate * arguments -
4387c478bd9Sstevel@tonic-gate * oldname - the new name for the file to be closed
4397c478bd9Sstevel@tonic-gate * newname - the name of the new log file (for the trailer)
4407c478bd9Sstevel@tonic-gate */
4417c478bd9Sstevel@tonic-gate static void
close_log(dirlist_t ** lastOpenDir_ptr,char * oname,char * newname)442f8994074SJan Friedel close_log(dirlist_t **lastOpenDir_ptr, char *oname, char *newname)
4437c478bd9Sstevel@tonic-gate {
444f8994074SJan Friedel char auditdate[AUDIT_DATE_SZ+1];
445f8994074SJan Friedel char *name;
446f8994074SJan Friedel char oldname[AUDIT_FNAME_SZ+1];
447f8994074SJan Friedel dirlist_t *currentdir = *lastOpenDir_ptr;
4487c478bd9Sstevel@tonic-gate
4497c478bd9Sstevel@tonic-gate if ((currentdir == NULL) || (currentdir->dl_fd == -1))
4507c478bd9Sstevel@tonic-gate return;
4517c478bd9Sstevel@tonic-gate /*
4527c478bd9Sstevel@tonic-gate * If oldname is blank, we were called by auditd_plugin_close()
4537c478bd9Sstevel@tonic-gate * instead of by open_log, so we need to update our name.
4547c478bd9Sstevel@tonic-gate */
4557c478bd9Sstevel@tonic-gate (void) strlcpy(oldname, oname, AUDIT_FNAME_SZ);
4567c478bd9Sstevel@tonic-gate
4577c478bd9Sstevel@tonic-gate if (strcmp(oldname, "") == 0) {
4587c478bd9Sstevel@tonic-gate getauditdate(auditdate);
4597c478bd9Sstevel@tonic-gate
4607c478bd9Sstevel@tonic-gate assert(currentdir->dl_filename != NULL);
4617c478bd9Sstevel@tonic-gate
4627c478bd9Sstevel@tonic-gate (void) strlcpy(oldname, currentdir->dl_filename,
4637c478bd9Sstevel@tonic-gate AUDIT_FNAME_SZ);
4647c478bd9Sstevel@tonic-gate
4657c478bd9Sstevel@tonic-gate name = strrchr(oldname, '/') + 1;
4667c478bd9Sstevel@tonic-gate (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate,
4677c478bd9Sstevel@tonic-gate AUDIT_DATE_SZ);
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate /*
4707c478bd9Sstevel@tonic-gate * Write the trailer record and rename and close the file.
4717c478bd9Sstevel@tonic-gate * If any of the write, rename, or close fail, ignore it
4727c478bd9Sstevel@tonic-gate * since there is not much else we can do and the next open()
4737c478bd9Sstevel@tonic-gate * will trigger the necessary full directory logic.
4747c478bd9Sstevel@tonic-gate *
4757c478bd9Sstevel@tonic-gate * newname is "" if binfile is being closed down.
4767c478bd9Sstevel@tonic-gate */
4777c478bd9Sstevel@tonic-gate (void) write_file_token(currentdir->dl_fd, newname);
4786a3b10dbStz if (currentdir->dl_fd >= 0) {
4796a3b10dbStz (void) fsync(currentdir->dl_fd);
4807c478bd9Sstevel@tonic-gate (void) close(currentdir->dl_fd);
4816a3b10dbStz }
4827c478bd9Sstevel@tonic-gate currentdir->dl_fd = -1;
4837c478bd9Sstevel@tonic-gate (void) rename(currentdir->dl_filename, oldname);
4847c478bd9Sstevel@tonic-gate
4857c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: Log closed %s\n", oldname));
4867c478bd9Sstevel@tonic-gate
487f8994074SJan Friedel freedirlist(currentdir);
488f8994074SJan Friedel *lastOpenDir_ptr = NULL;
4897c478bd9Sstevel@tonic-gate }
4907c478bd9Sstevel@tonic-gate
4917c478bd9Sstevel@tonic-gate
4927c478bd9Sstevel@tonic-gate /*
4937c478bd9Sstevel@tonic-gate * open_log - open a new file in the current directory. If a
4947c478bd9Sstevel@tonic-gate * file is already open, close it.
4957c478bd9Sstevel@tonic-gate *
4967c478bd9Sstevel@tonic-gate * return 1 if ok, 0 if all directories are full.
4977c478bd9Sstevel@tonic-gate *
4987c478bd9Sstevel@tonic-gate * lastOpenDir - used to get the oldfile name (and change it),
4997c478bd9Sstevel@tonic-gate * to close the oldfile.
5007c478bd9Sstevel@tonic-gate *
5017c478bd9Sstevel@tonic-gate * The caller must hold log_mutex while calling open_log.
5027c478bd9Sstevel@tonic-gate *
5037c478bd9Sstevel@tonic-gate */
5047c478bd9Sstevel@tonic-gate static int
open_log(dirlist_t * current_dir)5057c478bd9Sstevel@tonic-gate open_log(dirlist_t *current_dir)
5067c478bd9Sstevel@tonic-gate {
5077c478bd9Sstevel@tonic-gate char auditdate[AUDIT_DATE_SZ + 1];
5087c478bd9Sstevel@tonic-gate char oldname[AUDIT_FNAME_SZ + 1] = "";
5097c478bd9Sstevel@tonic-gate char newname[AUDIT_FNAME_SZ + 1];
5107c478bd9Sstevel@tonic-gate char *name; /* pointer into oldname */
511f8994074SJan Friedel int opened = 0;
5127c478bd9Sstevel@tonic-gate int error = 0;
5137c478bd9Sstevel@tonic-gate int newfd = 0;
5147c478bd9Sstevel@tonic-gate
5157c478bd9Sstevel@tonic-gate static char host[MAXHOSTNAMELEN + 1] = "";
5167c478bd9Sstevel@tonic-gate /* previous directory with open log file */
5177c478bd9Sstevel@tonic-gate
5187c478bd9Sstevel@tonic-gate if (host[0] == '\0')
5197c478bd9Sstevel@tonic-gate (void) gethostname(host, MAXHOSTNAMELEN);
5207c478bd9Sstevel@tonic-gate
5217c478bd9Sstevel@tonic-gate /* Get a filename which does not already exist */
5227c478bd9Sstevel@tonic-gate while (!opened) {
5237c478bd9Sstevel@tonic-gate getauditdate(auditdate);
5247c478bd9Sstevel@tonic-gate (void) snprintf(newname, AUDIT_FNAME_SZ,
5257c478bd9Sstevel@tonic-gate "%s/%s.not_terminated.%s",
5267c478bd9Sstevel@tonic-gate current_dir->dl_dirname, auditdate, host);
5277c478bd9Sstevel@tonic-gate newfd = open(newname,
528172c0d20Sme O_RDWR | O_APPEND | O_CREAT | O_EXCL, 0640);
5297c478bd9Sstevel@tonic-gate if (newfd < 0) {
5307c478bd9Sstevel@tonic-gate switch (errno) {
5317c478bd9Sstevel@tonic-gate case EEXIST:
5327c478bd9Sstevel@tonic-gate DPRINT((dbfp,
5337c478bd9Sstevel@tonic-gate "open_log says duplicate for %s "
5347c478bd9Sstevel@tonic-gate "(will try another)\n", newname));
5357c478bd9Sstevel@tonic-gate (void) sleep(1);
5367c478bd9Sstevel@tonic-gate break;
537f8994074SJan Friedel case ENOENT: {
538f8994074SJan Friedel /* invalid path */
539f8994074SJan Friedel char *msg;
540f8994074SJan Friedel (void) asprintf(&msg,
541f8994074SJan Friedel gettext("No such p_dir: %s\n"),
542f8994074SJan Friedel current_dir->dl_dirname);
543f8994074SJan Friedel DPRINT((dbfp,
544f8994074SJan Friedel "open_log says about %s: %s\n",
545f8994074SJan Friedel newname, strerror(errno)));
546f8994074SJan Friedel __audit_syslog("audit_binfile.so",
547f8994074SJan Friedel LOG_CONS | LOG_NDELAY,
548f8994074SJan Friedel LOG_DAEMON, LOG_ERR, msg);
549f8994074SJan Friedel free(msg);
550f8994074SJan Friedel current_dir = current_dir->dl_next;
551f8994074SJan Friedel return (0);
552f8994074SJan Friedel }
5537c478bd9Sstevel@tonic-gate default:
5547c478bd9Sstevel@tonic-gate /* open failed */
5557c478bd9Sstevel@tonic-gate DPRINT((dbfp,
5567c478bd9Sstevel@tonic-gate "open_log says full for %s: %s\n",
5577c478bd9Sstevel@tonic-gate newname, strerror(errno)));
5587c478bd9Sstevel@tonic-gate current_dir->dl_space = SPACE_FULL;
5597c478bd9Sstevel@tonic-gate current_dir = current_dir->dl_next;
5607c478bd9Sstevel@tonic-gate return (0);
5617c478bd9Sstevel@tonic-gate } /* switch */
5627c478bd9Sstevel@tonic-gate } else
5637c478bd9Sstevel@tonic-gate opened = 1;
5647c478bd9Sstevel@tonic-gate } /* while */
5657c478bd9Sstevel@tonic-gate
5667c478bd9Sstevel@tonic-gate /*
5677c478bd9Sstevel@tonic-gate * When we get here, we have opened our new log file.
5687c478bd9Sstevel@tonic-gate * Now we need to update the name of the old file to
5697c478bd9Sstevel@tonic-gate * store in this file's header. lastOpenDir may point
5707c478bd9Sstevel@tonic-gate * to current_dir if the list is only one entry long and
5717c478bd9Sstevel@tonic-gate * there is only one list.
5727c478bd9Sstevel@tonic-gate */
5737c478bd9Sstevel@tonic-gate if ((lastOpenDir != NULL) && (lastOpenDir->dl_filename != NULL)) {
5747c478bd9Sstevel@tonic-gate (void) strlcpy(oldname, lastOpenDir->dl_filename,
5757c478bd9Sstevel@tonic-gate AUDIT_FNAME_SZ);
5767c478bd9Sstevel@tonic-gate name = (char *)strrchr(oldname, '/') + 1;
5777c478bd9Sstevel@tonic-gate
5787c478bd9Sstevel@tonic-gate (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate,
5791a578a15Spaulson AUDIT_DATE_SZ);
5807c478bd9Sstevel@tonic-gate
581f8994074SJan Friedel close_log(&lastOpenDir, oldname, newname);
5827c478bd9Sstevel@tonic-gate }
5837c478bd9Sstevel@tonic-gate error = write_file_token(newfd, oldname);
5847c478bd9Sstevel@tonic-gate if (error) {
5857c478bd9Sstevel@tonic-gate /* write token failed */
5867c478bd9Sstevel@tonic-gate (void) close(newfd);
5877c478bd9Sstevel@tonic-gate
5887c478bd9Sstevel@tonic-gate current_dir->dl_space = SPACE_FULL;
5897c478bd9Sstevel@tonic-gate current_dir->dl_fd = -1;
5907c478bd9Sstevel@tonic-gate free(current_dir->dl_filename);
5917c478bd9Sstevel@tonic-gate current_dir->dl_filename = NULL;
5927c478bd9Sstevel@tonic-gate current_dir = current_dir->dl_next;
5937c478bd9Sstevel@tonic-gate return (0);
5947c478bd9Sstevel@tonic-gate } else {
595f8994074SJan Friedel if (current_dir->dl_filename != NULL) {
596f8994074SJan Friedel free(current_dir->dl_filename);
597f8994074SJan Friedel }
5987c478bd9Sstevel@tonic-gate current_dir->dl_filename = strdup(newname);
599f8994074SJan Friedel current_dir->dl_fd = newfd;
600f8994074SJan Friedel
601f8994074SJan Friedel if (lastOpenDir == NULL) {
602f8994074SJan Friedel freedirlist(lastOpenDir);
603f8994074SJan Friedel if ((lastOpenDir = dupdirnode(current_dir)) == NULL) {
604f8994074SJan Friedel __audit_syslog("audit_binfile.so",
605f8994074SJan Friedel LOG_CONS | LOG_NDELAY,
606f8994074SJan Friedel LOG_DAEMON, LOG_ERR, gettext("no memory"));
607f8994074SJan Friedel return (0);
608f8994074SJan Friedel }
609f8994074SJan Friedel DPRINT((dbfp, "open_log created new lastOpenDir "
610f8994074SJan Friedel "(%s, %s [fd: %d])\n",
611f8994074SJan Friedel lastOpenDir->dl_dirname == NULL ? "" :
612f8994074SJan Friedel lastOpenDir->dl_dirname,
613f8994074SJan Friedel lastOpenDir->dl_filename == NULL ? "" :
614f8994074SJan Friedel lastOpenDir->dl_filename, lastOpenDir->dl_fd));
615f8994074SJan Friedel }
6167c478bd9Sstevel@tonic-gate
617f7092930Spr /*
618f7092930Spr * New file opened, so reset file size statistic (used
619f7092930Spr * to ensure audit log does not grow above size limit
620f7092930Spr * set by p_fsize).
621f7092930Spr */
622f7092930Spr binfile_cursize = 0;
623f7092930Spr
6245ad42b1bSSurya Prakki (void) __logpost(newname);
6257c478bd9Sstevel@tonic-gate
6267c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: Log opened: %s\n", newname));
6277c478bd9Sstevel@tonic-gate return (1);
6287c478bd9Sstevel@tonic-gate }
6297c478bd9Sstevel@tonic-gate }
6307c478bd9Sstevel@tonic-gate
6317c478bd9Sstevel@tonic-gate #define IGNORE_SIZE 8192
6327c478bd9Sstevel@tonic-gate /*
6337c478bd9Sstevel@tonic-gate * spacecheck - determine whether the given directory's filesystem
6347c478bd9Sstevel@tonic-gate * has the at least the space requested. Also set the space
6357c478bd9Sstevel@tonic-gate * value in the directory list structure. If the caller
6367c478bd9Sstevel@tonic-gate * passes other than PLENTY_SPACE or SOFT_SPACE, the caller should
6377c478bd9Sstevel@tonic-gate * ignore the return value. Otherwise, 0 = less than the
6387c478bd9Sstevel@tonic-gate * requested space is available, 1 = at least the requested space
6397c478bd9Sstevel@tonic-gate * is available.
6407c478bd9Sstevel@tonic-gate *
6417c478bd9Sstevel@tonic-gate * log_mutex must be held by the caller
6427c478bd9Sstevel@tonic-gate *
6437c478bd9Sstevel@tonic-gate * -1 is returned if stat fails
6447c478bd9Sstevel@tonic-gate *
6457c478bd9Sstevel@tonic-gate * IGNORE_SIZE is one page (Sol 9 / 10 timeframe) and is the default
6467c478bd9Sstevel@tonic-gate * buffer size written for Sol 9 and earlier. To keep the same accuracy
6477c478bd9Sstevel@tonic-gate * for the soft limit check as before, spacecheck checks for space
6487c478bd9Sstevel@tonic-gate * remaining IGNORE_SIZE bytes. This reduces the number of statvfs()
6497c478bd9Sstevel@tonic-gate * calls and related math.
6507c478bd9Sstevel@tonic-gate *
6517c478bd9Sstevel@tonic-gate * globals -
6527c478bd9Sstevel@tonic-gate * minfree - the soft limit, i.e., the % of filesystem to reserve
6537c478bd9Sstevel@tonic-gate */
6547c478bd9Sstevel@tonic-gate static int
spacecheck(dirlist_t * thisdir,int test_limit,size_t next_buf_size)6557c478bd9Sstevel@tonic-gate spacecheck(dirlist_t *thisdir, int test_limit, size_t next_buf_size)
6567c478bd9Sstevel@tonic-gate {
6577c478bd9Sstevel@tonic-gate struct statvfs sb;
6587c478bd9Sstevel@tonic-gate static int ignore_size = 0;
6597c478bd9Sstevel@tonic-gate
6607c478bd9Sstevel@tonic-gate ignore_size += next_buf_size;
6617c478bd9Sstevel@tonic-gate
6627c478bd9Sstevel@tonic-gate if ((test_limit == PLENTY_SPACE) && (ignore_size < IGNORE_SIZE))
6637c478bd9Sstevel@tonic-gate return (1);
6647c478bd9Sstevel@tonic-gate
6657c478bd9Sstevel@tonic-gate assert(thisdir != NULL);
6667c478bd9Sstevel@tonic-gate
6671a578a15Spaulson if (statvfs(thisdir->dl_dirname, &sb) < 0) {
6687c478bd9Sstevel@tonic-gate thisdir->dl_space = SPACE_FULL;
6697c478bd9Sstevel@tonic-gate minfreeblocks = AVAIL_MIN;
6707c478bd9Sstevel@tonic-gate return (-1);
6717c478bd9Sstevel@tonic-gate } else {
6727c478bd9Sstevel@tonic-gate minfreeblocks = ((minfree * sb.f_blocks) / 100) + AVAIL_MIN;
6737c478bd9Sstevel@tonic-gate
6747c478bd9Sstevel@tonic-gate if (sb.f_bavail < AVAIL_MIN)
6757c478bd9Sstevel@tonic-gate thisdir->dl_space = SPACE_FULL;
6761a578a15Spaulson else if (sb.f_bavail > minfreeblocks) {
6771a578a15Spaulson thisdir->dl_space = fullness_state = PLENTY_SPACE;
6781a578a15Spaulson ignore_size = 0;
6791a578a15Spaulson } else
6807c478bd9Sstevel@tonic-gate thisdir->dl_space = SOFT_SPACE;
6817c478bd9Sstevel@tonic-gate }
6827c478bd9Sstevel@tonic-gate if (thisdir->dl_space == PLENTY_SPACE)
6837c478bd9Sstevel@tonic-gate return (1);
6847c478bd9Sstevel@tonic-gate
6857c478bd9Sstevel@tonic-gate return (thisdir->dl_space == test_limit);
6867c478bd9Sstevel@tonic-gate }
6877c478bd9Sstevel@tonic-gate
688f7092930Spr /*
689f7092930Spr * Parses p_fsize value and contains it within the range FSIZE_MIN and
690f7092930Spr * INT_MAX so using uints won't cause an undetected overflow of
691f7092930Spr * INT_MAX. Defaults to 0 if the value is invalid or is missing.
692f7092930Spr */
693f7092930Spr static void
save_maxsize(char * maxsize)69464ea2d20SToomas Soome save_maxsize(char *maxsize)
69564ea2d20SToomas Soome {
696f7092930Spr /*
697f7092930Spr * strtol() returns a long which could be larger than int so
698f7092930Spr * store here for sanity checking first
699f7092930Spr */
700f7092930Spr long proposed_maxsize;
701f7092930Spr
702f7092930Spr if (maxsize != NULL) {
703f7092930Spr /*
704f7092930Spr * There is no explicit error return from strtol() so
705f7092930Spr * we may need to depend on the value of errno.
706f7092930Spr */
707f7092930Spr errno = 0;
708f7092930Spr proposed_maxsize = strtol(maxsize, (char **)NULL, 10);
709f7092930Spr
710f7092930Spr /*
711f7092930Spr * If sizeof(long) is greater than sizeof(int) on this
712f7092930Spr * platform, proposed_maxsize might be greater than
713f7092930Spr * INT_MAX without it being reported as ERANGE.
714f7092930Spr */
715f7092930Spr if ((errno == ERANGE) ||
716f7092930Spr ((proposed_maxsize != 0) &&
71764ea2d20SToomas Soome (proposed_maxsize < FSIZE_MIN)) ||
718f7092930Spr (proposed_maxsize > INT_MAX)) {
719f7092930Spr binfile_maxsize = 0;
720f7092930Spr DPRINT((dbfp, "binfile: p_fsize parameter out of "
72164ea2d20SToomas Soome "range: %s\n", maxsize));
722f7092930Spr /*
723f7092930Spr * Inform administrator of the error via
724f7092930Spr * syslog
725f7092930Spr */
726f7092930Spr __audit_syslog("audit_binfile.so",
727f7092930Spr LOG_CONS | LOG_NDELAY,
728f7092930Spr LOG_DAEMON, LOG_ERR,
729f7092930Spr gettext("p_fsize parameter out of range\n"));
730f7092930Spr } else {
731f7092930Spr binfile_maxsize = proposed_maxsize;
732f7092930Spr }
733f7092930Spr } else { /* p_fsize string was not present */
734f7092930Spr binfile_maxsize = 0;
735f7092930Spr }
736f7092930Spr
737f7092930Spr DPRINT((dbfp, "binfile: set maxsize to %d\n", binfile_maxsize));
738f7092930Spr }
739f7092930Spr
7407c478bd9Sstevel@tonic-gate /*
7411a578a15Spaulson * auditd_plugin() writes a buffer to the currently open file. The
742f7092930Spr * global "openNewFile" is used to force a new log file for cases such
743f7092930Spr * as the initial open, when minfree is reached, the p_fsize value is
744f7092930Spr * exceeded or the current file system fills up, and "audit -s" with
745f7092930Spr * changed parameters. For "audit -n" a new log file is opened
746f7092930Spr * immediately in auditd_plugin_open().
7477c478bd9Sstevel@tonic-gate *
7487c478bd9Sstevel@tonic-gate * This function manages one or more audit directories as follows:
7497c478bd9Sstevel@tonic-gate *
7507c478bd9Sstevel@tonic-gate * If the current open file is in a directory that has not
7517c478bd9Sstevel@tonic-gate * reached the soft limit, write the input data and return.
7527c478bd9Sstevel@tonic-gate *
7537c478bd9Sstevel@tonic-gate * Scan the list of directories for one which has not reached
7547c478bd9Sstevel@tonic-gate * the soft limit; if one is found, write and return. Such
7557c478bd9Sstevel@tonic-gate * a writable directory is in "PLENTY_SPACE" state.
7567c478bd9Sstevel@tonic-gate *
7577c478bd9Sstevel@tonic-gate * Scan the list of directories for one which has not reached
7587c478bd9Sstevel@tonic-gate * the hard limit; if one is found, write and return. This
7597c478bd9Sstevel@tonic-gate * directory in in "SOFT_SPACE" state.
7607c478bd9Sstevel@tonic-gate *
7617c478bd9Sstevel@tonic-gate * Oh, and if a write fails, handle it like a hard space limit.
7627c478bd9Sstevel@tonic-gate *
7637c478bd9Sstevel@tonic-gate * audit_warn (via __audit_dowarn()) is used to alert an operator
7647c478bd9Sstevel@tonic-gate * at various levels of fullness.
7657c478bd9Sstevel@tonic-gate */
7667c478bd9Sstevel@tonic-gate /* ARGSUSED */
7677c478bd9Sstevel@tonic-gate auditd_rc_t
auditd_plugin(const char * input,size_t in_len,uint64_t sequence,char ** error)7688f775e0aSJan Friedel auditd_plugin(const char *input, size_t in_len, uint64_t sequence, char **error)
7697c478bd9Sstevel@tonic-gate {
7707c478bd9Sstevel@tonic-gate auditd_rc_t rc = AUDITD_FAIL;
7717c478bd9Sstevel@tonic-gate int open_status;
7727c478bd9Sstevel@tonic-gate size_t out_len;
7737c478bd9Sstevel@tonic-gate /* avoid excess audit_warnage */
7747c478bd9Sstevel@tonic-gate static int allsoftfull_warning = 0;
7751a578a15Spaulson static int allhard_pause = 0;
7761a578a15Spaulson static struct timeval next_allhard;
7771a578a15Spaulson struct timeval now;
7787c478bd9Sstevel@tonic-gate #if DEBUG
779dfc7be02SJan Friedel int statrc;
7807c478bd9Sstevel@tonic-gate static char *last_file_written_to = NULL;
7818f775e0aSJan Friedel static uint64_t last_sequence = 0;
7828f775e0aSJan Friedel static uint64_t write_count = 0;
7837c478bd9Sstevel@tonic-gate
7847c478bd9Sstevel@tonic-gate if ((last_sequence > 0) && (sequence != last_sequence + 1))
785dfc7be02SJan Friedel (void) fprintf(dbfp,
786dfc7be02SJan Friedel "binfile: buffer sequence=%llu but prev=%llu=n",
7871a578a15Spaulson sequence, last_sequence);
7887c478bd9Sstevel@tonic-gate last_sequence = sequence;
7897c478bd9Sstevel@tonic-gate
790dfc7be02SJan Friedel (void) fprintf(dbfp, "binfile: input seq=%llu, len=%d\n",
791dfc7be02SJan Friedel sequence, in_len);
7927c478bd9Sstevel@tonic-gate #endif
7937c478bd9Sstevel@tonic-gate *error = NULL;
7947c478bd9Sstevel@tonic-gate /*
7957c478bd9Sstevel@tonic-gate * lock is for activeDir, referenced by open_log() and close_log()
7967c478bd9Sstevel@tonic-gate */
7977c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&log_mutex);
798f7092930Spr
799f7092930Spr /*
800f7092930Spr * If this would take us over the maximum size, open a new
801f7092930Spr * file, unless maxsize is 0, in which case growth of the
802f7092930Spr * audit log is unrestricted.
803f7092930Spr */
804f7092930Spr if ((binfile_maxsize != 0) &&
805f7092930Spr ((binfile_cursize + in_len) > binfile_maxsize)) {
806f7092930Spr DPRINT((dbfp, "binfile: maxsize exceeded, opening new audit "
807f7092930Spr "file.\n"));
808f7092930Spr openNewFile = 1;
809f7092930Spr }
810f7092930Spr
8117c478bd9Sstevel@tonic-gate while (rc == AUDITD_FAIL) {
8127c478bd9Sstevel@tonic-gate open_status = 1;
8137c478bd9Sstevel@tonic-gate if (openNewFile) {
8147c478bd9Sstevel@tonic-gate open_status = open_log(activeDir);
8157c478bd9Sstevel@tonic-gate if (open_status == 1) /* ok */
8167c478bd9Sstevel@tonic-gate openNewFile = 0;
8177c478bd9Sstevel@tonic-gate }
8187c478bd9Sstevel@tonic-gate /*
8197c478bd9Sstevel@tonic-gate * consider "space ok" return and error return the same;
8207c478bd9Sstevel@tonic-gate * a -1 means spacecheck couldn't check for space.
8217c478bd9Sstevel@tonic-gate */
822dfc7be02SJan Friedel #if !DEBUG
823dfc7be02SJan Friedel if ((open_status == 1) &&
824dfc7be02SJan Friedel (spacecheck(activeDir, fullness_state, in_len) != 0)) {
825dfc7be02SJan Friedel #else
8267c478bd9Sstevel@tonic-gate if ((open_status == 1) &&
8277c478bd9Sstevel@tonic-gate (statrc = spacecheck(activeDir, fullness_state,
828dfc7be02SJan Friedel in_len) != 0)) {
8297c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: returned from spacecheck\n"));
8307c478bd9Sstevel@tonic-gate /*
8317c478bd9Sstevel@tonic-gate * The last copy of last_file_written_to is
8327c478bd9Sstevel@tonic-gate * never free'd, so there will be one open
8337c478bd9Sstevel@tonic-gate * memory reference on exit. It's debug only.
8347c478bd9Sstevel@tonic-gate */
8357c478bd9Sstevel@tonic-gate if ((last_file_written_to != NULL) &&
8367c478bd9Sstevel@tonic-gate (strcmp(last_file_written_to,
8377c478bd9Sstevel@tonic-gate activeDir->dl_filename) != 0)) {
8387c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: now writing to %s\n",
8397c478bd9Sstevel@tonic-gate activeDir->dl_filename));
8407c478bd9Sstevel@tonic-gate free(last_file_written_to);
8417c478bd9Sstevel@tonic-gate }
8427c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: finished some debug stuff\n"));
8437c478bd9Sstevel@tonic-gate last_file_written_to =
8447c478bd9Sstevel@tonic-gate strdup(activeDir->dl_filename);
8457c478bd9Sstevel@tonic-gate #endif
8467c478bd9Sstevel@tonic-gate out_len = write(activeDir->dl_fd, input, in_len);
8477c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: finished the write\n"));
8487c478bd9Sstevel@tonic-gate
849f7092930Spr binfile_cursize += out_len;
850f7092930Spr
8517c478bd9Sstevel@tonic-gate if (out_len == in_len) {
8527c478bd9Sstevel@tonic-gate DPRINT((dbfp,
8538f775e0aSJan Friedel "binfile: write_count=%llu, sequence=%llu,"
8547c478bd9Sstevel@tonic-gate " l=%u\n",
8557c478bd9Sstevel@tonic-gate ++write_count, sequence, out_len));
8567c478bd9Sstevel@tonic-gate allsoftfull_warning = 0;
8571a578a15Spaulson activeDir->dl_flags = 0;
8587c478bd9Sstevel@tonic-gate
8597c478bd9Sstevel@tonic-gate rc = AUDITD_SUCCESS;
8607c478bd9Sstevel@tonic-gate break;
8611a578a15Spaulson } else if (!(activeDir->dl_flags & HARD_WARNED)) {
8627c478bd9Sstevel@tonic-gate DPRINT((dbfp,
8638f775e0aSJan Friedel "binfile: write failed, sequence=%llu, "
8647c478bd9Sstevel@tonic-gate "l=%u\n", sequence, out_len));
8657c478bd9Sstevel@tonic-gate DPRINT((dbfp, "hard warning sent.\n"));
8667c478bd9Sstevel@tonic-gate __audit_dowarn("hard", activeDir->dl_dirname,
8677c478bd9Sstevel@tonic-gate 0);
8687c478bd9Sstevel@tonic-gate
8697c478bd9Sstevel@tonic-gate activeDir->dl_flags |= HARD_WARNED;
8707c478bd9Sstevel@tonic-gate }
8717c478bd9Sstevel@tonic-gate } else {
8727c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: statrc=%d, fullness_state=%d\n",
8737c478bd9Sstevel@tonic-gate statrc, fullness_state));
8741a578a15Spaulson if (!(activeDir->dl_flags & SOFT_WARNED) &&
8751a578a15Spaulson (activeDir->dl_space == SOFT_SPACE)) {
8767c478bd9Sstevel@tonic-gate DPRINT((dbfp, "soft warning sent\n"));
8777c478bd9Sstevel@tonic-gate __audit_dowarn("soft",
8787c478bd9Sstevel@tonic-gate activeDir->dl_dirname, 0);
8797c478bd9Sstevel@tonic-gate activeDir->dl_flags |= SOFT_WARNED;
8807c478bd9Sstevel@tonic-gate }
8811a578a15Spaulson if (!(activeDir->dl_flags & HARD_WARNED) &&
8821a578a15Spaulson (activeDir->dl_space == SPACE_FULL)) {
8837c478bd9Sstevel@tonic-gate DPRINT((dbfp, "hard warning sent.\n"));
8847c478bd9Sstevel@tonic-gate __audit_dowarn("hard",
8851a578a15Spaulson activeDir->dl_dirname, 0);
8867c478bd9Sstevel@tonic-gate activeDir->dl_flags |= HARD_WARNED;
8877c478bd9Sstevel@tonic-gate }
8887c478bd9Sstevel@tonic-gate }
8897c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: activeDir=%s, next=%s\n",
8907c478bd9Sstevel@tonic-gate activeDir->dl_dirname, activeDir->dl_next->dl_dirname));
8917c478bd9Sstevel@tonic-gate
8927c478bd9Sstevel@tonic-gate activeDir = activeDir->dl_next;
8931a578a15Spaulson openNewFile = 1;
8947c478bd9Sstevel@tonic-gate
8957c478bd9Sstevel@tonic-gate if (activeDir == startdir) { /* full circle */
8967c478bd9Sstevel@tonic-gate if (fullness_state == PLENTY_SPACE) { /* once */
8977c478bd9Sstevel@tonic-gate fullness_state = SOFT_SPACE;
8987c478bd9Sstevel@tonic-gate if (allsoftfull_warning == 0) {
8997c478bd9Sstevel@tonic-gate allsoftfull_warning++;
9007c478bd9Sstevel@tonic-gate __audit_dowarn("allsoft", "", 0);
9017c478bd9Sstevel@tonic-gate }
9027c478bd9Sstevel@tonic-gate } else { /* full circle twice */
9031a578a15Spaulson if ((hung_count > 0) && !allhard_pause) {
9041a578a15Spaulson allhard_pause = 1;
9051a578a15Spaulson (void) gettimeofday(&next_allhard,
9061a578a15Spaulson NULL);
9071a578a15Spaulson next_allhard.tv_sec += ALLHARD_DELAY;
9081a578a15Spaulson }
9091a578a15Spaulson
9101a578a15Spaulson if (allhard_pause) {
9111a578a15Spaulson (void) gettimeofday(&now, NULL);
9121a578a15Spaulson if (now.tv_sec >= next_allhard.tv_sec) {
9131a578a15Spaulson allhard_pause = 0;
9141a578a15Spaulson __audit_dowarn("allhard", "",
9151a578a15Spaulson ++hung_count);
9161a578a15Spaulson }
9171a578a15Spaulson } else {
9181a578a15Spaulson __audit_dowarn("allhard", "",
9191a578a15Spaulson ++hung_count);
9201a578a15Spaulson }
9217c478bd9Sstevel@tonic-gate minfreeblocks = AVAIL_MIN;
9227c478bd9Sstevel@tonic-gate rc = AUDITD_RETRY;
9237c478bd9Sstevel@tonic-gate *error = strdup(gettext(
9247c478bd9Sstevel@tonic-gate "all partitions full\n"));
9255ad42b1bSSurya Prakki (void) __logpost("");
9267c478bd9Sstevel@tonic-gate }
9277c478bd9Sstevel@tonic-gate }
9287c478bd9Sstevel@tonic-gate }
9297c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&log_mutex);
9307c478bd9Sstevel@tonic-gate
9317c478bd9Sstevel@tonic-gate return (rc);
9327c478bd9Sstevel@tonic-gate }
9337c478bd9Sstevel@tonic-gate
9347c478bd9Sstevel@tonic-gate
9357c478bd9Sstevel@tonic-gate /*
9367c478bd9Sstevel@tonic-gate * It may be called multiple times as auditd handles SIGHUP and SIGUSR1
937*bbf21555SRichard Lowe * corresponding to the audit(8) flags -s and -n
9387c478bd9Sstevel@tonic-gate *
939f8994074SJan Friedel * kvlist is NULL only if auditd caught a SIGUSR1 (audit -n), so after the first
940f8994074SJan Friedel * time open is called; the reason is -s if kvlist != NULL and -n otherwise.
9417c478bd9Sstevel@tonic-gate *
9427c478bd9Sstevel@tonic-gate */
9437c478bd9Sstevel@tonic-gate auditd_rc_t
9447c478bd9Sstevel@tonic-gate auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error)
9457c478bd9Sstevel@tonic-gate {
9467c478bd9Sstevel@tonic-gate int rc = 0;
9477c478bd9Sstevel@tonic-gate int status;
9487c478bd9Sstevel@tonic-gate int reason;
9497c478bd9Sstevel@tonic-gate char *dirlist;
9507c478bd9Sstevel@tonic-gate char *minfree;
951f7092930Spr char *maxsize;
9527c478bd9Sstevel@tonic-gate kva_t *kv;
9537c478bd9Sstevel@tonic-gate
9547c478bd9Sstevel@tonic-gate *error = NULL;
9557c478bd9Sstevel@tonic-gate *ret_list = NULL;
9567c478bd9Sstevel@tonic-gate kv = (kva_t *)kvlist;
9577c478bd9Sstevel@tonic-gate
9587c478bd9Sstevel@tonic-gate if (am_open) {
9597c478bd9Sstevel@tonic-gate if (kvlist == NULL)
9607c478bd9Sstevel@tonic-gate reason = 1; /* audit -n */
9617c478bd9Sstevel@tonic-gate else
9627c478bd9Sstevel@tonic-gate reason = 2; /* audit -s */
9637c478bd9Sstevel@tonic-gate } else {
9647c478bd9Sstevel@tonic-gate reason = 0; /* initial open */
9657c478bd9Sstevel@tonic-gate #if DEBUG
9667c478bd9Sstevel@tonic-gate dbfp = __auditd_debug_file_open();
9677c478bd9Sstevel@tonic-gate #endif
9687c478bd9Sstevel@tonic-gate }
9697c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: am_open=%d, reason=%d\n", am_open, reason));
9707c478bd9Sstevel@tonic-gate
9717c478bd9Sstevel@tonic-gate am_open = 1;
9727c478bd9Sstevel@tonic-gate
9737c478bd9Sstevel@tonic-gate if (kvlist == NULL) {
9747c478bd9Sstevel@tonic-gate dirlist = NULL;
9757c478bd9Sstevel@tonic-gate minfree = NULL;
976f7092930Spr maxsize = NULL;
9777c478bd9Sstevel@tonic-gate } else {
9787c478bd9Sstevel@tonic-gate dirlist = kva_match(kv, "p_dir");
9797c478bd9Sstevel@tonic-gate minfree = kva_match(kv, "p_minfree");
980f7092930Spr maxsize = kva_match(kv, "p_fsize");
9817c478bd9Sstevel@tonic-gate }
9827c478bd9Sstevel@tonic-gate switch (reason) {
9837c478bd9Sstevel@tonic-gate case 0: /* initial open */
9847c478bd9Sstevel@tonic-gate if (!binfile_is_open)
9857c478bd9Sstevel@tonic-gate (void) pthread_mutex_init(&log_mutex, NULL);
9867c478bd9Sstevel@tonic-gate binfile_is_open = 1;
9877c478bd9Sstevel@tonic-gate openNewFile = 1;
9887c478bd9Sstevel@tonic-gate /* FALLTHRU */
9897c478bd9Sstevel@tonic-gate case 2: /* audit -s */
990f7092930Spr /* handle p_fsize parameter */
991f7092930Spr save_maxsize(maxsize);
992f7092930Spr
9937c478bd9Sstevel@tonic-gate fullness_state = PLENTY_SPACE;
9947c478bd9Sstevel@tonic-gate status = loadauditlist(dirlist, minfree);
9957c478bd9Sstevel@tonic-gate
9967c478bd9Sstevel@tonic-gate if (status == -1) {
9975ad42b1bSSurya Prakki (void) __logpost("");
9987c478bd9Sstevel@tonic-gate *error = strdup(gettext("no directories configured"));
9997c478bd9Sstevel@tonic-gate return (AUDITD_RETRY);
10007c478bd9Sstevel@tonic-gate } else if (status == AUDITD_NO_MEMORY) {
10015ad42b1bSSurya Prakki (void) __logpost("");
10027c478bd9Sstevel@tonic-gate *error = strdup(gettext("no memory"));
10037c478bd9Sstevel@tonic-gate return (status);
10047c478bd9Sstevel@tonic-gate } else { /* status is 0 or -2 (no change or changed) */
10057c478bd9Sstevel@tonic-gate hung_count = 0;
10067c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: loadauditlist returned %d\n",
10071a578a15Spaulson status));
10087c478bd9Sstevel@tonic-gate }
10097c478bd9Sstevel@tonic-gate break;
10107c478bd9Sstevel@tonic-gate case 1: /* audit -n */
10117c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&log_mutex);
10127c478bd9Sstevel@tonic-gate if (open_log(activeDir) == 1) /* ok */
10137c478bd9Sstevel@tonic-gate openNewFile = 0;
10147c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&log_mutex);
10157c478bd9Sstevel@tonic-gate break;
10167c478bd9Sstevel@tonic-gate }
10177c478bd9Sstevel@tonic-gate
10187c478bd9Sstevel@tonic-gate rc = AUDITD_SUCCESS;
10197c478bd9Sstevel@tonic-gate *ret_list = NULL;
10207c478bd9Sstevel@tonic-gate
10217c478bd9Sstevel@tonic-gate return (rc);
10227c478bd9Sstevel@tonic-gate }
10237c478bd9Sstevel@tonic-gate
10247c478bd9Sstevel@tonic-gate auditd_rc_t
10257c478bd9Sstevel@tonic-gate auditd_plugin_close(char **error)
10267c478bd9Sstevel@tonic-gate {
10277c478bd9Sstevel@tonic-gate *error = NULL;
10287c478bd9Sstevel@tonic-gate
10297c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&log_mutex);
1030f8994074SJan Friedel close_log(&lastOpenDir, "", "");
10317c478bd9Sstevel@tonic-gate freedirlist(activeDir);
10327c478bd9Sstevel@tonic-gate activeDir = NULL;
10337c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&log_mutex);
10347c478bd9Sstevel@tonic-gate
10357c478bd9Sstevel@tonic-gate DPRINT((dbfp, "binfile: closed\n"));
10367c478bd9Sstevel@tonic-gate
1037c900e163Sgww (void) __logpost("");
1038c900e163Sgww
10397c478bd9Sstevel@tonic-gate if (binfile_is_open) {
10407c478bd9Sstevel@tonic-gate (void) pthread_mutex_destroy(&log_mutex);
10417c478bd9Sstevel@tonic-gate binfile_is_open = 0;
1042dfc7be02SJan Friedel #if DEBUG
10437c478bd9Sstevel@tonic-gate } else {
1044dfc7be02SJan Friedel (void) fprintf(dbfp,
1045dfc7be02SJan Friedel "auditd_plugin_close() called when already closed.");
1046dfc7be02SJan Friedel #endif
10477c478bd9Sstevel@tonic-gate }
10487c478bd9Sstevel@tonic-gate am_open = 0;
10497c478bd9Sstevel@tonic-gate return (AUDITD_SUCCESS);
10507c478bd9Sstevel@tonic-gate }
1051