Corrupted avc log messages
by Peter Martuccelli
Hello,
I had some of the SELinux folks reporting a problem with corrupted avc
messages, truncated entries, concatenated entries, etc. I reproduced
the problem for the first time last week after a 16 hour SMP test run.
I can now reproduce the issue within 10 minutes on a UP system running
2.6.9, which is a much better test environment. I will be adding some
debug code to determine the root cause, I will follow up with a patch
when finished.
Regards,
Peter
19 years, 12 months
Audit daemon from the SGI OB1 project
by Casey Schaufler
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(a)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);
}
19 years, 12 months
audit 0.6 release
by Steve Grubb
Hi,
The next version of auditd is available. You can get it from here:
http://people.redhat.com/sgrubb/audit/index.html and it will be available in
rawhide tomorrow morning.
Its still under development, but significant progress has been made. I'm not
quite ready for bug reports, but that day is getting closer. If you see
missing functionality that's not on the TODO list in the top directory, let
me know. If you have patches...even better.
Thanks,
-Steve Grubb
19 years, 12 months
AVC messages
by Steve Grubb
Hi,
I was looking at my audit logs and have a question. Does the SE Linux AVC
denial messages constitute something that ought to be in the audit logs? Or
does it belong in syslog?
I agree that it is important information...just curious where it should really
live.
Thanks,
-Steve Grubb
19 years, 12 months
RE: AVC messages
by Chad Hanson
Stephen Smalley wrote:
> But I don't see why that should prevent you
> from handling
> SELinux audit messages via auditd and directing them to a MAC
> audit log
> file. The kernel logging infrastructure can't really handle the
> potential load of SELinux audit, and you don't really want
> SELinux audit
> messages intermingled with other kernel log messages.
>
What type of audit log separation are you suggesting?
I would think SELinux AVC messages could logged to separate location.
However, even a failed request because of DAC needs to have complete MAC
information (label/type) of subject and object in the audit record for LSPP.
Does this match up to what you were stating?
-Chad
____________________________
Chad Hanson
Senior Secure Systems Engineer
Trusted Computer Solutions
121 W Goose Alley
Urbana, IL 61801
www.TrustedCS.com
V: 217.384.0028 ext.12
F: 217.384.0288
19 years, 12 months