/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include <fcntl.h> #include <libscf.h> #include <secdb.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/file.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <sys/param.h> #include <unistd.h> #include <bsm/audit.h> #include <bsm/libbsm.h> #include <locale.h> #include <audit_sig_infc.h> #include <zone.h> #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SUNW_OST_OSCMD" #endif #define VERIFY -1 /* GLOBALS */ static char *auditdatafile = AUDITDATAFILE; static char *progname = "audit"; static char *usage = "audit [-n] | [-s] | [-t] | [-v filepath]"; static int silent = 0; static char *instance_name = "svc:/system/auditd:default"; static int get_auditd_pid(); static void display_smf_error(); static boolean_t is_audit_control_ok(char *); /* file validation */ static boolean_t is_valid_zone(boolean_t); /* operation ok in this zone? */ static void start_auditd(); /* start audit daemon */ /* * audit() - This program serves as a general administrator's interface to * the audit trail. Only one option is valid at a time. * * input: * audit -s * - signal audit daemon to read audit_control file and * start auditd if needed. * audit -n * - signal audit daemon to use next audit_control audit directory. * audit -t * - signal audit daemon to disable auditing. * audit -T * - signal audit daemon to disable auditing report no errors. * audit -v filepath * - validate audit_control parameters but use filepath for * the name. Emit errors or "syntax ok" * * * output: * * returns: 0 - command successful * >0 - command failed */ int main(int argc, char *argv[]) { pid_t pid; /* process id of auditd read from auditdatafile */ int sig = 0; /* signal to send auditd */ char c; char *first_option; /* Internationalization */ (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if (getuid() != 0) { (void) fprintf(stderr, gettext("%s: not super-user\n"), progname); exit(2); } /* first option required */ if ((c = getopt(argc, argv, "nstTv:")) == -1) { (void) fprintf(stderr, gettext("usage: %s\n"), usage); exit(3); } first_option = optarg; /* second or more options not allowed; please pick one */ if (getopt(argc, argv, "nstTv:") != -1) { (void) fprintf(stderr, gettext("usage: %s\n"), usage); exit(5); } switch (c) { case 'n': if (!is_valid_zone(1)) /* 1 == display error if any */ exit(10); sig = AU_SIG_NEXT_DIR; break; case 's': if (!is_valid_zone(1)) /* 1 == display error if any */ exit(10); else if (!is_audit_control_ok(NULL)) exit(7); start_auditd(); break; case 't': if (!is_valid_zone(0)) /* 0 == no error message display */ exit(0); /* use bmsunconv to permanently disable, -t for temporary */ if (smf_disable_instance(instance_name, SMF_TEMPORARY) != 0) display_smf_error(); break; case 'T': if (!is_valid_zone(0)) /* 0 == no error message display */ exit(0); (void) smf_disable_instance(instance_name, SMF_TEMPORARY); silent = 1; break; case 'v': if (is_audit_control_ok(first_option)) { (void) fprintf(stderr, gettext("syntax ok\n")); exit(0); } else { exit(8); } break; default: (void) fprintf(stderr, gettext("usage: %s\n"), usage); exit(6); } if (get_auditd_pid(&pid) != 0) { if (silent) { exit(0); } else { (void) fprintf(stderr, "%s: %s\n", progname, gettext( "can't get process id of auditd from audit_data(4)")); exit(4); } } if ((sig != 0) && (kill(pid, sig) != 0)) { if (silent) { exit(0); } else { perror(progname); (void) fprintf(stderr, gettext("%s: cannot signal auditd\n"), progname); exit(1); } } return (0); } /* * get_auditd_pid(&pid): * * reads PID from audit_data * * returns: 0 - successful * 1 - error */ static int get_auditd_pid(pid_t *p_pid) { FILE *adp; /* audit_data file pointer */ int retstat; if ((adp = fopen(auditdatafile, "r")) == NULL) { if (!silent) perror(progname); return (1); } retstat = (fscanf(adp, "%ld", p_pid) != 1); (void) fclose(adp); return (retstat); } /* * perform reasonableness check on audit_control or its standin; goal * is that "audit -s" (1) not crash the system and (2) c2audit/auditd * actually generates data. * * A NULL input is ok -- it is used to tell _openac() to use the * real audit_control file, not a substitute. */ #define TRADITIONAL_MAX 1024 static boolean_t is_audit_control_ok(char *filename) { char buf[TRADITIONAL_MAX]; int outputs = 0; int state = 1; /* 1 is ok, 0 is not */ int rc; int min; kva_t *kvlist; char *value; au_acinfo_t *ach; ach = _openac(filename); /* open audit_control */ if (ach == NULL) { perror(progname); exit(9); } /* * There must be at least one directory or one plugin * defined. */ if ((rc = _getacdir(ach, buf, TRADITIONAL_MAX)) == 0) { outputs++; } else if (rc < -1) { /* -1 is not found, others are errors */ (void) fprintf(stderr, gettext("%s: audit_control \"dir:\" spec invalid\n"), progname); state = 0; /* is_not_ok */ } /* * _getacplug -- all that is of interest is the return code. */ _rewindac(ach); /* rewind audit_control */ if ((rc = _getacplug(ach, &kvlist)) == 0) { value = kva_match(kvlist, "name"); if (value == NULL) { (void) fprintf(stderr, gettext("%s: audit_control " "\"plugin:\" missing name\n"), progname); state = 0; /* is_not_ok */ } else outputs++; _kva_free(kvlist); } else if (rc < -1) { (void) fprintf(stderr, gettext("%s: audit_control \"plugin:\" spec invalid\n"), progname); state = 0; /* is_not_ok */ } if (outputs == 0) { (void) fprintf(stderr, gettext("%s: audit_control must have either a " "\"dir:\" or a \"plugin:\" specified.\n"), progname); state = 0; /* is_not_ok */ } /* minfree is not required */ _rewindac(ach); if ((rc = _getacmin(ach, &min)) < -1) { (void) fprintf(stderr, gettext( "%s: audit_control \"minfree:\" spec invalid\n"), progname); state = 0; /* is_not_ok */ } /* flags is not required */ _rewindac(ach); if ((rc = _getacflg(ach, buf, TRADITIONAL_MAX)) < -1) { (void) fprintf(stderr, gettext("%s: audit_control \"flags:\" spec invalid\n"), progname); state = 0; /* is_not_ok */ } /* naflags is not required */ _rewindac(ach); if ((rc = _getacna(ach, buf, TRADITIONAL_MAX)) < -1) { (void) fprintf(stderr, gettext( "%s: audit_control \"naflags:\" spec invalid\n"), progname); state = 0; /* is_not_ok */ } _endac(ach); return (state); } /* * The operations that call this function are only valid in the global * zone unless the perzone audit policy is set. * * "!silent" and "show_err" are slightly different; silent is from * -T for which no error messages should be displayed and show_err * applies to more options (including -T) * */ static boolean_t is_valid_zone(boolean_t show_err) { long policy; if (auditon(A_GETPOLICY, (char *)&policy, 0) == -1) { if (!silent) (void) fprintf(stderr, gettext( "%s: Cannot read audit policy: %s\n"), progname, strerror(errno)); return (0); } if (policy & AUDIT_PERZONE) return (1); if (getzoneid() != GLOBAL_ZONEID) { if (show_err) (void) fprintf(stderr, gettext("%s: Not valid in a local zone.\n"), progname); return (0); } else { return (1); } } /* * if auditd isn't running, start it. Otherwise refresh. * First check to see if c2audit is loaded via the auditon() * system call, then check SMF state. */ static void start_auditd() { int audit_state; char *state; if (auditon(A_GETCOND, (caddr_t)&audit_state, sizeof (audit_state)) != 0) return; if ((state = smf_get_state(instance_name)) == NULL) { display_smf_error(); return; } if (strcmp(SCF_STATE_STRING_ONLINE, state) != 0) { if (smf_enable_instance(instance_name, 0) != 0) display_smf_error(); } else { if (smf_refresh_instance(instance_name) != 0) display_smf_error(); } free(state); } static void display_smf_error() { int rc = scf_error(); switch (rc) { case SCF_ERROR_NOT_FOUND: (void) fprintf(stderr, "SMF error: \"%s\" not found.\n", instance_name); break; default: (void) fprintf(stderr, "SMF error %d\n", rc); break; } }