config root man

Current Path : /compat/linux/proc/self/root/usr/src/contrib/openbsm/libauditd/

FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64
Upload File :
Current File : //compat/linux/proc/self/root/usr/src/contrib/openbsm/libauditd/auditd_lib.c

/*-
 * Copyright (c) 2008-2009 Apple Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * $P4: //depot/projects/trustedbsd/openbsm/libauditd/auditd_lib.c#11 $
 */

#include <sys/param.h>

#include <config/config.h>

#include <sys/dirent.h>
#ifdef HAVE_FULL_QUEUE_H
#include <sys/queue.h>
#else /* !HAVE_FULL_QUEUE_H */
#include <compat/queue.h>
#endif /* !HAVE_FULL_QUEUE_H */
#include <sys/mount.h>
#include <sys/socket.h>

#include <sys/stat.h>
#include <sys/time.h>

#include <netinet/in.h>

#include <bsm/audit.h>
#include <bsm/audit_uevents.h>
#include <bsm/auditd_lib.h>
#include <bsm/libbsm.h>

#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <netdb.h>

#ifdef __APPLE__
#include <notify.h>
#ifndef __BSM_INTERNAL_NOTIFY_KEY
#define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change"
#endif /* __BSM_INTERNAL_NOTIFY_KEY */
#endif /* __APPLE__ */

/*
 * XXX This is temporary until this is moved to <bsm/audit.h> and shared with
 * the kernel.
 */
#ifndef	AUDIT_HARD_LIMIT_FREE_BLOCKS
#define	AUDIT_HARD_LIMIT_FREE_BLOCKS	4
#endif

/*
 * Number of seconds to January 1, 2000
 */
#define	JAN_01_2000	946598400

struct dir_ent {
	char			*dirname;
	uint8_t			 softlim;
	uint8_t			 hardlim;
	TAILQ_ENTRY(dir_ent)	 dirs;
};

static TAILQ_HEAD(, dir_ent)	dir_q;

struct audit_trail {
	time_t			 at_time;
	char			*at_path;
	off_t			 at_size;

	TAILQ_ENTRY(audit_trail) at_trls;
};

static int auditd_minval = -1;

static char auditd_host[MAXHOSTNAMELEN];
static int auditd_hostlen = -1;

static char *auditd_errmsg[] = {
	"no error",					/* ADE_NOERR 	( 0) */
	"could not parse audit_control(5) file",	/* ADE_PARSE 	( 1) */
	"auditon(2) failed",				/* ADE_AUDITON 	( 2) */
	"malloc(3) failed",				/* ADE_NOMEM 	( 3) */
	"all audit log directories over soft limit",	/* ADE_SOFTLIM  ( 4) */
	"all audit log directories over hard limit",	/* ADE_HARDLIM 	( 5) */
	"could not create file name string",		/* ADE_STRERR 	( 6) */
	"could not open audit record", 			/* ADE_AU_OPEN 	( 7) */
	"could not close audit record",			/* ADE_AU_CLOSE ( 8) */
	"could not set active audit session state",	/* ADE_SETAUDIT ( 9) */
	"auditctl(2) failed (trail still swapped)",	/* ADE_ACTL 	(10) */
	"auditctl(2) failed (trail not swapped)",	/* ADE_ACTLERR  (11) */
	"could not swap audit trail file",		/* ADE_SWAPERR 	(12) */
	"could not rename crash recovery file",		/* ADE_RENAME	(13) */
	"could not read 'current' link file",		/* ADE_READLINK	(14) */
	"could not create 'current' link file", 	/* ADE_SYMLINK  (15) */
	"invalid argument",				/* ADE_INVAL	(16) */
	"could not resolve hostname to address",	/* ADE_GETADDR	(17) */
	"address family not supported",			/* ADE_ADDRFAM	(18) */
	"error expiring audit trail files",		/* ADE_EXPIRE	(19) */
};

#define MAXERRCODE (sizeof(auditd_errmsg) / sizeof(auditd_errmsg[0]))

#define NA_EVENT_STR_SIZE       128
#define POL_STR_SIZE            128


/*
 * Look up and return the error string for the given audit error code.
 */
const char *
auditd_strerror(int errcode)
{
	int idx = -errcode;

	if (idx < 0 || idx > (int)MAXERRCODE)
		return ("Invalid auditd error code");
	
	return (auditd_errmsg[idx]);
}


/*
 * Free our local list of directory names and init list 
 */
static void
free_dir_q(void)
{
	struct dir_ent *d1, *d2;
	
	d1 = TAILQ_FIRST(&dir_q);
	while (d1 != NULL) {
		d2 = TAILQ_NEXT(d1, dirs);
		free(d1->dirname);
		free(d1);
		d1 = d2;
	}
	TAILQ_INIT(&dir_q);
}

/*
 * Concat the directory name to the given file name.
 * XXX We should affix the hostname also
 */
static char *
affixdir(char *name, struct dir_ent *dirent)
{
	char *fn = NULL;

	/*
	 * Sanity check on file name.
	 */
	if (strlen(name) != (FILENAME_LEN - 1)) {
		errno = EINVAL;
                return (NULL);
	}

	/*
	 * If the host is set then also add the hostname to the filename.
	 */
	if (auditd_hostlen != -1)
		asprintf(&fn, "%s/%s.%s", dirent->dirname, name, auditd_host);
	else
		asprintf(&fn, "%s/%s", dirent->dirname, name);
	return (fn);
}

/*
 * Insert the directory entry in the list by the way they are ordered in
 * audit_control(5).  Move the entries that are over the soft and hard limits
 * toward the tail.
 */
static void
insert_orderly(struct dir_ent *denew)
{
	struct dir_ent *dep;
	
	TAILQ_FOREACH(dep, &dir_q, dirs) {
		if (dep->softlim == 1 && denew->softlim == 0) {
			TAILQ_INSERT_BEFORE(dep, denew, dirs);
			return;			
		}
		if (dep->hardlim == 1 && denew->hardlim == 0) {
			TAILQ_INSERT_BEFORE(dep, denew, dirs);
			return;
		}
	}
	TAILQ_INSERT_TAIL(&dir_q, denew, dirs);
}

/*
 * Get the host from audit_control(5) and set it in the audit kernel
 * information.  Return:
 *	ADE_NOERR	on success.
 *	ADE_PARSE	error parsing audit_control(5).
 *	ADE_AUDITON	error getting/setting auditon(2) value.
 *	ADE_GETADDR 	error getting address info for host. 
 *	ADE_ADDRFAM	un-supported address family. 	
 */
int
auditd_set_host(void)
{
	struct sockaddr_in6 *sin6;
	struct sockaddr_in *sin;
	struct addrinfo *res;
	struct auditinfo_addr aia;
	int error, ret = ADE_NOERR;

	if (getachost(auditd_host, sizeof(auditd_host)) != 0) {
		ret = ADE_PARSE;	
	
		/*
		 * To maintain reverse compatability with older audit_control
		 * files, simply drop a warning if the host parameter has not
		 * been set.  However, we will explicitly disable the
		 * generation of extended audit header by passing in a zeroed
		 * termid structure.
		 */
		bzero(&aia, sizeof(aia));
		aia.ai_termid.at_type = AU_IPv4;
		error = audit_set_kaudit(&aia, sizeof(aia));
		if (error < 0 && errno != ENOSYS)
			ret = ADE_AUDITON;
		return (ret);
	}
	auditd_hostlen = strlen(auditd_host);
	error = getaddrinfo(auditd_host, NULL, NULL, &res);
	if (error)
		return (ADE_GETADDR);
	switch (res->ai_family) {
	case PF_INET6:
		sin6 = (struct sockaddr_in6 *) res->ai_addr;
		bcopy(&sin6->sin6_addr.s6_addr,
		    &aia.ai_termid.at_addr[0], sizeof(struct in6_addr));
		aia.ai_termid.at_type = AU_IPv6;
		break;

	case PF_INET:
		sin = (struct sockaddr_in *) res->ai_addr;
		bcopy(&sin->sin_addr.s_addr,
		    &aia.ai_termid.at_addr[0], sizeof(struct in_addr));
		aia.ai_termid.at_type = AU_IPv4;
		break;

	default:
		/* Un-supported address family in host parameter. */
		errno = EAFNOSUPPORT;
		return (ADE_ADDRFAM);
	}

	if (audit_set_kaudit(&aia, sizeof(aia)) < 0)
		ret = ADE_AUDITON;

	return (ret);
}

/* 
 * Get the min percentage of free blocks from audit_control(5) and that
 * value in the kernel.  Return:
 *	ADE_NOERR	on success,
 *	ADE_PARSE 	error parsing audit_control(5),
 *	ADE_AUDITON	error getting/setting auditon(2) value.
 */
int
auditd_set_minfree(void)
{
	au_qctrl_t qctrl;

	if (getacmin(&auditd_minval) != 0)
		return (ADE_PARSE);
	
	if (audit_get_qctrl(&qctrl, sizeof(qctrl)) != 0)
		return (ADE_AUDITON);

	if (qctrl.aq_minfree != auditd_minval) {
		qctrl.aq_minfree = auditd_minval;
		if (audit_set_qctrl(&qctrl, sizeof(qctrl)) != 0)
			return (ADE_AUDITON);
	}

	return (0);
}

/*
 * Convert a trailname into a timestamp (seconds).  Return 0 if the conversion
 * was successful.
 */
static int
trailname_to_tstamp(char *fn, time_t *tstamp)
{
	struct tm tm;
	char ts[TIMESTAMP_LEN];
	char *p;

	*tstamp = 0;

	/*
	 * Get the ending time stamp.
	 */
	if ((p = strchr(fn, '.')) == NULL)
		return (1);
	strlcpy(ts, ++p, TIMESTAMP_LEN);
	if (strlen(ts) != POSTFIX_LEN)
		return (1);

	bzero(&tm, sizeof(tm));

	/* seconds (0-60) */
	p = ts + POSTFIX_LEN - 2;
	tm.tm_sec = atol(p);
	if (tm.tm_sec < 0 || tm.tm_sec > 60)
		return (1);

	/* minutes (0-59) */ 
	*p = '\0'; p -= 2;
	tm.tm_min = atol(p);
	if (tm.tm_min < 0 || tm.tm_min > 59)
		return (1);

	/* hours (0 - 23) */
	*p = '\0'; p -= 2;
	tm.tm_hour = atol(p);
	if (tm.tm_hour < 0 || tm.tm_hour > 23)
		return (1);

	/* day of month (1-31) */
	*p = '\0'; p -= 2;
	tm.tm_mday = atol(p);
	if (tm.tm_mday < 1 || tm.tm_mday > 31)
		return (1);

	/* month (0 - 11) */
	*p = '\0'; p -= 2;
	tm.tm_mon = atol(p) - 1;
	if (tm.tm_mon < 0 || tm.tm_mon > 11)
		return (1);

	/* year (year - 1900) */
	*p = '\0'; p -= 4;
	tm.tm_year = atol(p) - 1900;
	if (tm.tm_year < 0)
		return (1);

	*tstamp = timegm(&tm);

	return (0);
}

/*
 * Remove audit trails files according to the expiration conditions.  Returns:
 * 	ADE_NOERR	on success or there is nothing to do.
 * 	ADE_PARSE	if error parsing audit_control(5).
 * 	ADE_NOMEM	if could not allocate memory.
 * 	ADE_EXPIRE	if there was an unespected error.
 */
int
auditd_expire_trails(int (*warn_expired)(char *))
{
	int andflg, ret = ADE_NOERR;
	size_t expire_size, total_size = 0L;
	time_t expire_age, oldest_time, current_time = time(NULL);
	struct dir_ent *traildir;
	struct audit_trail *at;
	char *afnp, *pn;
	TAILQ_HEAD(au_trls_head, audit_trail) head =
	    TAILQ_HEAD_INITIALIZER(head);
	struct stat stbuf;
	char activefn[MAXPATHLEN];

	/*
	 * Read the expiration conditions.  If no conditions then return no
	 * error.
	 */
	if (getacexpire(&andflg, &expire_age, &expire_size) < 0)
		return (ADE_PARSE);
	if (!expire_age && !expire_size)
		return (ADE_NOERR);

	/*
	 * Read the 'current' trail file name.  Trim off directory path.
	 */
	activefn[0] = '\0';
	readlink(AUDIT_CURRENT_LINK, activefn, MAXPATHLEN - 1);
	if ((afnp = strrchr(activefn, '/')) != NULL) 
		afnp++;


	/*
	 * Build tail queue of the trail files.
	 */
	TAILQ_FOREACH(traildir, &dir_q, dirs) {
		DIR *dirp;
		struct dirent *dp;

		dirp = opendir(traildir->dirname);
		while ((dp = readdir(dirp)) != NULL) {
			time_t tstamp = 0;
			struct audit_trail *new;

			/*
			 * Quickly filter non-trail files.
			 */
			if (dp->d_namlen != (FILENAME_LEN - 1) ||
#ifdef DT_REG
			    dp->d_type != DT_REG || 
#endif
			    dp->d_name[POSTFIX_LEN] != '.')
				continue;

			if (asprintf(&pn, "%s/%s", traildir->dirname,
				dp->d_name) < 0) {
				ret = ADE_NOMEM;
				break;
			}

			if (stat(pn, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
				free(pn);
				continue;
			}

			total_size += stbuf.st_size;

			/*
			 * If this is the 'current' audit trail then
			 * don't add it to the tail queue.
			 */
			if (NULL != afnp &&
			    strncmp(dp->d_name, afnp, FILENAME_LEN) == 0) {
				free(pn);
				continue;
			}

			/*
			 * Get the ending time stamp encoded in the trail
			 * name.  If we can't read it or if it is older
			 * than Jan 1, 2000 then use the mtime.
			 */
			if (trailname_to_tstamp(dp->d_name, &tstamp) != 0 ||
			    tstamp < JAN_01_2000)
				tstamp = stbuf.st_mtime;

			/*
			 * If the time stamp is older than Jan 1, 2000 then
			 * update the mtime of the trail file to the current
			 * time. This is so we don't prematurely remove a trail
			 * file that was created while the system clock reset
			 * to the * "beginning of time" but later the system
			 * clock is set to the correct current time.
			 */
			if (current_time >= JAN_01_2000 &&
			    tstamp < JAN_01_2000) {
				struct timeval tv[2];

				tstamp = stbuf.st_mtime = current_time;
				TIMESPEC_TO_TIMEVAL(&tv[0], 
				    &stbuf.st_atimespec);
				TIMESPEC_TO_TIMEVAL(&tv[1], 
				    &stbuf.st_mtimespec);
				utimes(pn, tv);
			}

			/*
			 * Allocate and populate the new entry.
			 */
			new = malloc(sizeof(*new));
			if (NULL == new) {
				free(pn);
				ret = ADE_NOMEM;
				break;
			}
			new->at_time = tstamp;
			new->at_size = stbuf.st_size;
			new->at_path = pn;

			/*
			 * Check to see if we have a new head.  Otherwise,
			 * walk the tailq from the tail first and do a simple
			 * insertion sort.
			 */
			if (TAILQ_EMPTY(&head) ||
			    (new->at_time <= TAILQ_FIRST(&head)->at_time)) {
				TAILQ_INSERT_HEAD(&head, new, at_trls);
				continue;
			}

			TAILQ_FOREACH_REVERSE(at, &head, au_trls_head, at_trls)
				if (new->at_time >= at->at_time) {
					TAILQ_INSERT_AFTER(&head, at, new,
					    at_trls);
					break;
				}

		}
		closedir(dirp);
	}

	oldest_time = current_time - expire_age;

	/* 
	 * Expire trail files, oldest (mtime) first, if the given
	 * conditions are met.
	 */
	at = TAILQ_FIRST(&head);
	while (NULL != at) {
		struct audit_trail *at_next = TAILQ_NEXT(at, at_trls);

		if (andflg) {
			if ((expire_size && total_size > expire_size) &&
			    (expire_age && at->at_time < oldest_time)) {
				if (warn_expired)
				    (*warn_expired)(at->at_path);
				if (unlink(at->at_path) < 0)
					ret = ADE_EXPIRE;
				total_size -= at->at_size;
			}
		} else {
			if ((expire_size && total_size > expire_size) ||
			    (expire_age && at->at_time < oldest_time)) {
				if (warn_expired)
				    (*warn_expired)(at->at_path);
				if (unlink(at->at_path) < 0)
					ret = ADE_EXPIRE;
				total_size -= at->at_size;
			}
		}

		free(at->at_path);
		free(at);
		at = at_next;
	}

	return (ret);
}

/*
 * Parses the "dir" entry in audit_control(5) into an ordered list.  Also, will
 * set the minfree and host values if not already set.  Arguments include
 * function pointers to audit_warn functions for soft and hard limits. Returns:
 *	ADE_NOERR	on success,
 *	ADE_PARSE	error parsing audit_control(5),
 *	ADE_AUDITON	error getting/setting auditon(2) value,
 *	ADE_NOMEM	error allocating memory,
 *	ADE_SOFTLIM	if all the directories are over the soft limit,
 *	ADE_HARDLIM	if all the directories are over the hard limit,
 */
int
auditd_read_dirs(int (*warn_soft)(char *), int (*warn_hard)(char *))
{
	char cur_dir[MAXNAMLEN];
	struct dir_ent *dirent;
	struct statfs sfs;
	int err;
	char soft, hard;
	int tcnt = 0;
	int scnt = 0;
	int hcnt = 0;

	if (auditd_minval == -1 && (err = auditd_set_minfree()) != 0)
		return (err);

	if (auditd_hostlen == -1)
		auditd_set_host();

        /*
         * Init directory q.  Force a re-read of the file the next time.
         */
	free_dir_q();
	endac();

	/*
	 * Read the list of directories into an ordered linked list
	 * admin's preference, then those over soft limit and, finally,
	 * those over the hard limit.
	 *
         * XXX We should use the reentrant interfaces once they are
         * available.
         */
	while (getacdir(cur_dir, MAXNAMLEN) >= 0) {
		if (statfs(cur_dir, &sfs) < 0)
			continue;  /* XXX should warn */
		soft = (sfs.f_bfree < (sfs.f_blocks / (100 / auditd_minval))) ?
		    1 : 0;
		hard = (sfs.f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) ? 1 : 0;
		if (soft) {
			if (warn_soft) 
				(*warn_soft)(cur_dir);
			scnt++;
		}
		if (hard) {
			if (warn_hard)
				(*warn_hard)(cur_dir);
			hcnt++;
		}
		dirent = (struct dir_ent *) malloc(sizeof(struct dir_ent));
		if (dirent == NULL)
			return (ADE_NOMEM);
		dirent->softlim = soft;
		dirent->hardlim = hard; 
		dirent->dirname = (char *) malloc(MAXNAMLEN);
		if (dirent->dirname == NULL) {
			free(dirent);
			return (ADE_NOMEM);
		}
		strlcpy(dirent->dirname, cur_dir, MAXNAMLEN);
		insert_orderly(dirent);
		tcnt++;
	}

	if (hcnt == tcnt)
		return (ADE_HARDLIM);
	if (scnt == tcnt)
		return (ADE_SOFTLIM);
	return (0);
}

void
auditd_close_dirs(void)
{
	free_dir_q();
	auditd_minval = -1;
	auditd_hostlen = -1;
}


/*
 * Process the audit event file, obtaining a class mapping for each event, and
 * set that mapping into the kernel. Return:
 * 	 n	number of event mappings that were successfully processed,
 *   ADE_NOMEM	if there was an error allocating memory.	
 */
int
auditd_set_evcmap(void)
{
	au_event_ent_t ev, *evp;
	au_evclass_map_t evc_map;
	int ctr = 0;

         
	/*
	 * XXX There's a risk here that the BSM library will return NULL
	 * for an event when it can't properly map it to a class. In that
	 * case, we will not process any events beyond the one that failed,
	 * but should. We need a way to get a count of the events.
	 */
	ev.ae_name = (char *)malloc(AU_EVENT_NAME_MAX);
	ev.ae_desc = (char *)malloc(AU_EVENT_DESC_MAX);
	if ((ev.ae_name == NULL) || (ev.ae_desc == NULL)) {
		if (ev.ae_name != NULL)
			free(ev.ae_name);
		return (ADE_NOMEM);
	}
                 
	/*
	 * XXXRW: Currently we have no way to remove mappings from the kernel
	 * when they are removed from the file-based mappings.
	 */
	evp = &ev;
	setauevent();
	while ((evp = getauevent_r(evp)) != NULL) {
		evc_map.ec_number = evp->ae_number;
		evc_map.ec_class = evp->ae_class;
		if (audit_set_class(&evc_map, sizeof(evc_map)) == 0)
			ctr++;
	}
	endauevent();
	free(ev.ae_name);
	free(ev.ae_desc);

	return (ctr);
}

/*
 * Get the non-attributable event string and set the kernel mask.  Return:
 *	ADE_NOERR 	on success,
 *	ADE_PARSE	error parsing audit_control(5),
 *	ADE_AUDITON	error setting the mask using auditon(2).
 */
int
auditd_set_namask(void)
{
	au_mask_t aumask;
	char naeventstr[NA_EVENT_STR_SIZE];
	                 
	if ((getacna(naeventstr, NA_EVENT_STR_SIZE) != 0) || 
	    (getauditflagsbin(naeventstr, &aumask) != 0)) 
		return (ADE_PARSE);

	if (audit_set_kmask(&aumask, sizeof(aumask)) != 0)
		return (ADE_AUDITON);

	return (ADE_NOERR);
}

/*
 * Set the audit control policy if a policy is configured in audit_control(5),
 * implement the policy. However, if one isn't defined or if there is an error
 * parsing the control file, set AUDIT_CNT to avoid leaving the system in a
 * fragile state.  Return:
 *	ADE_NOERR 	on success,
 *	ADE_PARSE	error parsing audit_control(5),
 *	ADE_AUDITON	error setting policy using auditon(2).
 */
int
auditd_set_policy(void)
{
	int policy;
	char polstr[POL_STR_SIZE];

	if ((getacpol(polstr, POL_STR_SIZE) != 0) || 
            (au_strtopol(polstr, &policy) != 0)) {
		policy = AUDIT_CNT;
		if (audit_set_policy(&policy) != 0)
			return (ADE_AUDITON);
		return (ADE_PARSE);
        }

	if (audit_set_policy(&policy) != 0)
		return (ADE_AUDITON);

	return (ADE_NOERR);
}

/* 
 * Set trail rotation size.  Return:
 *	ADE_NOERR 	on success,
 *	ADE_PARSE	error parsing audit_control(5),
 *	ADE_AUDITON	error setting file size using auditon(2).
 */
int
auditd_set_fsize(void)
{
	size_t filesz;
	au_fstat_t au_fstat;

	/*
	 * Set trail rotation size.
	 */
	if (getacfilesz(&filesz) != 0)
		return (ADE_PARSE);

	bzero(&au_fstat, sizeof(au_fstat));
	au_fstat.af_filesz = filesz;
	if (audit_set_fsize(&au_fstat, sizeof(au_fstat)) != 0)
		return (ADE_AUDITON);

        return (ADE_NOERR);
}

/*
 * Create the new audit file with appropriate permissions and ownership.  Try
 * to clean up if something goes wrong.
 */
static int
open_trail(char *fname, gid_t gid)
{
	int error, fd;
	
	fd = open(fname, O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP);
	if (fd < 0)
		return (-1);
	if (fchown(fd, -1, gid) < 0) {
		error = errno;
		close(fd);
		(void)unlink(fname);
		errno = error;
		return (-1);
	}
	return (fd);
}

/*
 * Create the new audit trail file, swap with existing audit file.  Arguments
 * include timestamp for the filename, a pointer to a string for returning the
 * new file name, GID for trail file, and audit_warn function pointer for 
 * 'getacdir()' errors.  Returns:
 *  	ADE_NOERR	on success,
 *  	ADE_STRERR	if the file name string could not be created,
 *  	ADE_SWAPERR	if the audit trail file could not be swapped,
 *	ADE_ACTL 	if the auditctl(2) call failed but file swap still
 *			successful.
 *	ADE_ACTLERR	if the auditctl(2) call failed and file swap failed.
 *	ADE_SYMLINK	if symlink(2) failed updating the current link.
 */
int
auditd_swap_trail(char *TS, char **newfile, gid_t gid, 
    int (*warn_getacdir)(char *))
{
	char timestr[FILENAME_LEN];
	char *fn;
	struct dir_ent *dirent;
	int fd;
	int error;
	int saverrno = 0;
                
	if (strlen(TS) !=  (TIMESTAMP_LEN - 1) ||
	    snprintf(timestr, FILENAME_LEN, "%s.%s", TS, NOT_TERMINATED) < 0) {
		errno = EINVAL;
		return (ADE_STRERR);
	}
                
	/* Try until we succeed. */
	TAILQ_FOREACH(dirent, &dir_q, dirs) {
		if (dirent->hardlim) 
			continue;
		if ((fn = affixdir(timestr, dirent)) == NULL)
			return (ADE_STRERR);

		/*
		 * Create and open the file; then close and pass to the
		 * kernel if all went well.
		 */
		fd = open_trail(fn, gid);
		if (fd >= 0) {
			error = auditctl(fn);
			if (error) {
				/* 
				 * auditctl failed setting log file.  
				 * Try again.
				 */
				saverrno = errno;
                                close(fd);
                        } else {
                                /* Success. */
                                *newfile = fn;
                                close(fd);
				if (error)
					return (error);
				if (saverrno) {
					/*
					 * auditctl() failed but still
					 * successful. Return errno and "soft" 
					 * error.
					 */
					errno = saverrno;
					return (ADE_ACTL);
				}
                                return (ADE_NOERR);
                        }
                }

		/*
		 * Tell the administrator about lack of permissions for dir.
		 */
		if (warn_getacdir != NULL)
			(*warn_getacdir)(dirent->dirname);
	}
	if (saverrno) {
		errno = saverrno;
		return (ADE_ACTLERR);
	} else
		return (ADE_SWAPERR);
}

/*
 * Mask calling process from being audited. Returns:
 *	ADE_NOERR	on success,
 *	ADE_SETAUDIT	if setaudit(2) fails.
 */
#ifdef __APPLE__
int
auditd_prevent_audit(void)
{
	auditinfo_addr_t aia;

	/* 
	 * To prevent event feedback cycles and avoid audit becoming stalled if
	 * auditing is suspended we mask this processes events from being
	 * audited.  We allow the uid, tid, and mask fields to be implicitly
	 * set to zero, but do set the audit session ID to the PID. 
	 *
	 * XXXRW: Is there more to it than this?
	 */
	bzero(&aia, sizeof(aia));
	aia.ai_asid = AU_ASSIGN_ASID;
	aia.ai_termid.at_type = AU_IPv4;
	if (setaudit_addr(&aia, sizeof(aia)) != 0)
		return (ADE_SETAUDIT); 
	return (ADE_NOERR);
}
#else
int
auditd_prevent_audit(void)
{
	auditinfo_t ai;

	/* 
	 * To prevent event feedback cycles and avoid audit becoming stalled if
	 * auditing is suspended we mask this processes events from being
	 * audited.  We allow the uid, tid, and mask fields to be implicitly
	 * set to zero, but do set the audit session ID to the PID. 
	 *
	 * XXXRW: Is there more to it than this?
	 */
	bzero(&ai, sizeof(ai));
	ai.ai_asid = getpid();
	if (setaudit(&ai) != 0)
		return (ADE_SETAUDIT); 
	return (ADE_NOERR);
}
#endif /* __APPLE__ */

/*
 * Generate and submit audit record for audit startup or shutdown.  The event
 * argument can be AUE_audit_recovery, AUE_audit_startup or
 * AUE_audit_shutdown. The path argument will add a path token, if not NULL.
 * Returns:
 *	AUE_NOERR	on success,
 *	ADE_NOMEM	if memory allocation fails,
 * 	ADE_AU_OPEN	if au_open(3) fails,
 *	ADE_AU_CLOSE	if au_close(3) fails.
 */
int
auditd_gen_record(int event, char *path)
{
	int aufd;
	uid_t uid;
	pid_t pid;
	char *autext = NULL;
	token_t *tok;
	struct auditinfo_addr aia;

	if (event == AUE_audit_startup)
		asprintf(&autext, "%s::Audit startup", getprogname());
	else if (event == AUE_audit_shutdown)
		asprintf(&autext, "%s::Audit shutdown", getprogname());
	else if (event == AUE_audit_recovery)
		asprintf(&autext, "%s::Audit recovery", getprogname());
	else 
		return (ADE_INVAL);
	if (autext == NULL)
		return (ADE_NOMEM);

	if ((aufd = au_open()) == -1) {
		free(autext);
		return (ADE_AU_OPEN);
	}
	bzero(&aia, sizeof(aia));
	uid = getuid(); pid = getpid();
	if ((tok = au_to_subject32_ex(uid, geteuid(), getegid(), uid, getgid(),
	     pid, pid, &aia.ai_termid)) != NULL)
		au_write(aufd, tok);
	if ((tok = au_to_text(autext)) != NULL)
		au_write(aufd, tok);
	free(autext);
	if (path != NULL && (tok = au_to_path(path)) != NULL)
		au_write(aufd, tok);
	if ((tok = au_to_return32(0, 0)) != NULL)
		au_write(aufd, tok);
	if (au_close(aufd, 1, event) == -1)
		return (ADE_AU_CLOSE);

	return (ADE_NOERR);
}

/*
 * Check for a 'current' symlink and do crash recovery, if needed. Create a new
 * 'current' symlink. The argument 'curfile' is the file the 'current' symlink
 * should point to.  Returns:
 *	ADE_NOERR	on success,
 *  	ADE_AU_OPEN	if au_open(3) fails,
 *  	ADE_AU_CLOSE	if au_close(3) fails.
 *	ADE_RENAME	if error renaming audit trail file,
 *	ADE_READLINK	if error reading the 'current' link,
 *	ADE_SYMLINK	if error creating 'current' link.
 */
int
auditd_new_curlink(char *curfile)
{
	int len, err;
	char *ptr;
	char *path = NULL;
	struct stat sb;
	char recoveredname[MAXPATHLEN];
	char newname[MAXPATHLEN];

	/*
	 * Check to see if audit was shutdown properly.  If not, clean up,
	 * recover previous audit trail file, and generate audit record.
	 */
	len = readlink(AUDIT_CURRENT_LINK, recoveredname, MAXPATHLEN - 1);
	if (len > 0) {
		/* 'current' exist but is it pointing at a valid file?  */
		recoveredname[len++] = '\0';
		if (stat(recoveredname, &sb) == 0) { 
			/* Yes, rename it to a crash recovery file. */
			strlcpy(newname, recoveredname, MAXPATHLEN);

			if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) {
				memcpy(ptr, CRASH_RECOVERY, POSTFIX_LEN);
				if (rename(recoveredname, newname) != 0)
					return (ADE_RENAME);
			} else
				return (ADE_STRERR);

			path = newname;
		}

		/* 'current' symlink is (now) invalid so remove it. */
		(void) unlink(AUDIT_CURRENT_LINK);

		/* Note the crash recovery in current audit trail */
		err = auditd_gen_record(AUE_audit_recovery, path);
		if (err)
			return (err);
	}

	if (len < 0 && errno != ENOENT)
		return (ADE_READLINK);

	if (symlink(curfile, AUDIT_CURRENT_LINK) != 0)
		return (ADE_SYMLINK);

	return (0);
}

/*
 * Do just what we need to quickly start auditing.  Assume no system logging or
 * notify.  Return:
 *   0	 on success,
 *  -1   on failure.
 */
int
audit_quick_start(void)
{
	int err;
	char *newfile = NULL;
	time_t tt;
	char TS[TIMESTAMP_LEN];
	int ret = 0;

	/* 
	 * Mask auditing of this process.
	 */
	if (auditd_prevent_audit() != 0)
		return (-1);

	/*
	 * Read audit_control and get log directories.
	 */
        err = auditd_read_dirs(NULL, NULL);
	if (err != ADE_NOERR && err != ADE_SOFTLIM)
		return (-1);

	/*
	 *  Create a new audit trail log.
	 */
	if (getTSstr(tt, TS, TIMESTAMP_LEN) != 0)
		return (-1);
	err = auditd_swap_trail(TS, &newfile, getgid(), NULL);
	if (err != ADE_NOERR && err != ADE_ACTL) {
		ret = -1;
		goto out;
	}

	/*
	 * Add the current symlink and recover from crash, if needed. 
	 */
	if (auditd_new_curlink(newfile) != 0) {
		ret = -1;
		goto out;
	}

	/*
	 * At this point auditing has started so generate audit start-up record.
	 */
	if (auditd_gen_record(AUE_audit_startup, NULL) != 0) {
		ret = -1;
		goto out;
	}

	/*
	 *  Configure the audit controls.
	 */
	(void) auditd_set_evcmap();
	(void) auditd_set_namask();
	(void) auditd_set_policy();
	(void) auditd_set_fsize();
	(void) auditd_set_minfree();
	(void) auditd_set_host();

out:
	if (newfile != NULL)
		free(newfile);

	return (ret);
}

/*
 * Shut down auditing quickly.  Assumes that is only called on system shutdown.
 * Returns:
 *	 0	on success,
 *	-1	on failure.
 */
int
audit_quick_stop(void)
{
	int len;
	int cond;
	char *ptr;
	time_t tt;
	char oldname[MAXPATHLEN];
	char newname[MAXPATHLEN];
	char TS[TIMESTAMP_LEN];

	/*
	 * Auditing already disabled?
	 */
	if (audit_get_cond(&cond) != 0)
		return (-1);
	if (cond == AUC_NOAUDIT)
		return (0);

	/*
	 *  Generate audit shutdown record.
	 */
	(void) auditd_gen_record(AUE_audit_shutdown, NULL);

	/*
	 * Shutdown auditing in the kernel.
	 */
	cond = AUC_DISABLED;
	if (audit_set_cond(&cond) != 0)
		return (-1);
#ifdef	__BSM_INTERNAL_NOTIFY_KEY
	notify_post(__BSM_INTERNAL_NOTIFY_KEY);
#endif

	/*
	 * Rename last audit trail and remove 'current' link.
	 */
	len = readlink(AUDIT_CURRENT_LINK, oldname, MAXPATHLEN - 1);
	if (len < 0)
		return (-1);
	oldname[len++] = '\0';

	if (getTSstr(tt, TS, TIMESTAMP_LEN) != 0)
		return (-1);

	strlcpy(newname, oldname, len);

	if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) {
		memcpy(ptr, TS, POSTFIX_LEN);
		if (rename(oldname, newname) != 0)
			return (-1);
	} else
		return (-1);
	
	(void) unlink(AUDIT_CURRENT_LINK);

	return (0);
}

Man Man