Home | History | Annotate | Download | only in audit
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <fcntl.h>
     27 #include <libscf.h>
     28 #include <secdb.h>
     29 #include <stdlib.h>
     30 #include <stdio.h>
     31 #include <string.h>
     32 #include <sys/file.h>
     33 #include <sys/types.h>
     34 #include <sys/wait.h>
     35 #include <signal.h>
     36 #include <sys/param.h>
     37 #include <unistd.h>
     38 #include <bsm/audit.h>
     39 #include <bsm/libbsm.h>
     40 #include <locale.h>
     41 #include <audit_sig_infc.h>
     42 #include <zone.h>
     43 
     44 #if !defined(TEXT_DOMAIN)
     45 #define	TEXT_DOMAIN "SUNW_OST_OSCMD"
     46 #endif
     47 
     48 #define	VERIFY -1
     49 
     50 /* GLOBALS */
     51 static char	*progname = "audit";
     52 static char	*usage = "audit [-n] | [-s] | [-t] | [-v filepath]";
     53 static int	silent = 0;
     54 
     55 static void	display_smf_error();
     56 
     57 static boolean_t is_audit_control_ok(char *);	/* file validation  */
     58 static boolean_t is_valid_zone(boolean_t);	/* operation ok in this zone? */
     59 static int	start_auditd();			/* start audit daemon */
     60 static int	sig_auditd(int);		/* send signal to auditd */
     61 
     62 /*
     63  * audit() - This program serves as a general administrator's interface to
     64  *	the audit trail.  Only one option is valid at a time.
     65  *
     66  * input:
     67  *	audit -s
     68  *		- signal audit daemon to read audit_control file and
     69  *		  start auditd if needed.
     70  *	audit -n
     71  *		- signal audit daemon to use next audit_control audit directory.
     72  *	audit -t
     73  *		- signal audit daemon to disable auditing.
     74  *	audit -T
     75  *		- signal audit daemon to disable auditing report no errors.
     76  *	audit -v filepath
     77  *		- validate audit_control parameters but use filepath for
     78  *		  the name.  Emit errors or "syntax ok"
     79  *
     80  *
     81  * output:
     82  *
     83  * returns:	0 - command successful
     84  *		>0 - command failed
     85  */
     86 
     87 int
     88 main(int argc, char *argv[])
     89 {
     90 	char	c;
     91 	char	*first_option;
     92 
     93 	/* Internationalization */
     94 	(void) setlocale(LC_ALL, "");
     95 	(void) textdomain(TEXT_DOMAIN);
     96 
     97 	/* first option required */
     98 	if ((c = getopt(argc, argv, "nstTv:")) == -1) {
     99 		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
    100 		exit(3);
    101 	}
    102 	first_option = optarg;
    103 	/* second or more options not allowed; please pick one */
    104 	if (getopt(argc, argv, "nstTv:") != -1) {
    105 		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
    106 		exit(5);
    107 	}
    108 	switch (c) {
    109 	case 'n':
    110 		if (!is_valid_zone(1))	/* 1 == display error if any */
    111 			exit(10);
    112 
    113 		if (sig_auditd(AU_SIG_NEXT_DIR) != 0)
    114 			exit(1);
    115 		break;
    116 	case 's':
    117 		if (!is_valid_zone(1))	/* 1 == display error if any */
    118 			exit(10);
    119 		else if (!is_audit_control_ok(NULL))
    120 			exit(7);
    121 
    122 		return (start_auditd());
    123 	case 't':
    124 		if (!is_valid_zone(0))	/* 0 == no error message display */
    125 			exit(10);
    126 		/* use bmsunconv to permanently disable, -t for temporary */
    127 		if (smf_disable_instance(AUDITD_FMRI, SMF_TEMPORARY) != 0) {
    128 			display_smf_error();
    129 			exit(11);
    130 		}
    131 		break;
    132 	case 'T':
    133 		silent = 1;
    134 		if (!is_valid_zone(0))	/* 0 == no error message display */
    135 			exit(10);
    136 
    137 		if (smf_disable_instance(AUDITD_FMRI, SMF_TEMPORARY) != 0) {
    138 			exit(11);
    139 		}
    140 		break;
    141 	case 'v':
    142 		if (is_audit_control_ok(first_option)) {
    143 			(void) fprintf(stderr, gettext("syntax ok\n"));
    144 			exit(0);
    145 		} else {
    146 			exit(8);
    147 		}
    148 		break;
    149 	default:
    150 		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
    151 		exit(6);
    152 	}
    153 
    154 	return (0);
    155 }
    156 
    157 /*
    158  * sig_auditd(sig)
    159  *
    160  * send a signal to auditd service
    161  *
    162  * returns:	0 - successful
    163  *		1 - error
    164  */
    165 
    166 static int
    167 sig_auditd(int sig)
    168 {
    169 	scf_simple_prop_t *prop = NULL;
    170 	uint64_t	*cid = NULL;
    171 
    172 	if ((prop = scf_simple_prop_get(NULL, AUDITD_FMRI, SCF_PG_RESTARTER,
    173 	    SCF_PROPERTY_CONTRACT)) == NULL) {
    174 		display_smf_error();
    175 		return (1);
    176 	}
    177 	if ((scf_simple_prop_numvalues(prop) < 0) ||
    178 	    (cid = scf_simple_prop_next_count(prop)) == NULL) {
    179 		scf_simple_prop_free(prop);
    180 		display_smf_error();
    181 		return (1);
    182 	}
    183 	if (sigsend(P_CTID, (ctid_t)*cid, sig) != 0) {
    184 		perror("audit: can't signal auditd");
    185 		scf_simple_prop_free(prop);
    186 		return (1);
    187 	}
    188 	scf_simple_prop_free(prop);
    189 	return (0);
    190 }
    191 
    192 /*
    193  * perform reasonableness check on audit_control or its standin; goal
    194  * is that "audit -s" (1) not crash the system and (2) c2audit/auditd
    195  * actually generates data.
    196  *
    197  * A NULL input is ok -- it is used to tell _openac() to use the
    198  * real audit_control file, not a substitute.
    199  */
    200 #define	TRADITIONAL_MAX	1024
    201 
    202 static boolean_t
    203 is_audit_control_ok(char *filename) {
    204 	char		buf[TRADITIONAL_MAX];
    205 	int		outputs = 0;
    206 	int		state = 1;	/* 1 is ok, 0 is not */
    207 	int		rc;
    208 	int		min;
    209 	kva_t		*kvlist;
    210 	char		*plugin_name;
    211 	char		*plugin_dir;
    212 	au_acinfo_t	*ach;
    213 
    214 	ach = _openac(filename);	/* open audit_control */
    215 	if (ach == NULL) {
    216 		perror(progname);
    217 		exit(9);
    218 	}
    219 	/*
    220 	 * There must be at least one directory or one plugin
    221 	 * defined.
    222 	 */
    223 	if ((rc = _getacdir(ach, buf, TRADITIONAL_MAX)) == 0) {
    224 		outputs++;
    225 	} else if (rc < -1) {	/* -1 is not found, others are errors */
    226 		(void) fprintf(stderr,
    227 			gettext("%s: audit_control \"dir:\" spec invalid\n"),
    228 				progname);
    229 		state = 0;	/* is_not_ok */
    230 	}
    231 
    232 	/*
    233 	 * _getacplug -- all that is of interest is the return code.
    234 	 */
    235 	_rewindac(ach);	/* rewind audit_control */
    236 	while ((rc = _getacplug(ach, &kvlist)) == 0) {
    237 		plugin_name = kva_match(kvlist, "name");
    238 		if (plugin_name == NULL) {
    239 			(void) fprintf(stderr, gettext("%s: audit_control "
    240 			    "\"plugin:\" missing name\n"), progname);
    241 			state = 0;	/* is_not_ok */
    242 		} else {
    243 			if (strcmp(plugin_name, "audit_binfile.so") == 0) {
    244 				plugin_dir = kva_match(kvlist, "p_dir");
    245 				if ((plugin_dir == NULL) && (outputs == 0)) {
    246 					(void) fprintf(stderr,
    247 					    gettext("%s: audit_control "
    248 					    "\"plugin:\" missing p_dir\n"),
    249 					    progname);
    250 					state = 0;	/* is_not_ok */
    251 				} else {
    252 					outputs++;
    253 				}
    254 			}
    255 		}
    256 		_kva_free(kvlist);
    257 	}
    258 	if (rc < -1) {
    259 		(void) fprintf(stderr,
    260 			gettext("%s: audit_control \"plugin:\" spec invalid\n"),
    261 				progname);
    262 		state = 0;	/* is_not_ok */
    263 	}
    264 	if (outputs == 0) {
    265 		(void) fprintf(stderr,
    266 			gettext("%s: audit_control must have either a "
    267 				"valid \"dir:\" entry or a valid \"plugin:\" "
    268 				"entry with \"p_dir:\" specified.\n"),
    269 				progname);
    270 		state = 0;	/* is_not_ok */
    271 	}
    272 	/* minfree is not required */
    273 	_rewindac(ach);
    274 	if ((rc = _getacmin(ach, &min)) < -1) {
    275 		(void) fprintf(stderr,
    276 			gettext(
    277 			    "%s: audit_control \"minfree:\" spec invalid\n"),
    278 			    progname);
    279 		state = 0;	/* is_not_ok */
    280 	}
    281 	/* flags is not required */
    282 	_rewindac(ach);
    283 	if ((rc = _getacflg(ach, buf, TRADITIONAL_MAX)) < -1) {
    284 		(void) fprintf(stderr,
    285 			gettext("%s: audit_control \"flags:\" spec invalid\n"),
    286 				progname);
    287 		state = 0;	/* is_not_ok */
    288 	}
    289 	/* naflags is not required */
    290 	_rewindac(ach);
    291 	if ((rc = _getacna(ach, buf, TRADITIONAL_MAX)) < -1) {
    292 		(void) fprintf(stderr,
    293 			gettext(
    294 			    "%s: audit_control \"naflags:\" spec invalid\n"),
    295 			    progname);
    296 		state = 0;	/* is_not_ok */
    297 	}
    298 	_endac(ach);
    299 	return (state);
    300 }
    301 
    302 /*
    303  * The operations that call this function are only valid in the global
    304  * zone unless the perzone audit policy is set.
    305  *
    306  * "!silent" and "show_err" are slightly different; silent is from
    307  * -T for which no error messages should be displayed and show_err
    308  * applies to more options (including -T)
    309  *
    310  */
    311 
    312 static boolean_t
    313 is_valid_zone(boolean_t show_err)
    314 {
    315 	long	policy;
    316 
    317 	if (auditon(A_GETPOLICY, (char *)&policy, 0) == -1) {
    318 		if (!silent)
    319 			(void) fprintf(stderr, gettext(
    320 			    "%s: Cannot read audit policy:  %s\n"),
    321 			    progname, strerror(errno));
    322 		return (0);
    323 	}
    324 	if (policy & AUDIT_PERZONE)
    325 		return (1);
    326 
    327 	if (getzoneid() != GLOBAL_ZONEID) {
    328 		if (show_err)
    329 			(void) fprintf(stderr,
    330 			    gettext("%s: Not valid in a local zone.\n"),
    331 			    progname);
    332 		return (0);
    333 	} else {
    334 		return (1);
    335 	}
    336 }
    337 
    338 /*
    339  * if auditd isn't running, start it.  Otherwise refresh.
    340  * First check to see if c2audit is loaded via the auditon()
    341  * system call, then check SMF state.
    342  */
    343 static int
    344 start_auditd()
    345 {
    346 	int	audit_state;
    347 	char	*state;
    348 
    349 	if (auditon(A_GETCOND, (caddr_t)&audit_state,
    350 	    sizeof (audit_state)) != 0)
    351 		return (12);
    352 
    353 	if ((state = smf_get_state(AUDITD_FMRI)) == NULL) {
    354 		display_smf_error();
    355 		return (13);
    356 	}
    357 	if (strcmp(SCF_STATE_STRING_ONLINE, state) != 0) {
    358 		if (smf_enable_instance(AUDITD_FMRI, 0) != 0) {
    359 			display_smf_error();
    360 			free(state);
    361 			return (14);
    362 		}
    363 	} else {
    364 		if (smf_refresh_instance(AUDITD_FMRI) != 0) {
    365 			display_smf_error();
    366 			free(state);
    367 			return (15);
    368 		}
    369 	}
    370 	free(state);
    371 	return (0);
    372 }
    373 
    374 static void
    375 display_smf_error()
    376 {
    377 	scf_error_t	rc = scf_error();
    378 
    379 	switch (rc) {
    380 	case SCF_ERROR_NOT_FOUND:
    381 		(void) fprintf(stderr,
    382 		    "SMF error: \"%s\" not found.\n",
    383 		    AUDITD_FMRI);
    384 		break;
    385 	default:
    386 		(void) fprintf(stderr, "SMF error: %s\n", scf_strerror(rc));
    387 		break;
    388 	}
    389 }
    390