[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Audit daemon from the SGI OB1 project



Some time ago SGI released a bunch of code under
a project called "ob1". The Irix audit daemon was
included. I have attached the code so y'all can
see how another system did the wild-and-crazy things
we've been discussing today.


=====
Casey Schaufler
casey schaufler-ca com

__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 
/*
 * 
 * Copyright 1990, 1991, Silicon Graphics, Inc. 
 * All rights reserved.
 *     
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
 * satd	-	reliably preserve the system audit trail
 */

#ident  "$SGIRevision: 1.29 $"

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sat.h>
#include <sys/syssgi.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <syslog.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <getopt.h>
#include <pwd.h>
#include <sys/capability.h>

#ifndef	TRUE
#define	TRUE	1
#define FALSE	0
#endif  /* TRUE */

#define	BUFLEN			  0x2000	/* 8 K */
#define	SATD_MAX_DIRF_SZ	0x400000	/* 4 Megabytes */
#define	SATD_MIN_DIRF_SZ	 0x80000	/* 512 K */

#define	SATD_MAX_INTR_PER_IO	8

#define EMERGENCY_RESERVE_SIZE	250000		/* 250 K of reserve space */
#define EMERGENCY_RESERVE_FILE	"/sat/satd.reserve"
#define EMERGENCY_FILE_MASK	"/sat/satd.emergency-%d"

int	emergency_fd = -1;

typedef struct  list_t {
	struct  list_t *        next;
	struct  list_t *        prev;
	int                     datum;
}       list_t;

typedef	struct	path_t {
	enum	{
		path_err,		/* output path spec is in error      */
		path_dir,		/* output path spec is a directory   */
		path_dev,		/* output path spec is a device      */
		path_fil,		/* output path spec is a reg. file   */
		path_nul		/* output path spec is /dev/null     */
	}	path_dscr;	/* describes output - dir,dev,fil,nul*/
	int		path_fd;	/* file descriptor for path          */
	char *		path_name;	/* name of path from command line    */
	char 		path_ext [16];	/* extension to path name for dirs   */
	int		path_max_size;	/* maximum bytes path can hold       */
	off64_t		path_size;	/* number of bytes currently in path */
	off64_t		path_room;	/* fstatfs again after room consumed */
	int		path_warn;	/* boolean, 1 if full warning issued */
	struct stat64   *path_stat;	/* stat info on path inode	     */
	struct statvfs64 path_statfs;	/* file system stat info for path    */
}	path_t;

typedef	enum	opt_e	{
	opt_path,			/* option specifies an output path   */
	opt_input,			/* option to use standard input	     */
	opt_output,			/* option to copy to std output	     */
	opt_firstpath,			/* prior to replacement use 1st path */
	opt_onepass,			/* optional replacement algorithm    */
	opt_rotation,			/* optional replacement algorithm    */
	opt_preference,			/* optional replacement algorithm    */
	opt_verbose,			/* option to print annoying chatter  */
	opt_valuecontext,		/* option to add context to stream   */
	opt_oneshot			/* option to sample trail only once  */
}	opt_e;			/* command line options		     */

#define	OPT_PATH		(1 << opt_path)
#define	OPT_INPUT		(1 << opt_input)
#define	OPT_OUTPUT		(1 << opt_output)
#define	OPT_FIRSTPATH		(1 << opt_firstpath)
#define	OPT_ONEPASS		(1 << opt_onepass)
#define	OPT_ROTATION		(1 << opt_rotation)
#define	OPT_PREFERENCE		(1 << opt_preference)
#define	OPT_VERBOSE		(1 << opt_verbose)
#define	OPT_ONESHOT		(1 << opt_oneshot)

/* function prototype definitions */
void	 satd_usage (void);
void	 satd_exit (char *, double);
path_t * satd_parse_pathname (char *);
void	 satd_pick_pathname (list_t *, char *);
void	 satd_show_paths (list_t *);
void	 satd_show_parms (list_t *, int);
int	 satd_send (list_t *, char *, int, int);
list_t * satd_replace_path (list_t *, list_t *, int);
int	 satd_open_path (path_t *);
void	 satd_close_path (path_t *);
int	 satd_path_full (path_t *);
int	 satd_open_dirf (path_t *);
int	 satd_dirf_full (path_t *);
void	 satd_close_dirf (path_t *);
void	 satd_sgnl (int);

/* globals */
int	satd_signalled = 0;
int	satd_signo = 0;
int	select_opts = 0;
int	cap_enabled;
uid_t	ruid;
uid_t	auditor_uid;

cap_t
acquire_audit_control (void)
{
	cap_t ocap = NULL;
	const cap_value_t cap_audit_control = CAP_AUDIT_CONTROL;

	if (cap_enabled)
		ocap = cap_acquire (1, &cap_audit_control);
	else
		(void) setreuid(auditor_uid, ruid);
	return (ocap);
}

void
relinquish_audit_control (cap_t ocap)
{
	if (cap_enabled)
		cap_surrender (ocap);
	else
		(void) setreuid(ruid, auditor_uid);
}

void
satd_sgnl (int sig)
{
	if (sig == SIGHUP || sig == SIGTERM) {
		satd_signalled = 1;
		satd_signo = sig;
		(void) signal (sig, satd_sgnl);
		return;
	}
	/* SIGBUS or SIGSEGV */
	if (sig == SIGBUS)
		syslog(LOG_ALERT, "satd died with bus error!");
	if (sig == SIGSEGV)
		syslog(LOG_ALERT, "satd died with segmentation violation!");

	chdir("/");
	kill(0, SIGQUIT); /* dump core */
}

/*
 * satd_usage -	display usage string, invoked when options are used improperly
 */
void
satd_usage (void) {
	fprintf (stderr, "usage: satd [-f path ...] [-i] [-o] ");
	fprintf (stderr, "[-r replacement-mode] [-v] [-1]\n");
	fprintf (stderr, "        -f and/or -o option must be specified\n");
	syslog(LOG_ERR, "usage error: exiting.");
	exit (1);
}


/*
 * satd_exit -	give some information about the reason for a failure exit
 */
void
satd_exit (char * errstring, double location)
{
	static int already_called_exit = 0;
	FILE *fd;
	int amt_read;
	char buffer[BUFLEN];
	int i;

	if (location)
		syslog (LOG_ALERT, errstring, location);
	else
		syslog (LOG_ALERT, errstring);

	if (already_called_exit) {
		syslog (LOG_EMERG,
		    "Satd recovery failure!  System will probably hang soon.");
		exit(1);
	}

	already_called_exit = 1;

	syslog (LOG_EMERG,
	    "all output paths full -- system shutdown in 10 seconds!");

	/* write all users */
	if ((fd = popen("/etc/wall","w")) != NULL) {
		fprintf(fd, "Warning! All audit output paths full -- %s",
			"system shutdown in 10 seconds!\n");
		pclose(fd);
	}

	/* shut down system to single-user mode */

	switch(fork()) {
	case 0:
		/* Child: give users a whole ten seconds */
		sleep (10);
		/*
		 * init s requires stdin be a terminal, so give it
		 * the console.
		 */
		(void) close(0);
		(void) open("/dev/console",O_RDONLY);
		(void) execl("/etc/init", "init", "s", 0);
		satd_exit("/etc/init exec failure: %m", 0.0);
		break;
	case -1:
		satd_exit("fork failure: %m", 0.0);
		break;
	}

	/*
	 * Parent: switch to emergency file
	 */

	/* free pre-allocated space */
	(void) unlink(EMERGENCY_RESERVE_FILE);

	for (i=0; i < 10; i++) {
		char emergency_file[MAXPATHLEN];

		sprintf(emergency_file, EMERGENCY_FILE_MASK, i);
		emergency_fd = open(emergency_file,
				    O_WRONLY | O_CREAT | O_EXCL | O_SYNC, 0600);
		if (emergency_fd >= 0 || errno != EEXIST)
			break;
	}

	if (emergency_fd < 0 || sat_write_filehdr(emergency_fd) < 0)
		satd_exit("Can't open reserve file: %m", 0.0);

	for (;;) {
		if (OPT_INPUT & select_opts)
			amt_read = read (0, buffer, sizeof(buffer));
		else {
			cap_t ocap = acquire_audit_control();
			amt_read = satread (buffer, sizeof(buffer));
			relinquish_audit_control(ocap);
		}
		if (amt_read < 0)
			satd_exit("Can't read records: %m", 0.0);

		if (write(emergency_fd, buffer, amt_read) < 0)
			satd_exit("Can't write records: %m", 0.0);
	}

	/* NOTREACHED */
}


/*
 * satd_parse_pathname - test pathname for validity, determine (dir|dev|file)
 */
path_t *
satd_parse_pathname (char * name_p)
{
	path_t *	path_p;
	struct stat64	dev_null_stat;
	static ino64_t	dev_null_inode_num = 0;
	static dev_t	dev_null_dev_num = 0;

	/* get inode number for /dev/null, if it isn't already known	     */
	/* /dev/null is treated as a special case, unlike other char devices */
	if (!dev_null_inode_num) {
		if (-1 == stat64("/dev/null", &dev_null_stat)) {
			syslog (LOG_ERR, "can't stat /dev/null");
		}
		else {
			dev_null_inode_num = dev_null_stat.st_ino;
			dev_null_dev_num = dev_null_stat.st_dev;
		}
	}

	/* obtain file system stats for the path */
	path_p = malloc (sizeof (*path_p));
	path_p->path_name = name_p;
	path_p->path_stat = malloc (sizeof (*path_p->path_stat));
	if (-1 == stat64 (name_p, path_p->path_stat)) {
		int fd;
		fd = creat(path_p->path_name, 0600);
		if (fd < 0) {
			syslog (LOG_ERR, "attempt to create %s failed: %m",
				path_p->path_name);
			path_p->path_dscr = path_err;
		} else {
			syslog (LOG_INFO, "creating file %s",
				path_p->path_name);
			(void) stat64(path_p->path_name, path_p->path_stat);
			close(fd);
			path_p->path_dscr = path_fil;
		}
		return (path_p);
	}

	/* determine the type of path */
	switch (S_IFMT & (path_p->path_stat->st_mode)) {
	case S_IFDIR:
		path_p->path_dscr = path_dir;
		break;
	case S_IFCHR:
		if (path_p->path_stat->st_ino == dev_null_inode_num &&
		    path_p->path_stat->st_dev == dev_null_dev_num) {
			path_p->path_dscr = path_nul;
			break;
		}
		path_p->path_dscr = path_dev;
		break;
	case S_IFREG:
		path_p->path_dscr = path_fil;
		break;
	default:
		path_p->path_dscr = path_err;
		syslog(LOG_ERR, "ignoring path: %s", path_p->path_name);
		fprintf (stderr, "\tpath is neither directory, ");
		fprintf (stderr, "character special device, nor disk file\n");
	}

	return (path_p);
}


/*
 * parse paths from an ascii list of path names
 */
void
satd_pick_pathname (list_t * head_p, char * argu_p)
{
	path_t *	path_p;
	char *          name_p;
	list_t *        new_item_p;
	const char *	pattern = ", \t";

	if (!head_p->next) {    /* initialize list if not yet done           */
		head_p->next  = head_p;
		head_p->prev  = head_p;
		head_p->datum = NULL;
	}
	name_p = strtok (argu_p, pattern);

	while (name_p) {
		path_p = satd_parse_pathname (name_p);
		if (path_err == path_p->path_dscr) {
			/* fprintf (stderr, "satd: bad path: %s\n", name_p); */
			name_p = strtok (0, pattern);
			continue;
		}

		name_p = strtok (0, pattern);
		new_item_p = malloc (sizeof (*new_item_p));
		new_item_p->next = head_p;
		new_item_p->prev = head_p->prev;
		head_p->prev->next = new_item_p;
		head_p->prev = new_item_p;
		new_item_p->datum = (int) path_p;
	}
}


/*
 * satd_show_paths - display output paths for verbose user
 */
void
satd_show_paths (list_t * path_list_p)
{
	list_t *	curs;
	path_t *	path;

	fprintf (stderr, "Path list:\n");
	curs = path_list_p -> next;
	if (curs == NULL) {
		fprintf(stderr, "\t(none)\n");
		return;
	}
	while (NULL != (path = (path_t *) curs->datum)) {
		fprintf (stderr, "\tPath = %s\n", path->path_name);
		switch (path->path_dscr) {
		case path_err:
			fprintf (stderr, "\t\t(Error in specification)\n");
			continue;
		case path_dir:
			fprintf (stderr, "\t\t(directory)\n");
			break;
		case path_dev:
			fprintf (stderr, "\t\t(device)\n");
			break;
		case path_fil:
			fprintf (stderr, "\t\t(regular file)\n");
			break;
		case path_nul:
			fprintf (stderr, "\t\t(bit bucket)\n");
			break;
		default:
			satd_exit ("satd_show_paths internal error", 0.0);
		}
		curs = curs -> next;
	}
}


/*
 * satd_show_parms - display command line parameters for verbose user
 */
void
satd_show_parms (list_t * path_list_p, int select_opts)
{
	fprintf (stderr, "\n");
	satd_show_paths (path_list_p);
	fprintf (stderr, "\n");

	if (OPT_INPUT		& select_opts)
		fprintf (stderr, "Input comes from std input.\n");
	else
		fprintf (stderr, "Input comes from sat subsys.\n");

	if (OPT_OUTPUT		& select_opts)
		fprintf (stderr, "Output goes to std output.\n");
	else
		fprintf (stderr, "Output goes to archive only.\n");

	fprintf (stderr, "Path replacement algorithm = ");

	if (OPT_ONEPASS		& select_opts)
		fprintf (stderr, "onepass.\n");

	if (OPT_PREFERENCE	& select_opts)
		fprintf (stderr, "preference.\n");

	if (OPT_ROTATION		& select_opts)
		fprintf (stderr, "rotation.\n");

	if (OPT_VERBOSE		& select_opts)
		fprintf (stderr, "Messages are verbose.\n");
	else
		fprintf (stderr, "Messages are terse.\n");

	fprintf (stderr, "\n");
}


/*
 * satd_replace_path -	find next openable output path using user specified
 * 			replacement policy
 */
list_t *
satd_replace_path (list_t * path_list_p, list_t * curr_path_p, int replacement)
{
	list_t *	test_path_p;

	if (OPT_FIRSTPATH & replacement) {
		test_path_p = path_list_p->next;
		if (satd_open_path ((path_t *) test_path_p->datum))
			return (test_path_p);
		/* else find next path with user chosen replacement policy   */
	}

	if (OPT_ONEPASS & replacement) {
		test_path_p = curr_path_p;
		for (;;) {
			test_path_p = test_path_p->next;
			if (NULL == test_path_p->datum)
				satd_exit("Onepass path search complete", 0.0);
			if (satd_open_path ((path_t *) test_path_p->datum))
				return (test_path_p);
		}
	}

	if (OPT_PREFERENCE & replacement) {
		test_path_p = path_list_p;
		for (;;) {
			test_path_p = test_path_p->next;
			if (NULL == test_path_p->datum)
				satd_exit("Preference path search fails", 0.0);
			if (satd_open_path ((path_t *) test_path_p->datum))
				return (test_path_p);
		}
	}

	if (OPT_ROTATION & replacement) {
		test_path_p = curr_path_p;
		for (;;) {
			test_path_p = test_path_p->next;
			if (NULL == test_path_p->datum)
				test_path_p = test_path_p->next;
			if (test_path_p->datum == curr_path_p->datum)
				satd_exit ("Rotation path search fails", 0.0);
			if (satd_open_path ((path_t *) test_path_p->datum))
				return (test_path_p);
		}
	}

	satd_exit ("Internal error", 0.0);
	/* NOTREACHED */
}


/*
 * satd_path_full
 * Return 1 if no more data can be written to path, else 0
 */
int
satd_path_full (path_t *path_p)
{
	struct statvfs64 *path_fs_p;
	off64_t		 free_space;	/* Calculated bytes free */
	int		 fullness;	/* percent full */

	/*
	 * path_room tells how much room is left to fill before free space
	 * must be checked again
         */

	if (path_nul != path_p->path_dscr && 0 <= path_p->path_room) {

		path_fs_p = &(path_p->path_statfs);

		/* obtain space availability info on path */
		if (0 > fstatvfs64(path_p->path_fd, path_fs_p)){
			syslog (LOG_ERR,
			   "can't fstatvfs64: %s",path_p->path_name);
			return (TRUE);
		}

		free_space = path_fs_p->f_bsize * path_fs_p->f_bfree;

		/* path completely full */
		if (BUFLEN >= free_space)
			return (TRUE);

		/* determine how soon free space must be checked again       */
		path_p->path_room =
			(MIN(SATD_MAX_DIRF_SZ, free_space/2)) / 16 - BUFLEN;
		if (path_p->path_room < 0)
			path_p->path_room = free_space/4 - BUFLEN;

		/* path approaching fullness and never warned before */
		fullness = 100-((100*path_fs_p->f_bfree)/path_fs_p->f_blocks);
		if ((!path_p->path_warn) && (90 <= fullness)) {
			path_p->path_warn = TRUE;
			syslog (LOG_WARNING, "path %3d percent full: %s",
			   fullness, path_p->path_name);
		}
	}

	return (FALSE);
}


/*
 * satd_dirf_full -	check whether the directory file is large enough
 */
int
satd_dirf_full (path_t *path_p)
{
	return ((path_p->path_size >= path_p->path_max_size) ? TRUE : FALSE);
}


/*
 * satd_open_dirf -	invent a unique name for a file in this directory;
 *			attempt to create the newly named file; return success
 */
int
satd_open_dirf (path_t * path_p)
{
	time_t		 now;
	char		 dirf_name[MAXPATHLEN];	/* name of file in this dir  */
        struct statvfs64 *path_fs_p;
        struct stat64	 statbuf;
	off64_t		 free_space;

	/*
	 * Form a unique file name out of year, month, day, hour, & minute
	 * There's a (small) chance the file already exists, in which case
	 * The minute is incremented.
	 */
	now = time(0);
	cftime(path_p->path_ext, "sat_%y%m%d%H%M", &now);
	sprintf(dirf_name, "%s/%s", path_p->path_name, path_p->path_ext);

	while (stat64(dirf_name, &statbuf) >= 0) {
		/*
		 * Since the file exists, use the next minute.
		 * XXX: There's no guarantee this'll ever finish.
		 */
		now += 60;
		cftime(path_p->path_ext, "sat_%y%m%d%H%M", &now);
		sprintf(dirf_name,"%s/%s", path_p->path_name, path_p->path_ext);
	}
	/*
	 * Attempt to open the newly named unique file
	 */
	path_p->path_fd = open(dirf_name, O_EXCL|O_CREAT|O_RDWR, 0600);

	if (path_p->path_fd < 0) {
		syslog(LOG_WARNING, "can't open %s", dirf_name);
		return (FALSE);
	}

	if (sat_write_filehdr(path_p->path_fd) < 0) {
		syslog(LOG_WARNING, "can't write header to %s", dirf_name);
		return (FALSE);
	}

        /*
	 * Obtain space availability info on path
	 */
        path_fs_p = & (path_p->path_statfs);
        if (0 > fstatvfs64(path_p->path_fd, path_fs_p)){
                syslog (LOG_ERR, "can't fstatfs: %s", path_p->path_name);
		path_p->path_max_size = SATD_MAX_DIRF_SZ;
	} else {
        	free_space = path_fs_p->f_bsize * path_fs_p->f_bfree;
		path_p->path_max_size = MIN (SATD_MAX_DIRF_SZ, free_space/2);
		if (path_p->path_max_size < SATD_MIN_DIRF_SZ)
			path_p->path_max_size = SATD_MIN_DIRF_SZ;
	}

	path_p->path_size = 0;
	path_p->path_warn = FALSE;
	return (TRUE);
}


/*
 * satd_close_dirf -	close directory file because it's full
 */
void
satd_close_dirf (path_t * path_p)
{
	(void) sat_close_filehdr(path_p->path_fd);
	(void) close (path_p->path_fd);

	syslog (LOG_NOTICE, "closing directory file %s/%s",
		path_p->path_name, path_p->path_ext);
}


/*
 * satd_open_path -	attempt to open the indicated path; return success
 */
int
satd_open_path (path_t * path_p)
{
	if (!path_p) {
		syslog (LOG_DEBUG, "null path pointer");
		return (FALSE);
	}

	switch (path_p->path_dscr) {
	case path_dir:
		if (satd_open_dirf (path_p) == FALSE)
			return FALSE;
		break;
	case path_dev:
		/* not supported 'cause I can't figure out yet how to	     */
		/* tell how big a tape is in advance of filling it up	     */
		syslog (LOG_ERR, "satd device output not supported yet");
		return (FALSE);
	case path_fil:
	case path_nul:
		path_p->path_fd = open (path_p->path_name,
					O_RDWR|O_CREAT|O_TRUNC, 0600);
		if (path_p->path_fd < 0
		    || sat_write_filehdr(path_p->path_fd) < 0) {
			syslog (LOG_WARNING, "can't open %s",
				path_p->path_name);
			return (FALSE);
		}
		if (path_p->path_dscr == path_nul)
			syslog (LOG_WARNING, "discarding audit records");
		break;
	default:
		return (FALSE);
	}

	path_p->path_warn = FALSE;
	syslog (LOG_INFO, "opening path %s", path_p->path_name);

	/* checking path fullness has the side effect of setting path_room   */
	path_p->path_room = 0;
	if (satd_path_full (path_p))
		syslog (LOG_DEBUG, "opened full path %s", path_p->path_name);

	return (TRUE);
}


/*
 * satd_close_path -	close file, device, or directory because it's full
 */
void
satd_close_path (path_t * path_p)
{
	(void) sat_close_filehdr(path_p->path_fd);
	(void) close (path_p->path_fd);

	syslog (LOG_NOTICE, "closing path %s", path_p->path_name);
}


/*
 * satd_send - send the audit buffer to the best available user specified path
 */
int
satd_send (list_t * path_list_p, char * buffer, int bufsize, int opts)
{
	static list_t * current	= NULL;		/* pt to ptr to current path */

	path_t *	path_p;			/* path currently in use     */
	int		amt_writ;		/* number of bytes output    */

	/* if no path has ever been opened, open the first one               */
	if (!current)
		current = path_list_p;
	path_p = (path_t *) current->datum;
	if (!path_p) {
		current = satd_replace_path (path_list_p, current,
					     (OPT_FIRSTPATH | opts));
		path_p = (path_t *) current->datum;
	}

	/* on receipt of a sighup, try to switch output paths		     */
	if (satd_signalled) {
		switch (satd_signo) {
		case SIGTERM:
			satd_close_path (path_p);
			syslog(LOG_ALERT, "received SIGTERM -- exiting.");
			exit(1);
			break;
		case SIGHUP:
		default:
			if (OPT_VERBOSE & opts)
				fprintf (stderr, "satd - sighup received\n");
			satd_close_path (path_p);
			if (opts & OPT_ONEPASS)
				current = satd_replace_path (path_list_p, path_list_p, opts | OPT_FIRSTPATH);
			else
				current = satd_replace_path (path_list_p, current, opts);
			path_p = (path_t *) current->datum;
			break;
		}
		satd_signalled = 0;
	}

	/* if we're just here to serve the interrupt, return */
	if (bufsize == 0)
		return 0;

	/* if the current path is full, replace it with a new path           */
	if (satd_path_full (path_p)) {
		if (OPT_VERBOSE & opts)
			fprintf (stderr, "satd - current path full\n");
		satd_close_path (path_p);
		current = satd_replace_path (path_list_p,current,opts);
		path_p = (path_t *) current->datum;
	}

	/* if the current path is a directory, and if the directory file     */
	/* is full, replace with a new directory file                        */
	if ((path_dir == path_p->path_dscr) && (satd_dirf_full (path_p))) {
		if (OPT_VERBOSE & opts)
			fprintf (stderr, "satd - replacing directory file\n");
		satd_close_dirf (path_p);
		if (!satd_open_dirf  (path_p)) {
			if (OPT_VERBOSE & opts)
				fprintf (stderr,
					"satd - can't replace dir. file\n");
			satd_close_path (path_p);
			current = satd_replace_path (path_list_p,current,opts);
			path_p = (path_t *) current->datum;
		}
	}

	amt_writ = write (path_p->path_fd, buffer, bufsize);
	if (amt_writ == -1) {
		amt_writ = 0;
		path_p->path_room = 0;
	}
	else {
		path_p->path_size += amt_writ;
		path_p->path_room -= amt_writ;

		if (amt_writ < bufsize) {
			syslog (LOG_ALERT,
				"satd short write - %d asked but %d written",
				bufsize, amt_writ);
			current = satd_replace_path (path_list_p,current,opts);
		}
	}

	return amt_writ;
}


void
main (int argc, char *argv[])
{
	int		c;		/* option character		     */
	struct passwd * pw;		/* passwd struct for auditor	     */

	static	list_t	path_list;	/* dirs, devs, files for output	     */

	int	num_bufs_read = 0;	/* count of times through main loop  */
	char	buffer	 [ BUFLEN ];	/* read buffer                       */
	int	amt_reqd = BUFLEN;	/* requested  read length            */
	int	amt_read = 0;		/* the actual read length            */
	int	i;			/* counts repeated reads & writes    */
	int	io_done;		/* boolean, true if io succeeds	     */

	cap_t	ocap;
	cap_value_t cap_setuid = CAP_SETUID;

	ruid = getuid();

	/*
	 * Error messages go to both stderr and syslog until the fork().
	 */
	openlog("satd", LOG_CONS | LOG_PERROR, LOG_AUDIT);

	/* must be superuser */

	if (geteuid() != (uid_t) 0) {
		syslog(LOG_ERR, "Only root can run satd.");
		exit(1);
	}

	if (sysconf (_SC_AUDIT) <= 0) {
		syslog(LOG_ERR, "Audit not configured -- exiting.");
		exit(1);
	}

	cap_enabled = (sysconf (_SC_CAP) == CAP_SYS_NO_SUPERUSER);

	/* find out who auditor is on this system */

	pw = getpwnam("auditor");
	auditor_uid = (pw == NULL ? 0 : pw->pw_uid);

	ocap = cap_acquire (1, &cap_setuid);
	(void) setreuid (ruid, auditor_uid);
	cap_surrender (ocap);

	emergency_fd = open(EMERGENCY_RESERVE_FILE, O_RDWR | O_CREAT, 0600);
	if (emergency_fd < 0
	 || lseek(emergency_fd, EMERGENCY_RESERVE_SIZE-3, SEEK_SET) < 0
	 || write(emergency_fd, "xyz", 3) < 0) {
		(void) unlink(EMERGENCY_RESERVE_FILE);
		syslog(LOG_WARNING, "can't make reserve file: %m");
	}
	(void) close(emergency_fd);
	emergency_fd = -1;

	/* parse command line options	*/
	 while ((c = getopt(argc, argv, "1f:ior:v")) != EOF)
		switch (c) {
		case '1':
			select_opts |= OPT_ONESHOT;
			amt_reqd--;
			break;
		case 'f':
			select_opts |= OPT_PATH;
			satd_pick_pathname (&path_list, optarg);
			break;
		case 'i':
			select_opts |= OPT_INPUT;
			break;
		case 'o':
			select_opts |= OPT_OUTPUT;
			break;
		case 'r':
			if ((OPT_ROTATION | OPT_PREFERENCE | OPT_ONEPASS)
					& select_opts) {
				fprintf (stderr,
				"Only use one replacement strategy at a time");
				satd_usage();
			}
			if (!strcmp (optarg, "rotation"))
				select_opts |= OPT_ROTATION;
			if (!strcmp (optarg, "preference"))
				select_opts |= OPT_PREFERENCE;
			if (!strcmp (optarg, "onepass"))
				select_opts |= OPT_ONEPASS;
			break;
		case 'v':
			select_opts |= OPT_VERBOSE;
			break;
		default:
			satd_usage();
	}
	
	/* assign defaults if the user hasn't fully specified the command */
	if ((!(OPT_ONEPASS	& select_opts))
	&&  (!(OPT_PREFERENCE	& select_opts))
	&&  (!(OPT_ROTATION	& select_opts))) {
		select_opts |= OPT_PREFERENCE;
		if (OPT_VERBOSE & select_opts)
			fprintf (stderr,
				"Path replacement algorithm defaulted.\n");
	}

	/* confirm parameters - a debug option	*/
	if (OPT_VERBOSE & select_opts)
		satd_show_parms (&path_list, select_opts);

	/*
	 * Turn off all events for this process except fork, exec
	 * and exit so we can see what satd (and any children) are
	 * up to.  For these, we retain the default system setting.
	 */
	ocap = acquire_audit_control();
	for (i = 0; i < SAT_NTYPES; i++) {
		if (i == SAT_EXIT || i == SAT_EXEC || i == SAT_FORK)
			continue;
		(void) syssgi(SGI_SATCTL, SATCTL_LOCALAUDIT_OFF, i, 0);
	}
	relinquish_audit_control(ocap);

	/* put ourselves in the background */

#ifdef DEBUG
	/* catch signals	*/
	(void) signal(SIGHUP, satd_sgnl);
	(void) signal(SIGTERM, satd_sgnl);
	(void) signal(SIGBUS, satd_sgnl);
	(void) signal(SIGSEGV, satd_sgnl);
	/* reopen the log so msgs just go to syslog */
	openlog("satd", LOG_CONS, LOG_AUDIT);
#else /* NODEBUG */
	switch (fork()) {
	case -1:
		syslog(LOG_ERR, "Cannot fork: %m");
		exit(1);
	case 0:
		/* child */
		{ int fd = open("/dev/tty", O_RDWR);
		  if (fd > 0)
			(void) ioctl(fd, TIOCNOTTY, (char *)0);
		  closelog();
		  for (fd = getdtablesize(); --fd >= 0;) {
			if (fd == emergency_fd ||
			    (OPT_INPUT & select_opts) && fd == 0 ||
			    (OPT_OUTPUT & select_opts) && fd == 1 ||
			    (OPT_VERBOSE & select_opts) && fd == 2)
				continue;
			(void) close(fd);
		  }
		}
		/* reopen the log so msgs just go to syslog */
		openlog("satd", LOG_CONS, LOG_AUDIT);

		/* catch signals	*/
		(void) signal(SIGHUP, satd_sgnl);
		(void) signal(SIGTERM, satd_sgnl);
		(void) signal(SIGBUS, satd_sgnl);
		(void) signal(SIGSEGV, satd_sgnl);

		/* make sure we're the only sat daemon (unless we are */
		/* reading from stdin)                                */
		ocap = acquire_audit_control();
		if (!(OPT_INPUT & select_opts) &&
		    syssgi(SGI_SATCTL, SATCTL_REGISTER_SATD) < 0) {
			if (errno == EBUSY) {
				relinquish_audit_control(ocap);
				syslog(LOG_ERR,"Already a sat daemon running.");
				if (OPT_VERBOSE & select_opts)
				    	fprintf(stderr, 
					"Already a sat daemon running.\n");
				exit(1);
			} else
				syslog(LOG_WARNING,
				       "Warning, can't register satd: %m.");
		}
		relinquish_audit_control(ocap);

		break;
	default:
		/* parent */
		exit(0);
	}
#endif
	/*
	 * -f or -o option must be specified .
	 * Otherwise, open emergency file and take system down
	 * to single user mode.
	 */

	if ( !((OPT_OUTPUT | OPT_PATH) & select_opts)) {
		satd_exit("must specify -f and/or -o option", 0.0);
	}

        /* Write file header to stdout if needed */
	if (OPT_OUTPUT & select_opts) {
		if (sat_write_filehdr(1) < 0) {
			satd_exit("Can't write sat file header to"
				"stdout: %m",0);
		}
		fprintf(stderr,"Wrote file header to stdout\n");
	}

	/* gather records	*/
	for (;;) {
		num_bufs_read++;

		for (i = SATD_MAX_INTR_PER_IO, io_done = 0;
				(i > 0) && !io_done; i--) {

			if (OPT_INPUT & select_opts)
				amt_read = read (0, buffer, amt_reqd);
			else {
				ocap = acquire_audit_control();
				amt_read = satread (buffer, amt_reqd);
				relinquish_audit_control(ocap);
			}

			if (amt_read < 0) {
				if (errno == EINTR) {
					if (satd_signalled) {
						amt_read = 0;
						break;
					}
					errno = 0;
					continue;
				}
				else if (errno == EPERM)
					satd_exit("No permission match on read",
						0.0);
				else
					satd_exit (
					"satd read error (near byte %.0f): %m",
					    (double)(num_bufs_read * amt_reqd));
			}
			else
				io_done = 1;
		}

		if ((OPT_OUTPUT & select_opts) &&
		    write(1, buffer, amt_read) < 0)
			satd_exit(
			"Can't write sat buffer to stdout (near byte %.0f): %m",
			    (double) (num_bufs_read * amt_reqd));

		if ((OPT_PATH & select_opts) &&
		    satd_send(&path_list, buffer, amt_read, select_opts) < 0)
			satd_exit("Can't write sat buffer (near byte %.0f): %m",
			    (double) (num_bufs_read * amt_reqd));

		/*
		 * option 'oneshot' reads all the records currently queued,
		 * then exits.  This can be used to flush the audit record
		 * queue.  If '-1' isn't used, satd blocks on read and loops
		 * forever.  Since all records are an even multiple of four
		 * bytes long, amt_reqd is an odd number, ensuring that
		 * satd won't stop prematurely when it encounters a string
		 * of records exactly BUFLEN long.
		 */
		if ((OPT_ONESHOT & select_opts)
				&& (amt_read != amt_reqd))
			break;
	}

	closelog ();

	exit(0);
}

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]