10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 55007Spaulson * Common Development and Distribution License (the "License"). 65007Spaulson * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*6697Spr131582 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate * 250Sstevel@tonic-gate * write binary audit records directly to a file. 260Sstevel@tonic-gate */ 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate #define DEBUG 0 300Sstevel@tonic-gate 310Sstevel@tonic-gate #if DEBUG 320Sstevel@tonic-gate #define DPRINT(x) {fprintf x; } 330Sstevel@tonic-gate #else 340Sstevel@tonic-gate #define DPRINT(x) 350Sstevel@tonic-gate #endif 360Sstevel@tonic-gate 370Sstevel@tonic-gate /* 380Sstevel@tonic-gate * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close() 390Sstevel@tonic-gate * implement a replacable library for use by auditd; they are a 400Sstevel@tonic-gate * project private interface and may change without notice. 410Sstevel@tonic-gate * 420Sstevel@tonic-gate */ 430Sstevel@tonic-gate 440Sstevel@tonic-gate #include <assert.h> 450Sstevel@tonic-gate #include <bsm/audit.h> 460Sstevel@tonic-gate #include <bsm/audit_record.h> 470Sstevel@tonic-gate #include <bsm/libbsm.h> 480Sstevel@tonic-gate #include <errno.h> 490Sstevel@tonic-gate #include <fcntl.h> 500Sstevel@tonic-gate #include <libintl.h> 510Sstevel@tonic-gate #include <netdb.h> 520Sstevel@tonic-gate #include <pthread.h> 530Sstevel@tonic-gate #include <secdb.h> 540Sstevel@tonic-gate #include <signal.h> 550Sstevel@tonic-gate #include <stdio.h> 560Sstevel@tonic-gate #include <stdlib.h> 570Sstevel@tonic-gate #include <string.h> 580Sstevel@tonic-gate #include <sys/param.h> 590Sstevel@tonic-gate #include <sys/types.h> 600Sstevel@tonic-gate #include <time.h> 610Sstevel@tonic-gate #include <tzfile.h> 620Sstevel@tonic-gate #include <unistd.h> 630Sstevel@tonic-gate #include <sys/vfs.h> 64*6697Spr131582 #include <limits.h> 65*6697Spr131582 #include <syslog.h> 660Sstevel@tonic-gate #include <security/auditd.h> 670Sstevel@tonic-gate #include <audit_plugin.h> 680Sstevel@tonic-gate 690Sstevel@tonic-gate #define AUDIT_DATE_SZ 14 700Sstevel@tonic-gate #define AUDIT_FNAME_SZ 2 * AUDIT_DATE_SZ + 2 + MAXHOSTNAMELEN 710Sstevel@tonic-gate #define AUDIT_BAK_SZ 50 /* size of name of audit_data back-up file */ 720Sstevel@tonic-gate 730Sstevel@tonic-gate /* per-directory status */ 740Sstevel@tonic-gate #define SOFT_SPACE 0 /* minfree or less space available */ 750Sstevel@tonic-gate #define PLENTY_SPACE 1 /* more than minfree available */ 760Sstevel@tonic-gate #define SPACE_FULL 2 /* out of space */ 770Sstevel@tonic-gate 780Sstevel@tonic-gate #define AVAIL_MIN 50 /* If there are less that this number */ 790Sstevel@tonic-gate /* of blocks avail, the filesystem is */ 800Sstevel@tonic-gate /* presumed full. */ 810Sstevel@tonic-gate 825007Spaulson #define ALLHARD_DELAY 20 /* Call audit_warn(allhard) every 20 seconds */ 835007Spaulson 84*6697Spr131582 /* minimum reasonable size in bytes to roll over an audit file */ 85*6697Spr131582 #define FSIZE_MIN 512000 860Sstevel@tonic-gate 870Sstevel@tonic-gate /* 880Sstevel@tonic-gate * The directory list is a circular linked list. It is pointed into by 890Sstevel@tonic-gate * activeDir. Each element contains the pointer to the next 900Sstevel@tonic-gate * element, the directory pathname, a flag for how much space there is 910Sstevel@tonic-gate * in the directory's filesystem, and a file handle. Since a new 920Sstevel@tonic-gate * directory list can be created from auditd_plugin_open() while the 930Sstevel@tonic-gate * current list is in use, activeDir is protected by log_mutex. 940Sstevel@tonic-gate */ 950Sstevel@tonic-gate typedef struct dirlist_s dirlist_t; 960Sstevel@tonic-gate struct dirlist_s { 970Sstevel@tonic-gate dirlist_t *dl_next; 980Sstevel@tonic-gate int dl_space; 990Sstevel@tonic-gate int dl_flags; 1000Sstevel@tonic-gate char *dl_dirname; 1010Sstevel@tonic-gate char *dl_filename; /* file name (not path) if open */ 1020Sstevel@tonic-gate int dl_fd; /* file handle, -1 unless open */ 1030Sstevel@tonic-gate }; 1040Sstevel@tonic-gate /* 1050Sstevel@tonic-gate * Defines for dl_flags 1060Sstevel@tonic-gate */ 1070Sstevel@tonic-gate #define SOFT_WARNED 0x0001 /* already did soft warning for this dir */ 1080Sstevel@tonic-gate #define HARD_WARNED 0x0002 /* already did hard warning for this dir */ 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate #if DEBUG 1110Sstevel@tonic-gate static FILE *dbfp; /* debug file */ 1120Sstevel@tonic-gate #endif 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate static pthread_mutex_t log_mutex; 1150Sstevel@tonic-gate static int binfile_is_open = 0; 1160Sstevel@tonic-gate 1170Sstevel@tonic-gate static int minfree = -1; 1180Sstevel@tonic-gate static int minfreeblocks; /* minfree in blocks */ 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate static dirlist_t *activeDir = NULL; /* current directory */ 1215007Spaulson static dirlist_t *startdir; /* first dir in the ring */ 1220Sstevel@tonic-gate static int activeCount = 0; /* number of dirs in the ring */ 1230Sstevel@tonic-gate 1245007Spaulson static int openNewFile = 0; /* need to open a new file */ 1250Sstevel@tonic-gate static int hung_count = 0; /* count of audit_warn hard */ 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate /* flag from audit_plugin_open to audit_plugin_close */ 1280Sstevel@tonic-gate static int am_open = 0; 1290Sstevel@tonic-gate /* preferred dir state */ 1300Sstevel@tonic-gate static int fullness_state = PLENTY_SPACE; 1310Sstevel@tonic-gate 132*6697Spr131582 /* 133*6697Spr131582 * These are used to implement a maximum size for the auditing 134*6697Spr131582 * file. binfile_maxsize is set via the 'p_fsize' parameter to the 135*6697Spr131582 * audit_binfile plugin. 136*6697Spr131582 */ 137*6697Spr131582 static uint_t binfile_cursize = 0; 138*6697Spr131582 static uint_t binfile_maxsize = 0; 139*6697Spr131582 1400Sstevel@tonic-gate static int open_log(dirlist_t *); 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate static void 1430Sstevel@tonic-gate freedirlist(dirlist_t *head) 1440Sstevel@tonic-gate { 1450Sstevel@tonic-gate dirlist_t *n1, *n2; 1460Sstevel@tonic-gate /* 1470Sstevel@tonic-gate * Free up the old directory list if any 1480Sstevel@tonic-gate */ 1490Sstevel@tonic-gate if (head != NULL) { 1500Sstevel@tonic-gate n1 = head; 1510Sstevel@tonic-gate do { 1520Sstevel@tonic-gate n2 = n1->dl_next; 1530Sstevel@tonic-gate free(n1->dl_dirname); 1540Sstevel@tonic-gate free(n1->dl_filename); 1550Sstevel@tonic-gate free(n1); 1560Sstevel@tonic-gate n1 = n2; 1570Sstevel@tonic-gate } while (n1 != head); 1580Sstevel@tonic-gate } 1590Sstevel@tonic-gate } 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate /* 1630Sstevel@tonic-gate * add to a linked list of directories available for writing 1640Sstevel@tonic-gate * 1650Sstevel@tonic-gate */ 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate static int 1680Sstevel@tonic-gate growauditlist(dirlist_t **listhead, char *dirlist, 1690Sstevel@tonic-gate dirlist_t *endnode, int *count) 1700Sstevel@tonic-gate { 1710Sstevel@tonic-gate dirlist_t *node; 1720Sstevel@tonic-gate char *bs, *be; 1730Sstevel@tonic-gate dirlist_t **node_p; 1740Sstevel@tonic-gate char *dirname; 1750Sstevel@tonic-gate char *remainder; 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate DPRINT((dbfp, "binfile: dirlist=%s\n", dirlist)); 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate if (*listhead == NULL) 1800Sstevel@tonic-gate node_p = listhead; 1810Sstevel@tonic-gate else 1820Sstevel@tonic-gate node_p = &(endnode->dl_next); 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate node = NULL; 1850Sstevel@tonic-gate while ((dirname = strtok_r(dirlist, ",", &remainder)) != NULL) { 1860Sstevel@tonic-gate dirlist = NULL; 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate DPRINT((dbfp, "binfile: p_dir = %s\n", dirname)); 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate (*count)++; 1910Sstevel@tonic-gate node = malloc(sizeof (dirlist_t)); 1920Sstevel@tonic-gate if (node == NULL) 1930Sstevel@tonic-gate return (AUDITD_NO_MEMORY); 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate node->dl_flags = 0; 1960Sstevel@tonic-gate node->dl_filename = NULL; 1970Sstevel@tonic-gate node->dl_fd = -1; 1980Sstevel@tonic-gate node->dl_space = PLENTY_SPACE; 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate node->dl_dirname = malloc((unsigned)strlen(dirname) + 1); 2010Sstevel@tonic-gate if (node->dl_dirname == NULL) 2020Sstevel@tonic-gate return (AUDITD_NO_MEMORY); 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate bs = dirname; 2050Sstevel@tonic-gate while ((*bs == ' ') || (*bs == '\t')) /* trim blanks */ 2060Sstevel@tonic-gate bs++; 2070Sstevel@tonic-gate be = bs + strlen(bs) - 1; 2080Sstevel@tonic-gate while (be > bs) { /* trim trailing blanks */ 2090Sstevel@tonic-gate if ((*bs != ' ') && (*bs != '\t')) 2100Sstevel@tonic-gate break; 2110Sstevel@tonic-gate be--; 2120Sstevel@tonic-gate } 2130Sstevel@tonic-gate *(be + 1) = '\0'; 2140Sstevel@tonic-gate (void) strlcpy(node->dl_dirname, bs, AUDIT_FNAME_SZ); 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate if (*listhead != NULL) 2170Sstevel@tonic-gate node->dl_next = *listhead; 2180Sstevel@tonic-gate else 2190Sstevel@tonic-gate node->dl_next = node; 2200Sstevel@tonic-gate *node_p = node; 2210Sstevel@tonic-gate node_p = &(node->dl_next); 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate return (0); 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate /* 2280Sstevel@tonic-gate * create a linked list of directories available for writing 2290Sstevel@tonic-gate * 2300Sstevel@tonic-gate * if a list already exists, the two are compared and the new one is 2310Sstevel@tonic-gate * used only if it is different than the old. 2320Sstevel@tonic-gate * 2330Sstevel@tonic-gate * returns -2 for new or changed list, 0 for unchanged list and -1 for 2340Sstevel@tonic-gate * error. (Positive returns are for AUDITD_<error code> values) 2350Sstevel@tonic-gate * 2360Sstevel@tonic-gate */ 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate static int 2390Sstevel@tonic-gate loadauditlist(char *dirstr, char *minfreestr) 2400Sstevel@tonic-gate { 2410Sstevel@tonic-gate char buf[MAXPATHLEN]; 2420Sstevel@tonic-gate char *bs, *be; 2430Sstevel@tonic-gate dirlist_t *node, *n1, *n2; 2440Sstevel@tonic-gate dirlist_t **node_p; 2450Sstevel@tonic-gate dirlist_t *listhead = NULL; 2460Sstevel@tonic-gate dirlist_t *thisdir; 2470Sstevel@tonic-gate int acresult; 2480Sstevel@tonic-gate int node_count = 0; 2490Sstevel@tonic-gate int rc; 2500Sstevel@tonic-gate int temp_minfree; 2510Sstevel@tonic-gate au_acinfo_t *ach; 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate static dirlist_t *activeList = NULL; /* directory list */ 2540Sstevel@tonic-gate 2550Sstevel@tonic-gate DPRINT((dbfp, "binfile: Loading audit list from auditcontrol\n")); 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate /* 2580Sstevel@tonic-gate * Build new directory list 2590Sstevel@tonic-gate */ 2600Sstevel@tonic-gate /* part 1 -- using pre Sol 10 audit_control directives */ 2610Sstevel@tonic-gate node_p = &listhead; 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate ach = _openac(NULL); 2640Sstevel@tonic-gate if (ach == NULL) 2650Sstevel@tonic-gate return (-1); 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate /* at least one directory is needed */ 2680Sstevel@tonic-gate while ((acresult = _getacdir(ach, buf, sizeof (buf))) == 0 || 2695007Spaulson acresult == 2 || acresult == -3) { 2700Sstevel@tonic-gate /* 2710Sstevel@tonic-gate * loop if the result is 0 (success), 2 (a warning 2720Sstevel@tonic-gate * that the audit_data file has been rewound), 2730Sstevel@tonic-gate * or -3 (a directory entry was found, but it 2740Sstevel@tonic-gate * was badly formatted. 2750Sstevel@tonic-gate */ 2760Sstevel@tonic-gate if (acresult == 0) { 2770Sstevel@tonic-gate /* 2780Sstevel@tonic-gate * A directory entry was found. 2790Sstevel@tonic-gate */ 2800Sstevel@tonic-gate node_count++; 2810Sstevel@tonic-gate node = malloc(sizeof (dirlist_t)); 2820Sstevel@tonic-gate if (node == NULL) 2830Sstevel@tonic-gate return (AUDITD_NO_MEMORY); 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate node->dl_flags = 0; 2860Sstevel@tonic-gate node->dl_fd = -1; 2870Sstevel@tonic-gate node->dl_space = PLENTY_SPACE; 2880Sstevel@tonic-gate node->dl_filename = NULL; 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate node->dl_dirname = malloc((unsigned)strlen(buf) + 1); 2910Sstevel@tonic-gate if (node->dl_dirname == NULL) 2920Sstevel@tonic-gate return (AUDITD_NO_MEMORY); 2930Sstevel@tonic-gate 2940Sstevel@tonic-gate bs = buf; 2950Sstevel@tonic-gate while ((*bs == ' ') || (*bs == '\t')) 2960Sstevel@tonic-gate bs++; 2970Sstevel@tonic-gate be = bs + strlen(bs) - 1; 2980Sstevel@tonic-gate while (be > bs) { /* trim trailing blanks */ 2990Sstevel@tonic-gate if ((*bs != ' ') && (*bs != '\t')) 3000Sstevel@tonic-gate break; 3010Sstevel@tonic-gate be--; 3020Sstevel@tonic-gate } 3030Sstevel@tonic-gate *(be + 1) = '\0'; 3040Sstevel@tonic-gate (void) strlcpy(node->dl_dirname, bs, AUDIT_FNAME_SZ); 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate if (listhead != NULL) 3070Sstevel@tonic-gate node->dl_next = listhead; 3080Sstevel@tonic-gate else 3090Sstevel@tonic-gate node->dl_next = node; 3100Sstevel@tonic-gate *node_p = node; 3110Sstevel@tonic-gate node_p = &(node->dl_next); 3120Sstevel@tonic-gate } 3130Sstevel@tonic-gate } /* end of getacdir while */ 3140Sstevel@tonic-gate /* 3150Sstevel@tonic-gate * part 2 -- use directories and minfree from the (new as of Sol 10) 3160Sstevel@tonic-gate * plugin directive 3170Sstevel@tonic-gate */ 3180Sstevel@tonic-gate if (dirstr != NULL) { 3190Sstevel@tonic-gate if (node_count == 0) { 3200Sstevel@tonic-gate listhead = NULL; 3210Sstevel@tonic-gate node = NULL; 3220Sstevel@tonic-gate } 3230Sstevel@tonic-gate rc = growauditlist(&listhead, dirstr, node, &node_count); 3240Sstevel@tonic-gate if (rc) 3250Sstevel@tonic-gate return (rc); 3260Sstevel@tonic-gate } 3270Sstevel@tonic-gate if (node_count == 0) { 3280Sstevel@tonic-gate /* 3290Sstevel@tonic-gate * there was a problem getting the directory 3300Sstevel@tonic-gate * list or remote host info from the audit_control file 3310Sstevel@tonic-gate * even though auditd thought there was at least 1 good 3320Sstevel@tonic-gate * entry 3330Sstevel@tonic-gate */ 3340Sstevel@tonic-gate DPRINT((dbfp, "binfile: " 3350Sstevel@tonic-gate "problem getting directory / libpath list " 3360Sstevel@tonic-gate "from audit_control.\n")); 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate _endac(ach); 3390Sstevel@tonic-gate return (-1); 3400Sstevel@tonic-gate } 3410Sstevel@tonic-gate #if DEBUG 3420Sstevel@tonic-gate /* print out directory list */ 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate if (listhead != NULL) { 3450Sstevel@tonic-gate fprintf(dbfp, "Directory list:\n\t%s\n", listhead->dl_dirname); 3460Sstevel@tonic-gate thisdir = listhead->dl_next; 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate while (thisdir != listhead) { 3490Sstevel@tonic-gate fprintf(dbfp, "\t%s\n", thisdir->dl_dirname); 3500Sstevel@tonic-gate thisdir = thisdir->dl_next; 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate } 3530Sstevel@tonic-gate #endif /* DEBUG */ 3540Sstevel@tonic-gate thisdir = listhead; 3550Sstevel@tonic-gate /* 3560Sstevel@tonic-gate * See if the list has changed. 3570Sstevel@tonic-gate * If there was a change rc = 0 if no change, else 1 3580Sstevel@tonic-gate */ 3590Sstevel@tonic-gate rc = 0; /* no change */ 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate if (node_count == activeCount) { 3620Sstevel@tonic-gate n1 = listhead; 3630Sstevel@tonic-gate n2 = activeList; 3640Sstevel@tonic-gate do { 3650Sstevel@tonic-gate if (strcmp(n1->dl_dirname, n2->dl_dirname) != 0) { 3660Sstevel@tonic-gate DPRINT((dbfp, 3670Sstevel@tonic-gate "binfile: new dirname = %s\n" 3680Sstevel@tonic-gate "binfile: old dirname = %s\n", 3690Sstevel@tonic-gate n1->dl_dirname, 3700Sstevel@tonic-gate n2->dl_dirname)); 3710Sstevel@tonic-gate rc = -2; 3720Sstevel@tonic-gate break; 3730Sstevel@tonic-gate } 3740Sstevel@tonic-gate n1 = n1->dl_next; 3750Sstevel@tonic-gate n2 = n2->dl_next; 3760Sstevel@tonic-gate } while ((n1 != listhead) && (n2 != activeList)); 3770Sstevel@tonic-gate } else { 3780Sstevel@tonic-gate DPRINT((dbfp, "binfile: old dir count = %d\n" 3790Sstevel@tonic-gate "binfile: new dir count = %d\n", 3800Sstevel@tonic-gate activeCount, node_count)); 3810Sstevel@tonic-gate rc = -2; 3820Sstevel@tonic-gate } 3830Sstevel@tonic-gate if (rc == -2) { 3840Sstevel@tonic-gate (void) pthread_mutex_lock(&log_mutex); 3850Sstevel@tonic-gate DPRINT((dbfp, "loadauditlist: close / open log\n")); 3865007Spaulson if (open_log(listhead) == 0) { 3870Sstevel@tonic-gate openNewFile = 1; /* try again later */ 3885007Spaulson } else { 3895007Spaulson openNewFile = 0; 3905007Spaulson } 3910Sstevel@tonic-gate freedirlist(activeList); /* old list */ 3920Sstevel@tonic-gate activeList = listhead; /* new list */ 3935007Spaulson activeDir = startdir = thisdir; 3940Sstevel@tonic-gate activeCount = node_count; 3950Sstevel@tonic-gate (void) pthread_mutex_unlock(&log_mutex); 3960Sstevel@tonic-gate } else 3970Sstevel@tonic-gate freedirlist(listhead); 3980Sstevel@tonic-gate /* 3990Sstevel@tonic-gate * Get the minfree value. If minfree comes in via the attribute 4000Sstevel@tonic-gate * list, ignore the possibility it may also be listed on a separate 4010Sstevel@tonic-gate * audit_control line. 4020Sstevel@tonic-gate */ 4030Sstevel@tonic-gate if (minfreestr != NULL) 4040Sstevel@tonic-gate temp_minfree = atoi(minfreestr); 4050Sstevel@tonic-gate else if (!(_getacmin(ach, &temp_minfree) == 0)) 4060Sstevel@tonic-gate temp_minfree = 0; 4070Sstevel@tonic-gate 4080Sstevel@tonic-gate if ((temp_minfree < 0) || (temp_minfree > 100)) 4090Sstevel@tonic-gate temp_minfree = 0; 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate if (minfree != temp_minfree) { 4120Sstevel@tonic-gate DPRINT((dbfp, "minfree: old = %d, new = %d\n", 4130Sstevel@tonic-gate minfree, temp_minfree)); 4140Sstevel@tonic-gate rc = -2; /* data change */ 4150Sstevel@tonic-gate minfree = temp_minfree; 4160Sstevel@tonic-gate } 4170Sstevel@tonic-gate _endac(ach); 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate return (rc); 4200Sstevel@tonic-gate } 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate /* 4240Sstevel@tonic-gate * getauditdate - get the current time (GMT) and put it in the form 4250Sstevel@tonic-gate * yyyymmddHHMMSS . 4260Sstevel@tonic-gate */ 4270Sstevel@tonic-gate static void 4280Sstevel@tonic-gate getauditdate(char *date) 4290Sstevel@tonic-gate { 4300Sstevel@tonic-gate struct timeval tp; 4310Sstevel@tonic-gate struct timezone tzp; 4320Sstevel@tonic-gate struct tm tm; 4330Sstevel@tonic-gate 4340Sstevel@tonic-gate (void) gettimeofday(&tp, &tzp); 4350Sstevel@tonic-gate tm = *gmtime(&tp.tv_sec); 4360Sstevel@tonic-gate /* 4370Sstevel@tonic-gate * NOTE: if we want to use gmtime, we have to be aware that the 4380Sstevel@tonic-gate * structure only keeps the year as an offset from TM_YEAR_BASE. 4390Sstevel@tonic-gate * I have used TM_YEAR_BASE in this code so that if they change 4400Sstevel@tonic-gate * this base from 1900 to 2000, it will hopefully mean that this 4410Sstevel@tonic-gate * code does not have to change. TM_YEAR_BASE is defined in 4420Sstevel@tonic-gate * tzfile.h . 4430Sstevel@tonic-gate */ 4440Sstevel@tonic-gate (void) sprintf(date, "%.4d%.2d%.2d%.2d%.2d%.2d", 4455007Spaulson tm.tm_year + TM_YEAR_BASE, tm.tm_mon + 1, tm.tm_mday, 4465007Spaulson tm.tm_hour, tm.tm_min, tm.tm_sec); 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate 4500Sstevel@tonic-gate 4510Sstevel@tonic-gate /* 4520Sstevel@tonic-gate * write_file_token - put the file token into the audit log 4530Sstevel@tonic-gate */ 4540Sstevel@tonic-gate static int 4550Sstevel@tonic-gate write_file_token(int fd, char *name) 4560Sstevel@tonic-gate { 4570Sstevel@tonic-gate adr_t adr; /* xdr ptr */ 4580Sstevel@tonic-gate struct timeval tv; /* time now */ 4590Sstevel@tonic-gate char for_adr[AUDIT_FNAME_SZ + AUDIT_FNAME_SZ]; /* plenty of room */ 4600Sstevel@tonic-gate char token_id; 4610Sstevel@tonic-gate short i; 4620Sstevel@tonic-gate 4630Sstevel@tonic-gate (void) gettimeofday(&tv, (struct timezone *)0); 4640Sstevel@tonic-gate i = strlen(name) + 1; 4650Sstevel@tonic-gate adr_start(&adr, for_adr); 4660Sstevel@tonic-gate #ifdef _LP64 4670Sstevel@tonic-gate token_id = AUT_OTHER_FILE64; 4680Sstevel@tonic-gate adr_char(&adr, &token_id, 1); 4690Sstevel@tonic-gate adr_int64(&adr, (int64_t *)& tv, 2); 4700Sstevel@tonic-gate #else 4710Sstevel@tonic-gate token_id = AUT_OTHER_FILE32; 4720Sstevel@tonic-gate adr_char(&adr, &token_id, 1); 4730Sstevel@tonic-gate adr_int32(&adr, (int32_t *)& tv, 2); 4740Sstevel@tonic-gate #endif 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate adr_short(&adr, &i, 1); 4770Sstevel@tonic-gate adr_char(&adr, name, i); 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate if (write(fd, for_adr, adr_count(&adr)) < 0) { 4800Sstevel@tonic-gate DPRINT((dbfp, "binfile: Bad write\n")); 4810Sstevel@tonic-gate return (errno); 4820Sstevel@tonic-gate } 4830Sstevel@tonic-gate return (0); 4840Sstevel@tonic-gate } 4850Sstevel@tonic-gate 4860Sstevel@tonic-gate /* 4870Sstevel@tonic-gate * close_log - close the file if open. Also put the name of the 4880Sstevel@tonic-gate * new log file in the trailer, and rename the old file 4890Sstevel@tonic-gate * to oldname. The caller must hold log_mutext while calling 4900Sstevel@tonic-gate * close_log since any change to activeDir is a complete redo 4910Sstevel@tonic-gate * of all it points to. 4920Sstevel@tonic-gate * arguments - 4930Sstevel@tonic-gate * oldname - the new name for the file to be closed 4940Sstevel@tonic-gate * newname - the name of the new log file (for the trailer) 4950Sstevel@tonic-gate */ 4960Sstevel@tonic-gate static void 4970Sstevel@tonic-gate close_log(dirlist_t *currentdir, char *oname, char *newname) 4980Sstevel@tonic-gate { 4990Sstevel@tonic-gate char auditdate[AUDIT_DATE_SZ+1]; 5000Sstevel@tonic-gate char *name; 5010Sstevel@tonic-gate char oldname[AUDIT_FNAME_SZ+1]; 5020Sstevel@tonic-gate 5030Sstevel@tonic-gate if ((currentdir == NULL) || (currentdir->dl_fd == -1)) 5040Sstevel@tonic-gate return; 5050Sstevel@tonic-gate /* 5060Sstevel@tonic-gate * If oldname is blank, we were called by auditd_plugin_close() 5070Sstevel@tonic-gate * instead of by open_log, so we need to update our name. 5080Sstevel@tonic-gate */ 5090Sstevel@tonic-gate (void) strlcpy(oldname, oname, AUDIT_FNAME_SZ); 5100Sstevel@tonic-gate 5110Sstevel@tonic-gate if (strcmp(oldname, "") == 0) { 5120Sstevel@tonic-gate getauditdate(auditdate); 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate assert(currentdir->dl_filename != NULL); 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate (void) strlcpy(oldname, currentdir->dl_filename, 5170Sstevel@tonic-gate AUDIT_FNAME_SZ); 5180Sstevel@tonic-gate 5190Sstevel@tonic-gate name = strrchr(oldname, '/') + 1; 5200Sstevel@tonic-gate (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate, 5210Sstevel@tonic-gate AUDIT_DATE_SZ); 5220Sstevel@tonic-gate } 5230Sstevel@tonic-gate /* 5240Sstevel@tonic-gate * Write the trailer record and rename and close the file. 5250Sstevel@tonic-gate * If any of the write, rename, or close fail, ignore it 5260Sstevel@tonic-gate * since there is not much else we can do and the next open() 5270Sstevel@tonic-gate * will trigger the necessary full directory logic. 5280Sstevel@tonic-gate * 5290Sstevel@tonic-gate * newname is "" if binfile is being closed down. 5300Sstevel@tonic-gate */ 5310Sstevel@tonic-gate (void) write_file_token(currentdir->dl_fd, newname); 5325319Stz204579 if (currentdir->dl_fd >= 0) { 5335319Stz204579 (void) fsync(currentdir->dl_fd); 5340Sstevel@tonic-gate (void) close(currentdir->dl_fd); 5355319Stz204579 } 5360Sstevel@tonic-gate currentdir->dl_fd = -1; 5370Sstevel@tonic-gate (void) rename(currentdir->dl_filename, oldname); 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate DPRINT((dbfp, "binfile: Log closed %s\n", oldname)); 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate free(currentdir->dl_filename); 5420Sstevel@tonic-gate currentdir->dl_filename = NULL; 5430Sstevel@tonic-gate } 5440Sstevel@tonic-gate 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate /* 5470Sstevel@tonic-gate * open_log - open a new file in the current directory. If a 5480Sstevel@tonic-gate * file is already open, close it. 5490Sstevel@tonic-gate * 5500Sstevel@tonic-gate * return 1 if ok, 0 if all directories are full. 5510Sstevel@tonic-gate * 5520Sstevel@tonic-gate * lastOpenDir - used to get the oldfile name (and change it), 5530Sstevel@tonic-gate * to close the oldfile. 5540Sstevel@tonic-gate * 5550Sstevel@tonic-gate * The caller must hold log_mutex while calling open_log. 5560Sstevel@tonic-gate * 5570Sstevel@tonic-gate */ 5580Sstevel@tonic-gate static int 5590Sstevel@tonic-gate open_log(dirlist_t *current_dir) 5600Sstevel@tonic-gate { 5610Sstevel@tonic-gate char auditdate[AUDIT_DATE_SZ + 1]; 5620Sstevel@tonic-gate char oldname[AUDIT_FNAME_SZ + 1] = ""; 5630Sstevel@tonic-gate char newname[AUDIT_FNAME_SZ + 1]; 5640Sstevel@tonic-gate char *name; /* pointer into oldname */ 5650Sstevel@tonic-gate int opened; 5660Sstevel@tonic-gate int error = 0; 5670Sstevel@tonic-gate int newfd = 0; 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate static char host[MAXHOSTNAMELEN + 1] = ""; 5700Sstevel@tonic-gate /* previous directory with open log file */ 5710Sstevel@tonic-gate static dirlist_t *lastOpenDir = NULL; 5720Sstevel@tonic-gate 5730Sstevel@tonic-gate if (host[0] == '\0') 5740Sstevel@tonic-gate (void) gethostname(host, MAXHOSTNAMELEN); 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate /* Get a filename which does not already exist */ 5770Sstevel@tonic-gate opened = 0; 5780Sstevel@tonic-gate while (!opened) { 5790Sstevel@tonic-gate getauditdate(auditdate); 5800Sstevel@tonic-gate (void) snprintf(newname, AUDIT_FNAME_SZ, 5810Sstevel@tonic-gate "%s/%s.not_terminated.%s", 5820Sstevel@tonic-gate current_dir->dl_dirname, auditdate, host); 5830Sstevel@tonic-gate newfd = open(newname, 584832Sme23304 O_RDWR | O_APPEND | O_CREAT | O_EXCL, 0640); 5850Sstevel@tonic-gate if (newfd < 0) { 5860Sstevel@tonic-gate switch (errno) { 5870Sstevel@tonic-gate case EEXIST: 5880Sstevel@tonic-gate DPRINT((dbfp, 5890Sstevel@tonic-gate "open_log says duplicate for %s " 5900Sstevel@tonic-gate "(will try another)\n", newname)); 5910Sstevel@tonic-gate (void) sleep(1); 5920Sstevel@tonic-gate break; 5930Sstevel@tonic-gate default: 5940Sstevel@tonic-gate /* open failed */ 5950Sstevel@tonic-gate DPRINT((dbfp, 5960Sstevel@tonic-gate "open_log says full for %s: %s\n", 5970Sstevel@tonic-gate newname, strerror(errno))); 5980Sstevel@tonic-gate current_dir->dl_space = SPACE_FULL; 5990Sstevel@tonic-gate current_dir = current_dir->dl_next; 6000Sstevel@tonic-gate return (0); 6010Sstevel@tonic-gate } /* switch */ 6020Sstevel@tonic-gate } else 6030Sstevel@tonic-gate opened = 1; 6040Sstevel@tonic-gate } /* while */ 6050Sstevel@tonic-gate 6060Sstevel@tonic-gate /* 6070Sstevel@tonic-gate * When we get here, we have opened our new log file. 6080Sstevel@tonic-gate * Now we need to update the name of the old file to 6090Sstevel@tonic-gate * store in this file's header. lastOpenDir may point 6100Sstevel@tonic-gate * to current_dir if the list is only one entry long and 6110Sstevel@tonic-gate * there is only one list. 6120Sstevel@tonic-gate */ 6130Sstevel@tonic-gate if ((lastOpenDir != NULL) && (lastOpenDir->dl_filename != NULL)) { 6140Sstevel@tonic-gate (void) strlcpy(oldname, lastOpenDir->dl_filename, 6150Sstevel@tonic-gate AUDIT_FNAME_SZ); 6160Sstevel@tonic-gate name = (char *)strrchr(oldname, '/') + 1; 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate, 6195007Spaulson AUDIT_DATE_SZ); 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate close_log(lastOpenDir, oldname, newname); 6220Sstevel@tonic-gate } 6230Sstevel@tonic-gate error = write_file_token(newfd, oldname); 6240Sstevel@tonic-gate if (error) { 6250Sstevel@tonic-gate /* write token failed */ 6260Sstevel@tonic-gate (void) close(newfd); 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate current_dir->dl_space = SPACE_FULL; 6290Sstevel@tonic-gate current_dir->dl_fd = -1; 6300Sstevel@tonic-gate free(current_dir->dl_filename); 6310Sstevel@tonic-gate current_dir->dl_filename = NULL; 6320Sstevel@tonic-gate current_dir = current_dir->dl_next; 6330Sstevel@tonic-gate return (0); 6340Sstevel@tonic-gate } else { 6350Sstevel@tonic-gate lastOpenDir = current_dir; 6360Sstevel@tonic-gate current_dir->dl_fd = newfd; 6370Sstevel@tonic-gate current_dir->dl_filename = strdup(newname); 6380Sstevel@tonic-gate 639*6697Spr131582 /* 640*6697Spr131582 * New file opened, so reset file size statistic (used 641*6697Spr131582 * to ensure audit log does not grow above size limit 642*6697Spr131582 * set by p_fsize). 643*6697Spr131582 */ 644*6697Spr131582 binfile_cursize = 0; 645*6697Spr131582 6460Sstevel@tonic-gate __logpost(newname); 6470Sstevel@tonic-gate 6480Sstevel@tonic-gate DPRINT((dbfp, "binfile: Log opened: %s\n", newname)); 6490Sstevel@tonic-gate return (1); 6500Sstevel@tonic-gate } 6510Sstevel@tonic-gate } 6520Sstevel@tonic-gate 6530Sstevel@tonic-gate #define IGNORE_SIZE 8192 6540Sstevel@tonic-gate /* 6550Sstevel@tonic-gate * spacecheck - determine whether the given directory's filesystem 6560Sstevel@tonic-gate * has the at least the space requested. Also set the space 6570Sstevel@tonic-gate * value in the directory list structure. If the caller 6580Sstevel@tonic-gate * passes other than PLENTY_SPACE or SOFT_SPACE, the caller should 6590Sstevel@tonic-gate * ignore the return value. Otherwise, 0 = less than the 6600Sstevel@tonic-gate * requested space is available, 1 = at least the requested space 6610Sstevel@tonic-gate * is available. 6620Sstevel@tonic-gate * 6630Sstevel@tonic-gate * log_mutex must be held by the caller 6640Sstevel@tonic-gate * 6650Sstevel@tonic-gate * -1 is returned if stat fails 6660Sstevel@tonic-gate * 6670Sstevel@tonic-gate * IGNORE_SIZE is one page (Sol 9 / 10 timeframe) and is the default 6680Sstevel@tonic-gate * buffer size written for Sol 9 and earlier. To keep the same accuracy 6690Sstevel@tonic-gate * for the soft limit check as before, spacecheck checks for space 6700Sstevel@tonic-gate * remaining IGNORE_SIZE bytes. This reduces the number of statvfs() 6710Sstevel@tonic-gate * calls and related math. 6720Sstevel@tonic-gate * 6730Sstevel@tonic-gate * globals - 6740Sstevel@tonic-gate * minfree - the soft limit, i.e., the % of filesystem to reserve 6750Sstevel@tonic-gate */ 6760Sstevel@tonic-gate static int 6770Sstevel@tonic-gate spacecheck(dirlist_t *thisdir, int test_limit, size_t next_buf_size) 6780Sstevel@tonic-gate { 6790Sstevel@tonic-gate struct statvfs sb; 6800Sstevel@tonic-gate static int ignore_size = 0; 6810Sstevel@tonic-gate 6820Sstevel@tonic-gate ignore_size += next_buf_size; 6830Sstevel@tonic-gate 6840Sstevel@tonic-gate if ((test_limit == PLENTY_SPACE) && (ignore_size < IGNORE_SIZE)) 6850Sstevel@tonic-gate return (1); 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate assert(thisdir != NULL); 6880Sstevel@tonic-gate 6895007Spaulson if (statvfs(thisdir->dl_dirname, &sb) < 0) { 6900Sstevel@tonic-gate thisdir->dl_space = SPACE_FULL; 6910Sstevel@tonic-gate minfreeblocks = AVAIL_MIN; 6920Sstevel@tonic-gate return (-1); 6930Sstevel@tonic-gate } else { 6940Sstevel@tonic-gate minfreeblocks = ((minfree * sb.f_blocks) / 100) + AVAIL_MIN; 6950Sstevel@tonic-gate 6960Sstevel@tonic-gate if (sb.f_bavail < AVAIL_MIN) 6970Sstevel@tonic-gate thisdir->dl_space = SPACE_FULL; 6985007Spaulson else if (sb.f_bavail > minfreeblocks) { 6995007Spaulson thisdir->dl_space = fullness_state = PLENTY_SPACE; 7005007Spaulson ignore_size = 0; 7015007Spaulson } else 7020Sstevel@tonic-gate thisdir->dl_space = SOFT_SPACE; 7030Sstevel@tonic-gate } 7040Sstevel@tonic-gate if (thisdir->dl_space == PLENTY_SPACE) 7050Sstevel@tonic-gate return (1); 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate return (thisdir->dl_space == test_limit); 7080Sstevel@tonic-gate } 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate /* 711*6697Spr131582 * Parses p_fsize value and contains it within the range FSIZE_MIN and 712*6697Spr131582 * INT_MAX so using uints won't cause an undetected overflow of 713*6697Spr131582 * INT_MAX. Defaults to 0 if the value is invalid or is missing. 714*6697Spr131582 */ 715*6697Spr131582 static void 716*6697Spr131582 save_maxsize(char *maxsize) { 717*6697Spr131582 /* 718*6697Spr131582 * strtol() returns a long which could be larger than int so 719*6697Spr131582 * store here for sanity checking first 720*6697Spr131582 */ 721*6697Spr131582 long proposed_maxsize; 722*6697Spr131582 723*6697Spr131582 if (maxsize != NULL) { 724*6697Spr131582 /* 725*6697Spr131582 * There is no explicit error return from strtol() so 726*6697Spr131582 * we may need to depend on the value of errno. 727*6697Spr131582 */ 728*6697Spr131582 errno = 0; 729*6697Spr131582 proposed_maxsize = strtol(maxsize, (char **)NULL, 10); 730*6697Spr131582 731*6697Spr131582 /* 732*6697Spr131582 * If sizeof(long) is greater than sizeof(int) on this 733*6697Spr131582 * platform, proposed_maxsize might be greater than 734*6697Spr131582 * INT_MAX without it being reported as ERANGE. 735*6697Spr131582 */ 736*6697Spr131582 if ((errno == ERANGE) || 737*6697Spr131582 ((proposed_maxsize != 0) && 738*6697Spr131582 (proposed_maxsize < FSIZE_MIN)) || 739*6697Spr131582 (proposed_maxsize > INT_MAX)) { 740*6697Spr131582 binfile_maxsize = 0; 741*6697Spr131582 DPRINT((dbfp, "binfile: p_fsize parameter out of " 742*6697Spr131582 "range: %s\n", maxsize)); 743*6697Spr131582 /* 744*6697Spr131582 * Inform administrator of the error via 745*6697Spr131582 * syslog 746*6697Spr131582 */ 747*6697Spr131582 __audit_syslog("audit_binfile.so", 748*6697Spr131582 LOG_CONS | LOG_NDELAY, 749*6697Spr131582 LOG_DAEMON, LOG_ERR, 750*6697Spr131582 gettext("p_fsize parameter out of range\n")); 751*6697Spr131582 } else { 752*6697Spr131582 binfile_maxsize = proposed_maxsize; 753*6697Spr131582 } 754*6697Spr131582 } else { /* p_fsize string was not present */ 755*6697Spr131582 binfile_maxsize = 0; 756*6697Spr131582 } 757*6697Spr131582 758*6697Spr131582 DPRINT((dbfp, "binfile: set maxsize to %d\n", binfile_maxsize)); 759*6697Spr131582 } 760*6697Spr131582 761*6697Spr131582 /* 7625007Spaulson * auditd_plugin() writes a buffer to the currently open file. The 763*6697Spr131582 * global "openNewFile" is used to force a new log file for cases such 764*6697Spr131582 * as the initial open, when minfree is reached, the p_fsize value is 765*6697Spr131582 * exceeded or the current file system fills up, and "audit -s" with 766*6697Spr131582 * changed parameters. For "audit -n" a new log file is opened 767*6697Spr131582 * immediately in auditd_plugin_open(). 7680Sstevel@tonic-gate * 7690Sstevel@tonic-gate * This function manages one or more audit directories as follows: 7700Sstevel@tonic-gate * 7710Sstevel@tonic-gate * If the current open file is in a directory that has not 7720Sstevel@tonic-gate * reached the soft limit, write the input data and return. 7730Sstevel@tonic-gate * 7740Sstevel@tonic-gate * Scan the list of directories for one which has not reached 7750Sstevel@tonic-gate * the soft limit; if one is found, write and return. Such 7760Sstevel@tonic-gate * a writable directory is in "PLENTY_SPACE" state. 7770Sstevel@tonic-gate * 7780Sstevel@tonic-gate * Scan the list of directories for one which has not reached 7790Sstevel@tonic-gate * the hard limit; if one is found, write and return. This 7800Sstevel@tonic-gate * directory in in "SOFT_SPACE" state. 7810Sstevel@tonic-gate * 7820Sstevel@tonic-gate * Oh, and if a write fails, handle it like a hard space limit. 7830Sstevel@tonic-gate * 7840Sstevel@tonic-gate * audit_warn (via __audit_dowarn()) is used to alert an operator 7850Sstevel@tonic-gate * at various levels of fullness. 7860Sstevel@tonic-gate */ 7870Sstevel@tonic-gate /* ARGSUSED */ 7880Sstevel@tonic-gate auditd_rc_t 7890Sstevel@tonic-gate auditd_plugin(const char *input, size_t in_len, uint32_t sequence, char **error) 7900Sstevel@tonic-gate { 7910Sstevel@tonic-gate auditd_rc_t rc = AUDITD_FAIL; 7920Sstevel@tonic-gate int open_status; 7930Sstevel@tonic-gate size_t out_len; 7940Sstevel@tonic-gate /* LINTED */ 7950Sstevel@tonic-gate int statrc; 7960Sstevel@tonic-gate /* avoid excess audit_warnage */ 7970Sstevel@tonic-gate static int allsoftfull_warning = 0; 7985007Spaulson static int allhard_pause = 0; 7995007Spaulson static struct timeval next_allhard; 8005007Spaulson struct timeval now; 8010Sstevel@tonic-gate #if DEBUG 8020Sstevel@tonic-gate static char *last_file_written_to = NULL; 8030Sstevel@tonic-gate static uint32_t last_sequence = 0; 8040Sstevel@tonic-gate static uint32_t write_count = 0; 8050Sstevel@tonic-gate 8060Sstevel@tonic-gate if ((last_sequence > 0) && (sequence != last_sequence + 1)) 8070Sstevel@tonic-gate fprintf(dbfp, "binfile: buffer sequence=%d but prev=%d=n", 8085007Spaulson sequence, last_sequence); 8090Sstevel@tonic-gate last_sequence = sequence; 8100Sstevel@tonic-gate 8110Sstevel@tonic-gate fprintf(dbfp, "binfile: input seq=%d, len=%d\n", 8125007Spaulson sequence, in_len); 8130Sstevel@tonic-gate #endif 8140Sstevel@tonic-gate *error = NULL; 8150Sstevel@tonic-gate /* 8160Sstevel@tonic-gate * lock is for activeDir, referenced by open_log() and close_log() 8170Sstevel@tonic-gate */ 8180Sstevel@tonic-gate (void) pthread_mutex_lock(&log_mutex); 819*6697Spr131582 820*6697Spr131582 /* 821*6697Spr131582 * If this would take us over the maximum size, open a new 822*6697Spr131582 * file, unless maxsize is 0, in which case growth of the 823*6697Spr131582 * audit log is unrestricted. 824*6697Spr131582 */ 825*6697Spr131582 if ((binfile_maxsize != 0) && 826*6697Spr131582 ((binfile_cursize + in_len) > binfile_maxsize)) { 827*6697Spr131582 DPRINT((dbfp, "binfile: maxsize exceeded, opening new audit " 828*6697Spr131582 "file.\n")); 829*6697Spr131582 openNewFile = 1; 830*6697Spr131582 } 831*6697Spr131582 8320Sstevel@tonic-gate while (rc == AUDITD_FAIL) { 8330Sstevel@tonic-gate open_status = 1; 8340Sstevel@tonic-gate if (openNewFile) { 8350Sstevel@tonic-gate open_status = open_log(activeDir); 8360Sstevel@tonic-gate if (open_status == 1) /* ok */ 8370Sstevel@tonic-gate openNewFile = 0; 8380Sstevel@tonic-gate } 8390Sstevel@tonic-gate /* 8400Sstevel@tonic-gate * consider "space ok" return and error return the same; 8410Sstevel@tonic-gate * a -1 means spacecheck couldn't check for space. 8420Sstevel@tonic-gate */ 8430Sstevel@tonic-gate if ((open_status == 1) && 8440Sstevel@tonic-gate (statrc = spacecheck(activeDir, fullness_state, 8450Sstevel@tonic-gate in_len)) != 0) { 8460Sstevel@tonic-gate #if DEBUG 8470Sstevel@tonic-gate DPRINT((dbfp, "binfile: returned from spacecheck\n")); 8480Sstevel@tonic-gate /* 8490Sstevel@tonic-gate * The last copy of last_file_written_to is 8500Sstevel@tonic-gate * never free'd, so there will be one open 8510Sstevel@tonic-gate * memory reference on exit. It's debug only. 8520Sstevel@tonic-gate */ 8530Sstevel@tonic-gate if ((last_file_written_to != NULL) && 8540Sstevel@tonic-gate (strcmp(last_file_written_to, 8550Sstevel@tonic-gate activeDir->dl_filename) != 0)) { 8560Sstevel@tonic-gate DPRINT((dbfp, "binfile: now writing to %s\n", 8570Sstevel@tonic-gate activeDir->dl_filename)); 8580Sstevel@tonic-gate free(last_file_written_to); 8590Sstevel@tonic-gate } 8600Sstevel@tonic-gate DPRINT((dbfp, "binfile: finished some debug stuff\n")); 8610Sstevel@tonic-gate last_file_written_to = 8620Sstevel@tonic-gate strdup(activeDir->dl_filename); 8630Sstevel@tonic-gate #endif 8640Sstevel@tonic-gate out_len = write(activeDir->dl_fd, input, in_len); 8650Sstevel@tonic-gate DPRINT((dbfp, "binfile: finished the write\n")); 8660Sstevel@tonic-gate 867*6697Spr131582 binfile_cursize += out_len; 868*6697Spr131582 8690Sstevel@tonic-gate if (out_len == in_len) { 8700Sstevel@tonic-gate DPRINT((dbfp, 8710Sstevel@tonic-gate "binfile: write_count=%u, sequence=%u," 8720Sstevel@tonic-gate " l=%u\n", 8730Sstevel@tonic-gate ++write_count, sequence, out_len)); 8740Sstevel@tonic-gate allsoftfull_warning = 0; 8755007Spaulson activeDir->dl_flags = 0; 8760Sstevel@tonic-gate 8770Sstevel@tonic-gate rc = AUDITD_SUCCESS; 8780Sstevel@tonic-gate break; 8795007Spaulson } else if (!(activeDir->dl_flags & HARD_WARNED)) { 8800Sstevel@tonic-gate DPRINT((dbfp, 8810Sstevel@tonic-gate "binfile: write failed, sequence=%u, " 8820Sstevel@tonic-gate "l=%u\n", sequence, out_len)); 8830Sstevel@tonic-gate DPRINT((dbfp, "hard warning sent.\n")); 8840Sstevel@tonic-gate __audit_dowarn("hard", activeDir->dl_dirname, 8850Sstevel@tonic-gate 0); 8860Sstevel@tonic-gate 8870Sstevel@tonic-gate activeDir->dl_flags |= HARD_WARNED; 8880Sstevel@tonic-gate } 8890Sstevel@tonic-gate } else { 8900Sstevel@tonic-gate DPRINT((dbfp, "binfile: statrc=%d, fullness_state=%d\n", 8910Sstevel@tonic-gate statrc, fullness_state)); 8925007Spaulson if (!(activeDir->dl_flags & SOFT_WARNED) && 8935007Spaulson (activeDir->dl_space == SOFT_SPACE)) { 8940Sstevel@tonic-gate DPRINT((dbfp, "soft warning sent\n")); 8950Sstevel@tonic-gate __audit_dowarn("soft", 8960Sstevel@tonic-gate activeDir->dl_dirname, 0); 8970Sstevel@tonic-gate activeDir->dl_flags |= SOFT_WARNED; 8980Sstevel@tonic-gate } 8995007Spaulson if (!(activeDir->dl_flags & HARD_WARNED) && 9005007Spaulson (activeDir->dl_space == SPACE_FULL)) { 9010Sstevel@tonic-gate DPRINT((dbfp, "hard warning sent.\n")); 9020Sstevel@tonic-gate __audit_dowarn("hard", 9035007Spaulson activeDir->dl_dirname, 0); 9040Sstevel@tonic-gate activeDir->dl_flags |= HARD_WARNED; 9050Sstevel@tonic-gate } 9060Sstevel@tonic-gate } 9070Sstevel@tonic-gate DPRINT((dbfp, "binfile: activeDir=%s, next=%s\n", 9080Sstevel@tonic-gate activeDir->dl_dirname, activeDir->dl_next->dl_dirname)); 9090Sstevel@tonic-gate 9100Sstevel@tonic-gate activeDir = activeDir->dl_next; 9115007Spaulson openNewFile = 1; 9120Sstevel@tonic-gate 9130Sstevel@tonic-gate if (activeDir == startdir) { /* full circle */ 9140Sstevel@tonic-gate if (fullness_state == PLENTY_SPACE) { /* once */ 9150Sstevel@tonic-gate fullness_state = SOFT_SPACE; 9160Sstevel@tonic-gate if (allsoftfull_warning == 0) { 9170Sstevel@tonic-gate allsoftfull_warning++; 9180Sstevel@tonic-gate __audit_dowarn("allsoft", "", 0); 9190Sstevel@tonic-gate } 9200Sstevel@tonic-gate } else { /* full circle twice */ 9215007Spaulson if ((hung_count > 0) && !allhard_pause) { 9225007Spaulson allhard_pause = 1; 9235007Spaulson (void) gettimeofday(&next_allhard, 9245007Spaulson NULL); 9255007Spaulson next_allhard.tv_sec += ALLHARD_DELAY; 9265007Spaulson } 9275007Spaulson 9285007Spaulson if (allhard_pause) { 9295007Spaulson (void) gettimeofday(&now, NULL); 9305007Spaulson if (now.tv_sec >= next_allhard.tv_sec) { 9315007Spaulson allhard_pause = 0; 9325007Spaulson __audit_dowarn("allhard", "", 9335007Spaulson ++hung_count); 9345007Spaulson } 9355007Spaulson } else { 9365007Spaulson __audit_dowarn("allhard", "", 9375007Spaulson ++hung_count); 9385007Spaulson } 9390Sstevel@tonic-gate minfreeblocks = AVAIL_MIN; 9400Sstevel@tonic-gate rc = AUDITD_RETRY; 9410Sstevel@tonic-gate *error = strdup(gettext( 9420Sstevel@tonic-gate "all partitions full\n")); 9430Sstevel@tonic-gate __logpost(""); 9440Sstevel@tonic-gate } 9450Sstevel@tonic-gate } 9460Sstevel@tonic-gate } 9470Sstevel@tonic-gate (void) pthread_mutex_unlock(&log_mutex); 9480Sstevel@tonic-gate 9490Sstevel@tonic-gate return (rc); 9500Sstevel@tonic-gate } 9510Sstevel@tonic-gate 9520Sstevel@tonic-gate 9530Sstevel@tonic-gate /* 9540Sstevel@tonic-gate * the open function uses getacdir() and getacmin to determine which 9550Sstevel@tonic-gate * directories to use and when to switch. It takes no inputs. 9560Sstevel@tonic-gate * 9570Sstevel@tonic-gate * It may be called multiple times as auditd handles SIGHUP and SIGUSR1 9580Sstevel@tonic-gate * corresponding to the audit(1M) flags -s and -n 9590Sstevel@tonic-gate * 9600Sstevel@tonic-gate * kvlist is NULL only if auditd caught a SIGUSR1, so after the first 9610Sstevel@tonic-gate * time open is called, the reason is -s if kvlist != NULL and -n 9620Sstevel@tonic-gate * otherwise. 9630Sstevel@tonic-gate * 9640Sstevel@tonic-gate */ 9650Sstevel@tonic-gate 9660Sstevel@tonic-gate auditd_rc_t 9670Sstevel@tonic-gate auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error) 9680Sstevel@tonic-gate { 9690Sstevel@tonic-gate int rc = 0; 9700Sstevel@tonic-gate int status; 9710Sstevel@tonic-gate int reason; 9720Sstevel@tonic-gate char *dirlist; 9730Sstevel@tonic-gate char *minfree; 974*6697Spr131582 char *maxsize; 9750Sstevel@tonic-gate kva_t *kv; 9760Sstevel@tonic-gate 9770Sstevel@tonic-gate *error = NULL; 9780Sstevel@tonic-gate *ret_list = NULL; 9790Sstevel@tonic-gate kv = (kva_t *)kvlist; 9800Sstevel@tonic-gate 9810Sstevel@tonic-gate if (am_open) { 9820Sstevel@tonic-gate if (kvlist == NULL) 9830Sstevel@tonic-gate reason = 1; /* audit -n */ 9840Sstevel@tonic-gate else 9850Sstevel@tonic-gate reason = 2; /* audit -s */ 9860Sstevel@tonic-gate } else { 9870Sstevel@tonic-gate reason = 0; /* initial open */ 9880Sstevel@tonic-gate #if DEBUG 9890Sstevel@tonic-gate dbfp = __auditd_debug_file_open(); 9900Sstevel@tonic-gate #endif 9910Sstevel@tonic-gate } 9920Sstevel@tonic-gate DPRINT((dbfp, "binfile: am_open=%d, reason=%d\n", am_open, reason)); 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate am_open = 1; 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate if (kvlist == NULL) { 9970Sstevel@tonic-gate dirlist = NULL; 9980Sstevel@tonic-gate minfree = NULL; 999*6697Spr131582 maxsize = NULL; 10000Sstevel@tonic-gate } else { 10010Sstevel@tonic-gate dirlist = kva_match(kv, "p_dir"); 10020Sstevel@tonic-gate minfree = kva_match(kv, "p_minfree"); 1003*6697Spr131582 maxsize = kva_match(kv, "p_fsize"); 10040Sstevel@tonic-gate } 10050Sstevel@tonic-gate switch (reason) { 10060Sstevel@tonic-gate case 0: /* initial open */ 10070Sstevel@tonic-gate if (!binfile_is_open) 10080Sstevel@tonic-gate (void) pthread_mutex_init(&log_mutex, NULL); 10090Sstevel@tonic-gate binfile_is_open = 1; 10100Sstevel@tonic-gate openNewFile = 1; 10110Sstevel@tonic-gate /* FALLTHRU */ 10120Sstevel@tonic-gate case 2: /* audit -s */ 1013*6697Spr131582 /* handle p_fsize parameter */ 1014*6697Spr131582 save_maxsize(maxsize); 1015*6697Spr131582 10160Sstevel@tonic-gate fullness_state = PLENTY_SPACE; 10170Sstevel@tonic-gate status = loadauditlist(dirlist, minfree); 10180Sstevel@tonic-gate 10190Sstevel@tonic-gate if (status == -1) { 10200Sstevel@tonic-gate __logpost(""); 10210Sstevel@tonic-gate *error = strdup(gettext("no directories configured")); 10220Sstevel@tonic-gate return (AUDITD_RETRY); 10230Sstevel@tonic-gate } else if (status == AUDITD_NO_MEMORY) { 10240Sstevel@tonic-gate __logpost(""); 10250Sstevel@tonic-gate *error = strdup(gettext("no memory")); 10260Sstevel@tonic-gate return (status); 10270Sstevel@tonic-gate } else { /* status is 0 or -2 (no change or changed) */ 10280Sstevel@tonic-gate hung_count = 0; 10290Sstevel@tonic-gate DPRINT((dbfp, "binfile: loadauditlist returned %d\n", 10305007Spaulson status)); 10310Sstevel@tonic-gate } 10320Sstevel@tonic-gate break; 10330Sstevel@tonic-gate case 1: /* audit -n */ 10340Sstevel@tonic-gate (void) pthread_mutex_lock(&log_mutex); 10350Sstevel@tonic-gate if (open_log(activeDir) == 1) /* ok */ 10360Sstevel@tonic-gate openNewFile = 0; 10370Sstevel@tonic-gate (void) pthread_mutex_unlock(&log_mutex); 10380Sstevel@tonic-gate break; 10390Sstevel@tonic-gate } 10400Sstevel@tonic-gate 10410Sstevel@tonic-gate rc = AUDITD_SUCCESS; 10420Sstevel@tonic-gate *ret_list = NULL; 10430Sstevel@tonic-gate 10440Sstevel@tonic-gate return (rc); 10450Sstevel@tonic-gate } 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate auditd_rc_t 10480Sstevel@tonic-gate auditd_plugin_close(char **error) 10490Sstevel@tonic-gate { 10500Sstevel@tonic-gate *error = NULL; 10510Sstevel@tonic-gate 10520Sstevel@tonic-gate (void) pthread_mutex_lock(&log_mutex); 10530Sstevel@tonic-gate close_log(activeDir, "", ""); 10540Sstevel@tonic-gate freedirlist(activeDir); 10550Sstevel@tonic-gate activeDir = NULL; 10560Sstevel@tonic-gate (void) pthread_mutex_unlock(&log_mutex); 10570Sstevel@tonic-gate 10580Sstevel@tonic-gate DPRINT((dbfp, "binfile: closed\n")); 10590Sstevel@tonic-gate 10600Sstevel@tonic-gate if (binfile_is_open) { 10610Sstevel@tonic-gate (void) pthread_mutex_destroy(&log_mutex); 10620Sstevel@tonic-gate binfile_is_open = 0; 10630Sstevel@tonic-gate /* LINTED */ 10640Sstevel@tonic-gate } else { 10650Sstevel@tonic-gate DPRINT((dbfp, 10660Sstevel@tonic-gate "auditd_plugin_close() called when already closed.")); 10670Sstevel@tonic-gate } 10680Sstevel@tonic-gate am_open = 0; 10690Sstevel@tonic-gate return (AUDITD_SUCCESS); 10700Sstevel@tonic-gate } 1071