xref: /onnv-gate/usr/src/lib/auditd_plugins/binfile/binfile.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  *
26*0Sstevel@tonic-gate  * write binary audit records directly to a file.
27*0Sstevel@tonic-gate  */
28*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate #define	DEBUG   0
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate #if DEBUG
33*0Sstevel@tonic-gate #define	DPRINT(x) {fprintf x; }
34*0Sstevel@tonic-gate #else
35*0Sstevel@tonic-gate #define	DPRINT(x)
36*0Sstevel@tonic-gate #endif
37*0Sstevel@tonic-gate 
38*0Sstevel@tonic-gate /*
39*0Sstevel@tonic-gate  * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close()
40*0Sstevel@tonic-gate  * implement a replacable library for use by auditd; they are a
41*0Sstevel@tonic-gate  * project private interface and may change without notice.
42*0Sstevel@tonic-gate  *
43*0Sstevel@tonic-gate  */
44*0Sstevel@tonic-gate 
45*0Sstevel@tonic-gate #include <assert.h>
46*0Sstevel@tonic-gate #include <bsm/audit.h>
47*0Sstevel@tonic-gate #include <bsm/audit_record.h>
48*0Sstevel@tonic-gate #include <bsm/libbsm.h>
49*0Sstevel@tonic-gate #include <errno.h>
50*0Sstevel@tonic-gate #include <fcntl.h>
51*0Sstevel@tonic-gate #include <libintl.h>
52*0Sstevel@tonic-gate #include <netdb.h>
53*0Sstevel@tonic-gate #include <pthread.h>
54*0Sstevel@tonic-gate #include <secdb.h>
55*0Sstevel@tonic-gate #include <signal.h>
56*0Sstevel@tonic-gate #include <stdio.h>
57*0Sstevel@tonic-gate #include <stdlib.h>
58*0Sstevel@tonic-gate #include <string.h>
59*0Sstevel@tonic-gate #include <sys/param.h>
60*0Sstevel@tonic-gate #include <sys/types.h>
61*0Sstevel@tonic-gate #include <time.h>
62*0Sstevel@tonic-gate #include <tzfile.h>
63*0Sstevel@tonic-gate #include <unistd.h>
64*0Sstevel@tonic-gate #include <sys/vfs.h>
65*0Sstevel@tonic-gate #include <security/auditd.h>
66*0Sstevel@tonic-gate #include <audit_plugin.h>
67*0Sstevel@tonic-gate 
68*0Sstevel@tonic-gate #define	AUDIT_DATE_SZ	14
69*0Sstevel@tonic-gate #define	AUDIT_FNAME_SZ	2 * AUDIT_DATE_SZ + 2 + MAXHOSTNAMELEN
70*0Sstevel@tonic-gate #define	AUDIT_BAK_SZ	50	/* size of name of audit_data back-up file */
71*0Sstevel@tonic-gate 
72*0Sstevel@tonic-gate 			/* per-directory status */
73*0Sstevel@tonic-gate #define	SOFT_SPACE	0	/* minfree or less space available	*/
74*0Sstevel@tonic-gate #define	PLENTY_SPACE	1	/* more than minfree available		*/
75*0Sstevel@tonic-gate #define	SPACE_FULL	2	/* out of space				*/
76*0Sstevel@tonic-gate #define	STAY_FULL	3	/* unusable file system			*/
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate #define	AVAIL_MIN	50	/* If there are less that this number	*/
79*0Sstevel@tonic-gate 				/* of blocks avail, the filesystem is	*/
80*0Sstevel@tonic-gate 				/* presumed full.			*/
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate /*
84*0Sstevel@tonic-gate  * The directory list is a circular linked list.  It is pointed into by
85*0Sstevel@tonic-gate  * activeDir.  Each element contains the pointer to the next
86*0Sstevel@tonic-gate  * element, the directory pathname, a flag for how much space there is
87*0Sstevel@tonic-gate  * in the directory's filesystem, and a file handle.  Since a new
88*0Sstevel@tonic-gate  * directory list can be created from auditd_plugin_open() while the
89*0Sstevel@tonic-gate  * current list is in use, activeDir is protected by log_mutex.
90*0Sstevel@tonic-gate  */
91*0Sstevel@tonic-gate typedef struct dirlist_s dirlist_t;
92*0Sstevel@tonic-gate struct dirlist_s {
93*0Sstevel@tonic-gate 	dirlist_t	*dl_next;
94*0Sstevel@tonic-gate 	int		dl_space;
95*0Sstevel@tonic-gate 	int		dl_flags;
96*0Sstevel@tonic-gate 	char		*dl_dirname;
97*0Sstevel@tonic-gate 	char		*dl_filename;	/* file name (not path) if open */
98*0Sstevel@tonic-gate 	int		dl_fd;		/* file handle, -1 unless open */
99*0Sstevel@tonic-gate };
100*0Sstevel@tonic-gate /*
101*0Sstevel@tonic-gate  * Defines for dl_flags
102*0Sstevel@tonic-gate  */
103*0Sstevel@tonic-gate #define	SOFT_WARNED	0x0001	/* already did soft warning for this dir */
104*0Sstevel@tonic-gate #define	HARD_WARNED	0x0002	/* already did hard warning for this dir */
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate #if DEBUG
107*0Sstevel@tonic-gate static FILE		*dbfp;			/* debug file */
108*0Sstevel@tonic-gate #endif
109*0Sstevel@tonic-gate 
110*0Sstevel@tonic-gate static pthread_mutex_t	log_mutex;
111*0Sstevel@tonic-gate static int		binfile_is_open = 0;
112*0Sstevel@tonic-gate 
113*0Sstevel@tonic-gate static int		minfree = -1;
114*0Sstevel@tonic-gate static int		minfreeblocks;		/* minfree in blocks */
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate static dirlist_t	*activeDir = NULL;	/* current directory */
117*0Sstevel@tonic-gate static int		activeCount = 0;	/* number of dirs in the ring */
118*0Sstevel@tonic-gate 
119*0Sstevel@tonic-gate static int		openNewFile = 1;	/* need to open a new file */
120*0Sstevel@tonic-gate static int		hung_count = 0;		/* count of audit_warn hard */
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate /* flag from audit_plugin_open to audit_plugin_close */
123*0Sstevel@tonic-gate static int		am_open = 0;
124*0Sstevel@tonic-gate /* preferred dir state */
125*0Sstevel@tonic-gate static int		fullness_state = PLENTY_SPACE;
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate static int open_log(dirlist_t *);
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate static void
130*0Sstevel@tonic-gate freedirlist(dirlist_t *head)
131*0Sstevel@tonic-gate {
132*0Sstevel@tonic-gate 	dirlist_t	 *n1, *n2;
133*0Sstevel@tonic-gate 	/*
134*0Sstevel@tonic-gate 	 * Free up the old directory list if any
135*0Sstevel@tonic-gate 	 */
136*0Sstevel@tonic-gate 	if (head != NULL) {
137*0Sstevel@tonic-gate 		n1 = head;
138*0Sstevel@tonic-gate 		do {
139*0Sstevel@tonic-gate 			n2 = n1->dl_next;
140*0Sstevel@tonic-gate 			free(n1->dl_dirname);
141*0Sstevel@tonic-gate 			free(n1->dl_filename);
142*0Sstevel@tonic-gate 			free(n1);
143*0Sstevel@tonic-gate 			n1 = n2;
144*0Sstevel@tonic-gate 		} while (n1 != head);
145*0Sstevel@tonic-gate 	}
146*0Sstevel@tonic-gate }
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate /*
150*0Sstevel@tonic-gate  * add to a linked list of directories available for writing
151*0Sstevel@tonic-gate  *
152*0Sstevel@tonic-gate  */
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate static int
155*0Sstevel@tonic-gate growauditlist(dirlist_t **listhead, char *dirlist,
156*0Sstevel@tonic-gate     dirlist_t *endnode, int *count)
157*0Sstevel@tonic-gate {
158*0Sstevel@tonic-gate 	dirlist_t	*node;
159*0Sstevel@tonic-gate 	char		*bs, *be;
160*0Sstevel@tonic-gate 	dirlist_t	**node_p;
161*0Sstevel@tonic-gate 	char		*dirname;
162*0Sstevel@tonic-gate 	char		*remainder;
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate 	DPRINT((dbfp, "binfile: dirlist=%s\n", dirlist));
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	if (*listhead == NULL)
167*0Sstevel@tonic-gate 		node_p = listhead;
168*0Sstevel@tonic-gate 	else
169*0Sstevel@tonic-gate 		node_p = &(endnode->dl_next);
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate 	node = NULL;
172*0Sstevel@tonic-gate 	while ((dirname = strtok_r(dirlist, ",", &remainder)) != NULL) {
173*0Sstevel@tonic-gate 		dirlist = NULL;
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 		DPRINT((dbfp, "binfile: p_dir = %s\n", dirname));
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate 		(*count)++;
178*0Sstevel@tonic-gate 		node = malloc(sizeof (dirlist_t));
179*0Sstevel@tonic-gate 		if (node == NULL)
180*0Sstevel@tonic-gate 			return (AUDITD_NO_MEMORY);
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate 		node->dl_flags = 0;
183*0Sstevel@tonic-gate 		node->dl_filename = NULL;
184*0Sstevel@tonic-gate 		node->dl_fd = -1;
185*0Sstevel@tonic-gate 		node->dl_space = PLENTY_SPACE;
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate 		node->dl_dirname = malloc((unsigned)strlen(dirname) + 1);
188*0Sstevel@tonic-gate 		if (node->dl_dirname == NULL)
189*0Sstevel@tonic-gate 			return (AUDITD_NO_MEMORY);
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate 		bs = dirname;
192*0Sstevel@tonic-gate 		while ((*bs == ' ') || (*bs == '\t'))	/* trim blanks */
193*0Sstevel@tonic-gate 			bs++;
194*0Sstevel@tonic-gate 		be = bs + strlen(bs) - 1;
195*0Sstevel@tonic-gate 		while (be > bs) {	/* trim trailing blanks */
196*0Sstevel@tonic-gate 			if ((*bs != ' ') && (*bs != '\t'))
197*0Sstevel@tonic-gate 				break;
198*0Sstevel@tonic-gate 			be--;
199*0Sstevel@tonic-gate 		}
200*0Sstevel@tonic-gate 		*(be + 1) = '\0';
201*0Sstevel@tonic-gate 		(void) strlcpy(node->dl_dirname, bs, AUDIT_FNAME_SZ);
202*0Sstevel@tonic-gate 
203*0Sstevel@tonic-gate 		if (*listhead != NULL)
204*0Sstevel@tonic-gate 			node->dl_next = *listhead;
205*0Sstevel@tonic-gate 		else
206*0Sstevel@tonic-gate 			node->dl_next = node;
207*0Sstevel@tonic-gate 		*node_p = node;
208*0Sstevel@tonic-gate 		node_p = &(node->dl_next);
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate 	}
211*0Sstevel@tonic-gate 	return (0);
212*0Sstevel@tonic-gate }
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate /*
215*0Sstevel@tonic-gate  * create a linked list of directories available for writing
216*0Sstevel@tonic-gate  *
217*0Sstevel@tonic-gate  * if a list already exists, the two are compared and the new one is
218*0Sstevel@tonic-gate  * used only if it is different than the old.
219*0Sstevel@tonic-gate  *
220*0Sstevel@tonic-gate  * returns -2 for new or changed list, 0 for unchanged list and -1 for
221*0Sstevel@tonic-gate  * error.  (Positive returns are for AUDITD_<error code> values)
222*0Sstevel@tonic-gate  *
223*0Sstevel@tonic-gate  */
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate static int
226*0Sstevel@tonic-gate loadauditlist(char *dirstr, char *minfreestr)
227*0Sstevel@tonic-gate {
228*0Sstevel@tonic-gate 	char		buf[MAXPATHLEN];
229*0Sstevel@tonic-gate 	char		*bs, *be;
230*0Sstevel@tonic-gate 	dirlist_t	 *node, *n1, *n2;
231*0Sstevel@tonic-gate 	dirlist_t	 **node_p;
232*0Sstevel@tonic-gate 	dirlist_t	*listhead = NULL;
233*0Sstevel@tonic-gate 	dirlist_t	*thisdir;
234*0Sstevel@tonic-gate 	int		acresult;
235*0Sstevel@tonic-gate 	int		node_count = 0;
236*0Sstevel@tonic-gate 	int		rc;
237*0Sstevel@tonic-gate 	int		temp_minfree;
238*0Sstevel@tonic-gate 	au_acinfo_t	*ach;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	static dirlist_t	*activeList = NULL;	/* directory list */
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	DPRINT((dbfp, "binfile: Loading audit list from auditcontrol\n"));
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 	/*
245*0Sstevel@tonic-gate 	 * Build new directory list
246*0Sstevel@tonic-gate 	 */
247*0Sstevel@tonic-gate 	/* part 1 -- using pre Sol 10 audit_control directives */
248*0Sstevel@tonic-gate 	node_p = &listhead;
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate 	ach = _openac(NULL);
251*0Sstevel@tonic-gate 	if (ach == NULL)
252*0Sstevel@tonic-gate 		return (-1);
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate 	/* at least one directory is needed */
255*0Sstevel@tonic-gate 	while ((acresult = _getacdir(ach, buf, sizeof (buf))) == 0 ||
256*0Sstevel@tonic-gate 		acresult == 2 || acresult == -3) {
257*0Sstevel@tonic-gate 		/*
258*0Sstevel@tonic-gate 		 * loop if the result is 0 (success), 2 (a warning
259*0Sstevel@tonic-gate 		 * that the audit_data file has been rewound),
260*0Sstevel@tonic-gate 		 * or -3 (a directory entry was found, but it
261*0Sstevel@tonic-gate 		 * was badly formatted.
262*0Sstevel@tonic-gate 		 */
263*0Sstevel@tonic-gate 		if (acresult == 0) {
264*0Sstevel@tonic-gate 			/*
265*0Sstevel@tonic-gate 			 * A directory entry was found.
266*0Sstevel@tonic-gate 			 */
267*0Sstevel@tonic-gate 			node_count++;
268*0Sstevel@tonic-gate 			node = malloc(sizeof (dirlist_t));
269*0Sstevel@tonic-gate 			if (node == NULL)
270*0Sstevel@tonic-gate 				return (AUDITD_NO_MEMORY);
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 			node->dl_flags = 0;
273*0Sstevel@tonic-gate 			node->dl_fd = -1;
274*0Sstevel@tonic-gate 			node->dl_space = PLENTY_SPACE;
275*0Sstevel@tonic-gate 			node->dl_filename = NULL;
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 			node->dl_dirname = malloc((unsigned)strlen(buf) + 1);
278*0Sstevel@tonic-gate 			if (node->dl_dirname == NULL)
279*0Sstevel@tonic-gate 				return (AUDITD_NO_MEMORY);
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate 			bs = buf;
282*0Sstevel@tonic-gate 			while ((*bs == ' ') || (*bs == '\t'))
283*0Sstevel@tonic-gate 				bs++;
284*0Sstevel@tonic-gate 			be = bs + strlen(bs) - 1;
285*0Sstevel@tonic-gate 			while (be > bs) {	/* trim trailing blanks */
286*0Sstevel@tonic-gate 				if ((*bs != ' ') && (*bs != '\t'))
287*0Sstevel@tonic-gate 					break;
288*0Sstevel@tonic-gate 				be--;
289*0Sstevel@tonic-gate 			}
290*0Sstevel@tonic-gate 			*(be + 1) = '\0';
291*0Sstevel@tonic-gate 			(void) strlcpy(node->dl_dirname, bs, AUDIT_FNAME_SZ);
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 			if (listhead != NULL)
294*0Sstevel@tonic-gate 				node->dl_next = listhead;
295*0Sstevel@tonic-gate 			else
296*0Sstevel@tonic-gate 				node->dl_next = node;
297*0Sstevel@tonic-gate 			*node_p = node;
298*0Sstevel@tonic-gate 			node_p = &(node->dl_next);
299*0Sstevel@tonic-gate 		}
300*0Sstevel@tonic-gate 	}   /* end of getacdir while */
301*0Sstevel@tonic-gate 	/*
302*0Sstevel@tonic-gate 	 * part 2 -- use directories and minfree from the (new as of Sol 10)
303*0Sstevel@tonic-gate 	 * plugin directive
304*0Sstevel@tonic-gate 	 */
305*0Sstevel@tonic-gate 	if (dirstr != NULL) {
306*0Sstevel@tonic-gate 		if (node_count == 0) {
307*0Sstevel@tonic-gate 			listhead = NULL;
308*0Sstevel@tonic-gate 			node = NULL;
309*0Sstevel@tonic-gate 		}
310*0Sstevel@tonic-gate 		rc = growauditlist(&listhead, dirstr, node, &node_count);
311*0Sstevel@tonic-gate 		if (rc)
312*0Sstevel@tonic-gate 			return (rc);
313*0Sstevel@tonic-gate 	}
314*0Sstevel@tonic-gate 	if (node_count == 0) {
315*0Sstevel@tonic-gate 		/*
316*0Sstevel@tonic-gate 		 * there was a problem getting the directory
317*0Sstevel@tonic-gate 		 * list or remote host info from the audit_control file
318*0Sstevel@tonic-gate 		 * even though auditd thought there was at least 1 good
319*0Sstevel@tonic-gate 		 * entry
320*0Sstevel@tonic-gate 		 */
321*0Sstevel@tonic-gate 		DPRINT((dbfp, "binfile: "
322*0Sstevel@tonic-gate 		    "problem getting directory / libpath list "
323*0Sstevel@tonic-gate 		    "from audit_control.\n"));
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 		_endac(ach);
326*0Sstevel@tonic-gate 		return (-1);
327*0Sstevel@tonic-gate 	}
328*0Sstevel@tonic-gate #if DEBUG
329*0Sstevel@tonic-gate 	/* print out directory list */
330*0Sstevel@tonic-gate 
331*0Sstevel@tonic-gate 	if (listhead != NULL) {
332*0Sstevel@tonic-gate 		fprintf(dbfp, "Directory list:\n\t%s\n", listhead->dl_dirname);
333*0Sstevel@tonic-gate 		thisdir = listhead->dl_next;
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate 		while (thisdir != listhead) {
336*0Sstevel@tonic-gate 			fprintf(dbfp, "\t%s\n", thisdir->dl_dirname);
337*0Sstevel@tonic-gate 			thisdir = thisdir->dl_next;
338*0Sstevel@tonic-gate 		}
339*0Sstevel@tonic-gate 	}
340*0Sstevel@tonic-gate #endif	/* DEBUG */
341*0Sstevel@tonic-gate 	thisdir = listhead;
342*0Sstevel@tonic-gate 	/*
343*0Sstevel@tonic-gate 	 * See if the list has changed.
344*0Sstevel@tonic-gate 	 * If there was a change  rc = 0 if no change, else 1
345*0Sstevel@tonic-gate 	 */
346*0Sstevel@tonic-gate 	rc = 0;	/* no change */
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 	if (node_count == activeCount) {
349*0Sstevel@tonic-gate 		n1 = listhead;
350*0Sstevel@tonic-gate 		n2 = activeList;
351*0Sstevel@tonic-gate 		do {
352*0Sstevel@tonic-gate 			if (strcmp(n1->dl_dirname, n2->dl_dirname) != 0) {
353*0Sstevel@tonic-gate 				DPRINT((dbfp,
354*0Sstevel@tonic-gate 				    "binfile: new dirname = %s\n"
355*0Sstevel@tonic-gate 				    "binfile: old dirname = %s\n",
356*0Sstevel@tonic-gate 				    n1->dl_dirname,
357*0Sstevel@tonic-gate 				    n2->dl_dirname));
358*0Sstevel@tonic-gate 				rc = -2;
359*0Sstevel@tonic-gate 				break;
360*0Sstevel@tonic-gate 			}
361*0Sstevel@tonic-gate 			n1 = n1->dl_next;
362*0Sstevel@tonic-gate 			n2 = n2->dl_next;
363*0Sstevel@tonic-gate 		} while ((n1 != listhead) && (n2 != activeList));
364*0Sstevel@tonic-gate 	} else {
365*0Sstevel@tonic-gate 		DPRINT((dbfp, "binfile:  old dir count = %d\n"
366*0Sstevel@tonic-gate 		    "binfile:  new dir count = %d\n",
367*0Sstevel@tonic-gate 		    activeCount, node_count));
368*0Sstevel@tonic-gate 		rc = -2;
369*0Sstevel@tonic-gate 	}
370*0Sstevel@tonic-gate 	if (rc == -2) {
371*0Sstevel@tonic-gate 		(void) pthread_mutex_lock(&log_mutex);
372*0Sstevel@tonic-gate 		DPRINT((dbfp, "loadauditlist:  close / open log\n"));
373*0Sstevel@tonic-gate 		if (open_log(listhead) == 0)
374*0Sstevel@tonic-gate 			openNewFile = 1;	/* try again later */
375*0Sstevel@tonic-gate 		freedirlist(activeList);	/* old list */
376*0Sstevel@tonic-gate 		activeList = listhead;		/* new list */
377*0Sstevel@tonic-gate 		activeDir = thisdir;
378*0Sstevel@tonic-gate 		activeCount = node_count;
379*0Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&log_mutex);
380*0Sstevel@tonic-gate 	} else
381*0Sstevel@tonic-gate 		freedirlist(listhead);
382*0Sstevel@tonic-gate 	/*
383*0Sstevel@tonic-gate 	 * Get the minfree value.  If minfree comes in via the attribute
384*0Sstevel@tonic-gate 	 * list, ignore the possibility it may also be listed on a separate
385*0Sstevel@tonic-gate 	 * audit_control line.
386*0Sstevel@tonic-gate 	 */
387*0Sstevel@tonic-gate 	if (minfreestr != NULL)
388*0Sstevel@tonic-gate 		temp_minfree = atoi(minfreestr);
389*0Sstevel@tonic-gate 	else if (!(_getacmin(ach, &temp_minfree) == 0))
390*0Sstevel@tonic-gate 		temp_minfree = 0;
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate 	if ((temp_minfree < 0) || (temp_minfree > 100))
393*0Sstevel@tonic-gate 		temp_minfree = 0;
394*0Sstevel@tonic-gate 
395*0Sstevel@tonic-gate 	if (minfree != temp_minfree) {
396*0Sstevel@tonic-gate 		DPRINT((dbfp, "minfree:  old = %d, new = %d\n",
397*0Sstevel@tonic-gate 		    minfree, temp_minfree));
398*0Sstevel@tonic-gate 		rc = -2;		/* data change */
399*0Sstevel@tonic-gate 		minfree = temp_minfree;
400*0Sstevel@tonic-gate 	}
401*0Sstevel@tonic-gate 	_endac(ach);
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 	return (rc);
404*0Sstevel@tonic-gate }
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 
407*0Sstevel@tonic-gate /*
408*0Sstevel@tonic-gate  * getauditdate - get the current time (GMT) and put it in the form
409*0Sstevel@tonic-gate  *		  yyyymmddHHMMSS .
410*0Sstevel@tonic-gate  */
411*0Sstevel@tonic-gate static void
412*0Sstevel@tonic-gate getauditdate(char *date)
413*0Sstevel@tonic-gate {
414*0Sstevel@tonic-gate 	struct timeval tp;
415*0Sstevel@tonic-gate 	struct timezone tzp;
416*0Sstevel@tonic-gate 	struct tm tm;
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate 	(void) gettimeofday(&tp, &tzp);
419*0Sstevel@tonic-gate 	tm = *gmtime(&tp.tv_sec);
420*0Sstevel@tonic-gate 	/*
421*0Sstevel@tonic-gate 	 * NOTE:  if we want to use gmtime, we have to be aware that the
422*0Sstevel@tonic-gate 	 *	structure only keeps the year as an offset from TM_YEAR_BASE.
423*0Sstevel@tonic-gate 	 *	I have used TM_YEAR_BASE in this code so that if they change
424*0Sstevel@tonic-gate 	 *	this base from 1900 to 2000, it will hopefully mean that this
425*0Sstevel@tonic-gate 	 *	code does not have to change.  TM_YEAR_BASE is defined in
426*0Sstevel@tonic-gate 	 *	tzfile.h .
427*0Sstevel@tonic-gate 	 */
428*0Sstevel@tonic-gate 	(void) sprintf(date, "%.4d%.2d%.2d%.2d%.2d%.2d",
429*0Sstevel@tonic-gate 		tm.tm_year + TM_YEAR_BASE, tm.tm_mon + 1, tm.tm_mday,
430*0Sstevel@tonic-gate 		tm.tm_hour, tm.tm_min, tm.tm_sec);
431*0Sstevel@tonic-gate }
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate /*
436*0Sstevel@tonic-gate  * write_file_token - put the file token into the audit log
437*0Sstevel@tonic-gate  */
438*0Sstevel@tonic-gate static int
439*0Sstevel@tonic-gate write_file_token(int fd, char *name)
440*0Sstevel@tonic-gate {
441*0Sstevel@tonic-gate 	adr_t adr;					/* xdr ptr */
442*0Sstevel@tonic-gate 	struct timeval tv;				/* time now */
443*0Sstevel@tonic-gate 	char for_adr[AUDIT_FNAME_SZ + AUDIT_FNAME_SZ];	/* plenty of room */
444*0Sstevel@tonic-gate 	char	token_id;
445*0Sstevel@tonic-gate 	short	i;
446*0Sstevel@tonic-gate 
447*0Sstevel@tonic-gate 	(void) gettimeofday(&tv, (struct timezone *)0);
448*0Sstevel@tonic-gate 	i = strlen(name) + 1;
449*0Sstevel@tonic-gate 	adr_start(&adr, for_adr);
450*0Sstevel@tonic-gate #ifdef _LP64
451*0Sstevel@tonic-gate 		token_id = AUT_OTHER_FILE64;
452*0Sstevel@tonic-gate 		adr_char(&adr, &token_id, 1);
453*0Sstevel@tonic-gate 		adr_int64(&adr, (int64_t *)& tv, 2);
454*0Sstevel@tonic-gate #else
455*0Sstevel@tonic-gate 		token_id = AUT_OTHER_FILE32;
456*0Sstevel@tonic-gate 		adr_char(&adr, &token_id, 1);
457*0Sstevel@tonic-gate 		adr_int32(&adr, (int32_t *)& tv, 2);
458*0Sstevel@tonic-gate #endif
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	adr_short(&adr, &i, 1);
461*0Sstevel@tonic-gate 	adr_char(&adr, name, i);
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 	if (write(fd, for_adr, adr_count(&adr)) < 0) {
464*0Sstevel@tonic-gate 		DPRINT((dbfp, "binfile: Bad write\n"));
465*0Sstevel@tonic-gate 		return (errno);
466*0Sstevel@tonic-gate 	}
467*0Sstevel@tonic-gate 	return (0);
468*0Sstevel@tonic-gate }
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate /*
471*0Sstevel@tonic-gate  * close_log - close the file if open.  Also put the name of the
472*0Sstevel@tonic-gate  *	new log file in the trailer, and rename the old file
473*0Sstevel@tonic-gate  *	to oldname.  The caller must hold log_mutext while calling
474*0Sstevel@tonic-gate  *      close_log since any change to activeDir is a complete redo
475*0Sstevel@tonic-gate  *	of all it points to.
476*0Sstevel@tonic-gate  * arguments -
477*0Sstevel@tonic-gate  *	oldname - the new name for the file to be closed
478*0Sstevel@tonic-gate  *	newname - the name of the new log file (for the trailer)
479*0Sstevel@tonic-gate  */
480*0Sstevel@tonic-gate static void
481*0Sstevel@tonic-gate close_log(dirlist_t *currentdir, char *oname, char *newname)
482*0Sstevel@tonic-gate {
483*0Sstevel@tonic-gate 	char	auditdate[AUDIT_DATE_SZ+1];
484*0Sstevel@tonic-gate 	char	*name;
485*0Sstevel@tonic-gate 	char	oldname[AUDIT_FNAME_SZ+1];
486*0Sstevel@tonic-gate 
487*0Sstevel@tonic-gate 	if ((currentdir == NULL) || (currentdir->dl_fd == -1))
488*0Sstevel@tonic-gate 		return;
489*0Sstevel@tonic-gate 	/*
490*0Sstevel@tonic-gate 	 * If oldname is blank, we were called by auditd_plugin_close()
491*0Sstevel@tonic-gate 	 * instead of by open_log, so we need to update our name.
492*0Sstevel@tonic-gate 	 */
493*0Sstevel@tonic-gate 	(void) strlcpy(oldname, oname, AUDIT_FNAME_SZ);
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 	if (strcmp(oldname, "") == 0) {
496*0Sstevel@tonic-gate 		getauditdate(auditdate);
497*0Sstevel@tonic-gate 
498*0Sstevel@tonic-gate 		assert(currentdir->dl_filename != NULL);
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate 		(void) strlcpy(oldname, currentdir->dl_filename,
501*0Sstevel@tonic-gate 		    AUDIT_FNAME_SZ);
502*0Sstevel@tonic-gate 
503*0Sstevel@tonic-gate 		name = strrchr(oldname, '/') + 1;
504*0Sstevel@tonic-gate 		(void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate,
505*0Sstevel@tonic-gate 		    AUDIT_DATE_SZ);
506*0Sstevel@tonic-gate 	}
507*0Sstevel@tonic-gate 	/*
508*0Sstevel@tonic-gate 	 * Write the trailer record and rename and close the file.
509*0Sstevel@tonic-gate 	 * If any of the write, rename, or close fail, ignore it
510*0Sstevel@tonic-gate 	 * since there is not much else we can do and the next open()
511*0Sstevel@tonic-gate 	 * will trigger the necessary full directory logic.
512*0Sstevel@tonic-gate 	 *
513*0Sstevel@tonic-gate 	 * newname is "" if binfile is being closed down.
514*0Sstevel@tonic-gate 	 */
515*0Sstevel@tonic-gate 	(void) write_file_token(currentdir->dl_fd, newname);
516*0Sstevel@tonic-gate 	if (currentdir->dl_fd >= 0)
517*0Sstevel@tonic-gate 		(void) close(currentdir->dl_fd);
518*0Sstevel@tonic-gate 	currentdir->dl_fd = -1;
519*0Sstevel@tonic-gate 	(void) rename(currentdir->dl_filename, oldname);
520*0Sstevel@tonic-gate 
521*0Sstevel@tonic-gate 	DPRINT((dbfp, "binfile: Log closed %s\n", oldname));
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate 	free(currentdir->dl_filename);
524*0Sstevel@tonic-gate 	currentdir->dl_filename = NULL;
525*0Sstevel@tonic-gate }
526*0Sstevel@tonic-gate 
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate /*
529*0Sstevel@tonic-gate  * open_log - open a new file in the current directory.  If a
530*0Sstevel@tonic-gate  *	file is already open, close it.
531*0Sstevel@tonic-gate  *
532*0Sstevel@tonic-gate  *	return 1 if ok, 0 if all directories are full.
533*0Sstevel@tonic-gate  *
534*0Sstevel@tonic-gate  *	lastOpenDir - used to get the oldfile name (and change it),
535*0Sstevel@tonic-gate  *		to close the oldfile.
536*0Sstevel@tonic-gate  *
537*0Sstevel@tonic-gate  * The caller must hold log_mutex while calling open_log.
538*0Sstevel@tonic-gate  *
539*0Sstevel@tonic-gate  */
540*0Sstevel@tonic-gate static int
541*0Sstevel@tonic-gate open_log(dirlist_t *current_dir)
542*0Sstevel@tonic-gate {
543*0Sstevel@tonic-gate 	char	auditdate[AUDIT_DATE_SZ + 1];
544*0Sstevel@tonic-gate 	char	oldname[AUDIT_FNAME_SZ + 1] = "";
545*0Sstevel@tonic-gate 	char	newname[AUDIT_FNAME_SZ + 1];
546*0Sstevel@tonic-gate 	char	*name;			/* pointer into oldname */
547*0Sstevel@tonic-gate 	int	opened;
548*0Sstevel@tonic-gate 	int	error = 0;
549*0Sstevel@tonic-gate 	int	newfd = 0;
550*0Sstevel@tonic-gate 
551*0Sstevel@tonic-gate 	static char		host[MAXHOSTNAMELEN + 1] = "";
552*0Sstevel@tonic-gate 	/* previous directory with open log file */
553*0Sstevel@tonic-gate 	static dirlist_t	*lastOpenDir = NULL;
554*0Sstevel@tonic-gate 
555*0Sstevel@tonic-gate 	if (host[0] == '\0')
556*0Sstevel@tonic-gate 		(void) gethostname(host, MAXHOSTNAMELEN);
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 	/* Get a filename which does not already exist */
559*0Sstevel@tonic-gate 	opened = 0;
560*0Sstevel@tonic-gate 	while (!opened) {
561*0Sstevel@tonic-gate 		getauditdate(auditdate);
562*0Sstevel@tonic-gate 		(void) snprintf(newname, AUDIT_FNAME_SZ,
563*0Sstevel@tonic-gate 		    "%s/%s.not_terminated.%s",
564*0Sstevel@tonic-gate 		    current_dir->dl_dirname, auditdate, host);
565*0Sstevel@tonic-gate 		newfd = open(newname,
566*0Sstevel@tonic-gate 		    O_RDWR | O_APPEND | O_CREAT | O_EXCL, 0600);
567*0Sstevel@tonic-gate 		if (newfd < 0) {
568*0Sstevel@tonic-gate 			switch (errno) {
569*0Sstevel@tonic-gate 			case EEXIST:
570*0Sstevel@tonic-gate 				DPRINT((dbfp,
571*0Sstevel@tonic-gate 				    "open_log says duplicate for %s "
572*0Sstevel@tonic-gate 				    "(will try another)\n", newname));
573*0Sstevel@tonic-gate 				(void) sleep(1);
574*0Sstevel@tonic-gate 				break;
575*0Sstevel@tonic-gate 			default:
576*0Sstevel@tonic-gate 				/* open failed */
577*0Sstevel@tonic-gate 				DPRINT((dbfp,
578*0Sstevel@tonic-gate 				    "open_log says full for %s: %s\n",
579*0Sstevel@tonic-gate 				    newname, strerror(errno)));
580*0Sstevel@tonic-gate 				current_dir->dl_space = SPACE_FULL;
581*0Sstevel@tonic-gate 				current_dir = current_dir->dl_next;
582*0Sstevel@tonic-gate 				return (0);
583*0Sstevel@tonic-gate 			} /* switch */
584*0Sstevel@tonic-gate 		} else
585*0Sstevel@tonic-gate 			opened = 1;
586*0Sstevel@tonic-gate 	} /* while */
587*0Sstevel@tonic-gate 
588*0Sstevel@tonic-gate 	/*
589*0Sstevel@tonic-gate 	 * When we get here, we have opened our new log file.
590*0Sstevel@tonic-gate 	 * Now we need to update the name of the old file to
591*0Sstevel@tonic-gate 	 * store in this file's header.  lastOpenDir may point
592*0Sstevel@tonic-gate 	 * to current_dir if the list is only one entry long and
593*0Sstevel@tonic-gate 	 * there is only one list.
594*0Sstevel@tonic-gate 	 */
595*0Sstevel@tonic-gate 	if ((lastOpenDir != NULL) && (lastOpenDir->dl_filename != NULL)) {
596*0Sstevel@tonic-gate 		(void) strlcpy(oldname, lastOpenDir->dl_filename,
597*0Sstevel@tonic-gate 		    AUDIT_FNAME_SZ);
598*0Sstevel@tonic-gate 		name = (char *)strrchr(oldname, '/') + 1;
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 		(void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate,
601*0Sstevel@tonic-gate 			AUDIT_DATE_SZ);
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate 		close_log(lastOpenDir, oldname, newname);
604*0Sstevel@tonic-gate 	}
605*0Sstevel@tonic-gate 	error = write_file_token(newfd, oldname);
606*0Sstevel@tonic-gate 	if (error) {
607*0Sstevel@tonic-gate 		/* write token failed */
608*0Sstevel@tonic-gate 		(void) close(newfd);
609*0Sstevel@tonic-gate 
610*0Sstevel@tonic-gate 		current_dir->dl_space = SPACE_FULL;
611*0Sstevel@tonic-gate 		current_dir->dl_fd = -1;
612*0Sstevel@tonic-gate 		free(current_dir->dl_filename);
613*0Sstevel@tonic-gate 		current_dir->dl_filename = NULL;
614*0Sstevel@tonic-gate 		current_dir = current_dir->dl_next;
615*0Sstevel@tonic-gate 		return (0);
616*0Sstevel@tonic-gate 	} else {
617*0Sstevel@tonic-gate 		lastOpenDir = current_dir;
618*0Sstevel@tonic-gate 		current_dir->dl_fd = newfd;
619*0Sstevel@tonic-gate 		current_dir->dl_filename = strdup(newname);
620*0Sstevel@tonic-gate 
621*0Sstevel@tonic-gate 		__logpost(newname);
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate 		DPRINT((dbfp, "binfile: Log opened: %s\n", newname));
624*0Sstevel@tonic-gate 		return (1);
625*0Sstevel@tonic-gate 	}
626*0Sstevel@tonic-gate }
627*0Sstevel@tonic-gate 
628*0Sstevel@tonic-gate #define	IGNORE_SIZE	8192
629*0Sstevel@tonic-gate /*
630*0Sstevel@tonic-gate  * spacecheck - determine whether the given directory's filesystem
631*0Sstevel@tonic-gate  *	has the at least the space requested.  Also set the space
632*0Sstevel@tonic-gate  *	value in the directory list structure.  If the caller
633*0Sstevel@tonic-gate  *	passes other than PLENTY_SPACE or SOFT_SPACE, the caller should
634*0Sstevel@tonic-gate  *	ignore the return value.  Otherwise, 0 = less than the
635*0Sstevel@tonic-gate  *	requested space is available, 1 = at least the requested space
636*0Sstevel@tonic-gate  *	is available.
637*0Sstevel@tonic-gate  *
638*0Sstevel@tonic-gate  *	log_mutex must be held by the caller
639*0Sstevel@tonic-gate  *
640*0Sstevel@tonic-gate  *	-1 is returned if stat fails
641*0Sstevel@tonic-gate  *
642*0Sstevel@tonic-gate  * IGNORE_SIZE is one page (Sol 9 / 10 timeframe) and is the default
643*0Sstevel@tonic-gate  * buffer size written for Sol 9 and earlier.  To keep the same accuracy
644*0Sstevel@tonic-gate  * for the soft limit check as before, spacecheck checks for space
645*0Sstevel@tonic-gate  * remaining IGNORE_SIZE bytes.  This reduces the number of statvfs()
646*0Sstevel@tonic-gate  * calls and related math.
647*0Sstevel@tonic-gate  *
648*0Sstevel@tonic-gate  * globals -
649*0Sstevel@tonic-gate  *	minfree - the soft limit, i.e., the % of filesystem to reserve
650*0Sstevel@tonic-gate  */
651*0Sstevel@tonic-gate static int
652*0Sstevel@tonic-gate spacecheck(dirlist_t *thisdir, int test_limit, size_t next_buf_size)
653*0Sstevel@tonic-gate {
654*0Sstevel@tonic-gate 	struct statvfs	sb;
655*0Sstevel@tonic-gate 	static int	ignore_size = 0;
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate 	ignore_size += next_buf_size;
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate 	if ((test_limit == PLENTY_SPACE) && (ignore_size < IGNORE_SIZE))
660*0Sstevel@tonic-gate 		return (1);
661*0Sstevel@tonic-gate 
662*0Sstevel@tonic-gate 	assert(thisdir != NULL);
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 	if (thisdir->dl_space == STAY_FULL) {
665*0Sstevel@tonic-gate 		thisdir->dl_space = SPACE_FULL;
666*0Sstevel@tonic-gate 		minfreeblocks = AVAIL_MIN;
667*0Sstevel@tonic-gate 	} else if (statvfs(thisdir->dl_dirname, &sb) < 0) {
668*0Sstevel@tonic-gate 		thisdir->dl_space = SPACE_FULL;
669*0Sstevel@tonic-gate 		minfreeblocks = AVAIL_MIN;
670*0Sstevel@tonic-gate 		return (-1);
671*0Sstevel@tonic-gate 	} else {
672*0Sstevel@tonic-gate 		minfreeblocks = ((minfree * sb.f_blocks) / 100) + AVAIL_MIN;
673*0Sstevel@tonic-gate 
674*0Sstevel@tonic-gate 		if (sb.f_bavail < AVAIL_MIN)
675*0Sstevel@tonic-gate 			thisdir->dl_space = SPACE_FULL;
676*0Sstevel@tonic-gate 		else if (sb.f_bavail > minfreeblocks)
677*0Sstevel@tonic-gate 			thisdir->dl_space = PLENTY_SPACE;
678*0Sstevel@tonic-gate 		else
679*0Sstevel@tonic-gate 			thisdir->dl_space = SOFT_SPACE;
680*0Sstevel@tonic-gate 	}
681*0Sstevel@tonic-gate 	if (thisdir->dl_space == PLENTY_SPACE)
682*0Sstevel@tonic-gate 		return (1);
683*0Sstevel@tonic-gate 
684*0Sstevel@tonic-gate 	return (thisdir->dl_space == test_limit);
685*0Sstevel@tonic-gate }
686*0Sstevel@tonic-gate 
687*0Sstevel@tonic-gate /*
688*0Sstevel@tonic-gate  * auditd_plugin() writes a buffer to the currently open file The
689*0Sstevel@tonic-gate  * global "openNewFile" is used to force a new log file for the
690*0Sstevel@tonic-gate  * initial open; for "audit -s" with changed audit_control data or
691*0Sstevel@tonic-gate  * "audit -n" the new log file is opened immediately.
692*0Sstevel@tonic-gate  *
693*0Sstevel@tonic-gate  * This function manages one or more audit directories as follows:
694*0Sstevel@tonic-gate  *
695*0Sstevel@tonic-gate  * 	If the current open file is in a directory that has not
696*0Sstevel@tonic-gate  *	reached the soft limit, write the input data and return.
697*0Sstevel@tonic-gate  *
698*0Sstevel@tonic-gate  *	Scan the list of directories for one which has not reached
699*0Sstevel@tonic-gate  *	the soft limit; if one is found, write and return.  Such
700*0Sstevel@tonic-gate  *	a writable directory is in "PLENTY_SPACE" state.
701*0Sstevel@tonic-gate  *
702*0Sstevel@tonic-gate  *	Scan the list of directories for one which has not reached
703*0Sstevel@tonic-gate  *	the hard limit; if one is found, write and return.  This
704*0Sstevel@tonic-gate  *	directory in in "SOFT_SPACE" state.
705*0Sstevel@tonic-gate  *
706*0Sstevel@tonic-gate  * Oh, and if a write fails, handle it like a hard space limit.
707*0Sstevel@tonic-gate  *
708*0Sstevel@tonic-gate  * audit_warn (via __audit_dowarn()) is used to alert an operator
709*0Sstevel@tonic-gate  * at various levels of fullness.
710*0Sstevel@tonic-gate  */
711*0Sstevel@tonic-gate /* ARGSUSED */
712*0Sstevel@tonic-gate auditd_rc_t
713*0Sstevel@tonic-gate auditd_plugin(const char *input, size_t in_len, uint32_t sequence, char **error)
714*0Sstevel@tonic-gate {
715*0Sstevel@tonic-gate 	auditd_rc_t	rc = AUDITD_FAIL;
716*0Sstevel@tonic-gate 	dirlist_t	*startdir;
717*0Sstevel@tonic-gate 	int		open_status;
718*0Sstevel@tonic-gate 	size_t		out_len;
719*0Sstevel@tonic-gate 	/* LINTED */
720*0Sstevel@tonic-gate 	int		statrc;
721*0Sstevel@tonic-gate 	/* avoid excess audit_warnage */
722*0Sstevel@tonic-gate 	static int	somesoftfull_warning = 0;
723*0Sstevel@tonic-gate 	static int	allsoftfull_warning = 0;
724*0Sstevel@tonic-gate #if DEBUG
725*0Sstevel@tonic-gate 	static char	*last_file_written_to = NULL;
726*0Sstevel@tonic-gate 	static uint32_t	last_sequence = 0;
727*0Sstevel@tonic-gate 	static uint32_t	write_count = 0;
728*0Sstevel@tonic-gate 
729*0Sstevel@tonic-gate 	if ((last_sequence > 0) && (sequence != last_sequence + 1))
730*0Sstevel@tonic-gate 		fprintf(dbfp, "binfile: buffer sequence=%d but prev=%d=n",
731*0Sstevel@tonic-gate 				sequence, last_sequence);
732*0Sstevel@tonic-gate 	last_sequence = sequence;
733*0Sstevel@tonic-gate 
734*0Sstevel@tonic-gate 	fprintf(dbfp, "binfile: input seq=%d, len=%d\n",
735*0Sstevel@tonic-gate 		sequence, in_len);
736*0Sstevel@tonic-gate #endif
737*0Sstevel@tonic-gate 	*error = NULL;
738*0Sstevel@tonic-gate 	/*
739*0Sstevel@tonic-gate 	 * lock is for activeDir, referenced by open_log() and close_log()
740*0Sstevel@tonic-gate 	 */
741*0Sstevel@tonic-gate 	(void) pthread_mutex_lock(&log_mutex);
742*0Sstevel@tonic-gate 	startdir = activeDir;
743*0Sstevel@tonic-gate 	while (rc == AUDITD_FAIL) {
744*0Sstevel@tonic-gate 		open_status = 1;
745*0Sstevel@tonic-gate 		if (openNewFile) {
746*0Sstevel@tonic-gate 			open_status = open_log(activeDir);
747*0Sstevel@tonic-gate 			if (open_status == 1)	/* ok */
748*0Sstevel@tonic-gate 				openNewFile = 0;
749*0Sstevel@tonic-gate 		}
750*0Sstevel@tonic-gate 		/*
751*0Sstevel@tonic-gate 		 * consider "space ok" return and error return the same;
752*0Sstevel@tonic-gate 		 * a -1 means spacecheck couldn't check for space.
753*0Sstevel@tonic-gate 		 */
754*0Sstevel@tonic-gate 		if ((open_status == 1) &&
755*0Sstevel@tonic-gate 		    (statrc = spacecheck(activeDir, fullness_state,
756*0Sstevel@tonic-gate 		    in_len)) != 0) {
757*0Sstevel@tonic-gate #if DEBUG
758*0Sstevel@tonic-gate 			DPRINT((dbfp, "binfile: returned from spacecheck\n"));
759*0Sstevel@tonic-gate 			/*
760*0Sstevel@tonic-gate 			 * The last copy of last_file_written_to is
761*0Sstevel@tonic-gate 			 * never free'd, so there will be one open
762*0Sstevel@tonic-gate 			 * memory reference on exit.  It's debug only.
763*0Sstevel@tonic-gate 			 */
764*0Sstevel@tonic-gate 			if ((last_file_written_to != NULL) &&
765*0Sstevel@tonic-gate 			    (strcmp(last_file_written_to,
766*0Sstevel@tonic-gate 			    activeDir->dl_filename) != 0)) {
767*0Sstevel@tonic-gate 				DPRINT((dbfp, "binfile:  now writing to %s\n",
768*0Sstevel@tonic-gate 				    activeDir->dl_filename));
769*0Sstevel@tonic-gate 				free(last_file_written_to);
770*0Sstevel@tonic-gate 			}
771*0Sstevel@tonic-gate 			DPRINT((dbfp, "binfile:  finished some debug stuff\n"));
772*0Sstevel@tonic-gate 			last_file_written_to =
773*0Sstevel@tonic-gate 			    strdup(activeDir->dl_filename);
774*0Sstevel@tonic-gate #endif
775*0Sstevel@tonic-gate 			out_len = write(activeDir->dl_fd, input, in_len);
776*0Sstevel@tonic-gate 			DPRINT((dbfp, "binfile:  finished the write\n"));
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 			if (out_len == in_len) {
779*0Sstevel@tonic-gate 				DPRINT((dbfp,
780*0Sstevel@tonic-gate 				    "binfile: write_count=%u, sequence=%u,"
781*0Sstevel@tonic-gate 				    " l=%u\n",
782*0Sstevel@tonic-gate 				    ++write_count, sequence, out_len));
783*0Sstevel@tonic-gate 				allsoftfull_warning = 0;
784*0Sstevel@tonic-gate 				if (fullness_state == PLENTY_SPACE)
785*0Sstevel@tonic-gate 					somesoftfull_warning = 0;
786*0Sstevel@tonic-gate 
787*0Sstevel@tonic-gate 				rc = AUDITD_SUCCESS;
788*0Sstevel@tonic-gate 				break;
789*0Sstevel@tonic-gate 			} else if (!activeDir->dl_flags & HARD_WARNED) {
790*0Sstevel@tonic-gate 				DPRINT((dbfp,
791*0Sstevel@tonic-gate 				    "binfile: write failed, sequence=%u, "
792*0Sstevel@tonic-gate 				    "l=%u\n", sequence, out_len));
793*0Sstevel@tonic-gate 				DPRINT((dbfp, "hard warning sent.\n"));
794*0Sstevel@tonic-gate 				__audit_dowarn("hard", activeDir->dl_dirname,
795*0Sstevel@tonic-gate 				    0);
796*0Sstevel@tonic-gate 
797*0Sstevel@tonic-gate 				activeDir->dl_flags |= HARD_WARNED;
798*0Sstevel@tonic-gate 			}
799*0Sstevel@tonic-gate 		} else {
800*0Sstevel@tonic-gate 			DPRINT((dbfp, "binfile: statrc=%d, fullness_state=%d\n",
801*0Sstevel@tonic-gate 			    statrc, fullness_state));
802*0Sstevel@tonic-gate 			somesoftfull_warning++;
803*0Sstevel@tonic-gate 			if ((somesoftfull_warning <= activeCount) &&
804*0Sstevel@tonic-gate 			    !(activeDir->dl_flags & SOFT_WARNED)) {
805*0Sstevel@tonic-gate 				DPRINT((dbfp, "soft warning sent\n"));
806*0Sstevel@tonic-gate 				__audit_dowarn("soft",
807*0Sstevel@tonic-gate 				    activeDir->dl_dirname, 0);
808*0Sstevel@tonic-gate 				activeDir->dl_flags |= SOFT_WARNED;
809*0Sstevel@tonic-gate 			}
810*0Sstevel@tonic-gate 			if (!activeDir->dl_flags & HARD_WARNED) {
811*0Sstevel@tonic-gate 				DPRINT((dbfp, "hard warning sent.\n"));
812*0Sstevel@tonic-gate 				__audit_dowarn("hard",
813*0Sstevel@tonic-gate 				activeDir->dl_dirname, 0);
814*0Sstevel@tonic-gate 				activeDir->dl_flags |= HARD_WARNED;
815*0Sstevel@tonic-gate 			}
816*0Sstevel@tonic-gate 		}
817*0Sstevel@tonic-gate 		DPRINT((dbfp, "binfile: activeDir=%s, next=%s\n",
818*0Sstevel@tonic-gate 		    activeDir->dl_dirname, activeDir->dl_next->dl_dirname));
819*0Sstevel@tonic-gate 
820*0Sstevel@tonic-gate 		activeDir = activeDir->dl_next;
821*0Sstevel@tonic-gate 
822*0Sstevel@tonic-gate 		if (activeDir == startdir) {		/* full circle */
823*0Sstevel@tonic-gate 			if (fullness_state == PLENTY_SPACE) {	/* once */
824*0Sstevel@tonic-gate 				fullness_state = SOFT_SPACE;
825*0Sstevel@tonic-gate 				if (allsoftfull_warning == 0) {
826*0Sstevel@tonic-gate 					allsoftfull_warning++;
827*0Sstevel@tonic-gate 					__audit_dowarn("allsoft", "", 0);
828*0Sstevel@tonic-gate 				}
829*0Sstevel@tonic-gate 			} else {			/* full circle twice */
830*0Sstevel@tonic-gate 				__audit_dowarn("allhard", "", ++hung_count);
831*0Sstevel@tonic-gate 				minfreeblocks = AVAIL_MIN;
832*0Sstevel@tonic-gate 				rc = AUDITD_RETRY;
833*0Sstevel@tonic-gate 				*error = strdup(gettext(
834*0Sstevel@tonic-gate 				    "all partitions full\n"));
835*0Sstevel@tonic-gate 				__logpost("");
836*0Sstevel@tonic-gate 			}
837*0Sstevel@tonic-gate 		}
838*0Sstevel@tonic-gate 	}
839*0Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&log_mutex);
840*0Sstevel@tonic-gate 
841*0Sstevel@tonic-gate 	return (rc);
842*0Sstevel@tonic-gate }
843*0Sstevel@tonic-gate 
844*0Sstevel@tonic-gate 
845*0Sstevel@tonic-gate /*
846*0Sstevel@tonic-gate  * the open function uses getacdir() and getacmin to determine which
847*0Sstevel@tonic-gate  * directories to use and when to switch.  It takes no inputs.
848*0Sstevel@tonic-gate  *
849*0Sstevel@tonic-gate  * It may be called multiple times as auditd handles SIGHUP and SIGUSR1
850*0Sstevel@tonic-gate  * corresponding to the audit(1M) flags -s and -n
851*0Sstevel@tonic-gate  *
852*0Sstevel@tonic-gate  * kvlist is NULL only if auditd caught a SIGUSR1, so after the first
853*0Sstevel@tonic-gate  * time open is called, the reason is -s if kvlist != NULL and -n
854*0Sstevel@tonic-gate  * otherwise.
855*0Sstevel@tonic-gate  *
856*0Sstevel@tonic-gate  */
857*0Sstevel@tonic-gate 
858*0Sstevel@tonic-gate auditd_rc_t
859*0Sstevel@tonic-gate auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error)
860*0Sstevel@tonic-gate {
861*0Sstevel@tonic-gate 	int		rc = 0;
862*0Sstevel@tonic-gate 	int		status;
863*0Sstevel@tonic-gate 	int		reason;
864*0Sstevel@tonic-gate 	char		*dirlist;
865*0Sstevel@tonic-gate 	char		*minfree;
866*0Sstevel@tonic-gate 	kva_t		*kv;
867*0Sstevel@tonic-gate 
868*0Sstevel@tonic-gate 	*error = NULL;
869*0Sstevel@tonic-gate 	*ret_list = NULL;
870*0Sstevel@tonic-gate 	kv = (kva_t *)kvlist;
871*0Sstevel@tonic-gate 
872*0Sstevel@tonic-gate 	if (am_open) {
873*0Sstevel@tonic-gate 		if (kvlist == NULL)
874*0Sstevel@tonic-gate 			reason = 1;	/* audit -n */
875*0Sstevel@tonic-gate 		else
876*0Sstevel@tonic-gate 			reason = 2;	/* audit -s */
877*0Sstevel@tonic-gate 	} else {
878*0Sstevel@tonic-gate 		reason = 0;		/* initial open */
879*0Sstevel@tonic-gate #if DEBUG
880*0Sstevel@tonic-gate 		dbfp = __auditd_debug_file_open();
881*0Sstevel@tonic-gate #endif
882*0Sstevel@tonic-gate 	}
883*0Sstevel@tonic-gate 	DPRINT((dbfp, "binfile: am_open=%d, reason=%d\n", am_open, reason));
884*0Sstevel@tonic-gate 
885*0Sstevel@tonic-gate 	am_open = 1;
886*0Sstevel@tonic-gate 
887*0Sstevel@tonic-gate 	if (kvlist == NULL) {
888*0Sstevel@tonic-gate 		dirlist = NULL;
889*0Sstevel@tonic-gate 		minfree = NULL;
890*0Sstevel@tonic-gate 	} else {
891*0Sstevel@tonic-gate 		dirlist = kva_match(kv, "p_dir");
892*0Sstevel@tonic-gate 		minfree = kva_match(kv, "p_minfree");
893*0Sstevel@tonic-gate 	}
894*0Sstevel@tonic-gate 	switch (reason) {
895*0Sstevel@tonic-gate 	case 0:			/* initial open */
896*0Sstevel@tonic-gate 		if (!binfile_is_open)
897*0Sstevel@tonic-gate 			(void) pthread_mutex_init(&log_mutex, NULL);
898*0Sstevel@tonic-gate 		binfile_is_open = 1;
899*0Sstevel@tonic-gate 		openNewFile = 1;
900*0Sstevel@tonic-gate 		/* FALLTHRU */
901*0Sstevel@tonic-gate 	case 2:			/* audit -s */
902*0Sstevel@tonic-gate 		fullness_state = PLENTY_SPACE;
903*0Sstevel@tonic-gate 		status = loadauditlist(dirlist, minfree);
904*0Sstevel@tonic-gate 
905*0Sstevel@tonic-gate 		if (status == -1) {
906*0Sstevel@tonic-gate 			__logpost("");
907*0Sstevel@tonic-gate 			*error = strdup(gettext("no directories configured"));
908*0Sstevel@tonic-gate 			return (AUDITD_RETRY);
909*0Sstevel@tonic-gate 		} else if (status == AUDITD_NO_MEMORY) {
910*0Sstevel@tonic-gate 			__logpost("");
911*0Sstevel@tonic-gate 			*error = strdup(gettext("no memory"));
912*0Sstevel@tonic-gate 			return (status);
913*0Sstevel@tonic-gate 		} else {	/* status is 0 or -2 (no change or changed) */
914*0Sstevel@tonic-gate 			hung_count = 0;
915*0Sstevel@tonic-gate 			DPRINT((dbfp, "binfile: loadauditlist returned %d\n",
916*0Sstevel@tonic-gate 				status));
917*0Sstevel@tonic-gate 		}
918*0Sstevel@tonic-gate 		break;
919*0Sstevel@tonic-gate 	case 1:			/* audit -n */
920*0Sstevel@tonic-gate 		(void) pthread_mutex_lock(&log_mutex);
921*0Sstevel@tonic-gate 		if (open_log(activeDir) == 1)	/* ok */
922*0Sstevel@tonic-gate 			openNewFile = 0;
923*0Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&log_mutex);
924*0Sstevel@tonic-gate 		break;
925*0Sstevel@tonic-gate 	}
926*0Sstevel@tonic-gate 
927*0Sstevel@tonic-gate 	rc = AUDITD_SUCCESS;
928*0Sstevel@tonic-gate 	*ret_list = NULL;
929*0Sstevel@tonic-gate 
930*0Sstevel@tonic-gate 	return (rc);
931*0Sstevel@tonic-gate }
932*0Sstevel@tonic-gate 
933*0Sstevel@tonic-gate auditd_rc_t
934*0Sstevel@tonic-gate auditd_plugin_close(char **error)
935*0Sstevel@tonic-gate {
936*0Sstevel@tonic-gate 	*error = NULL;
937*0Sstevel@tonic-gate 
938*0Sstevel@tonic-gate 	(void) pthread_mutex_lock(&log_mutex);
939*0Sstevel@tonic-gate 	close_log(activeDir, "", "");
940*0Sstevel@tonic-gate 	freedirlist(activeDir);
941*0Sstevel@tonic-gate 	activeDir = NULL;
942*0Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&log_mutex);
943*0Sstevel@tonic-gate 
944*0Sstevel@tonic-gate 	DPRINT((dbfp, "binfile:  closed\n"));
945*0Sstevel@tonic-gate 
946*0Sstevel@tonic-gate 	if (binfile_is_open) {
947*0Sstevel@tonic-gate 		(void) pthread_mutex_destroy(&log_mutex);
948*0Sstevel@tonic-gate 		binfile_is_open = 0;
949*0Sstevel@tonic-gate 		/* LINTED */
950*0Sstevel@tonic-gate 	} else {
951*0Sstevel@tonic-gate 		DPRINT((dbfp,
952*0Sstevel@tonic-gate 		    "auditd_plugin_close() called when already closed."));
953*0Sstevel@tonic-gate 	}
954*0Sstevel@tonic-gate 	am_open = 0;
955*0Sstevel@tonic-gate 	return (AUDITD_SUCCESS);
956*0Sstevel@tonic-gate }
957