audit.c revision 12160:f00ea86e54a6
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/*
23 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
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 */
51static char	*progname = "audit";
52static char	*usage = "audit [-n] | [-s] | [-t] | [-v filepath]";
53static int	silent = 0;
54
55static void	display_smf_error();
56
57static boolean_t is_audit_control_ok(char *);	/* file validation  */
58static boolean_t is_valid_zone(boolean_t);	/* operation ok in this zone? */
59static int	start_auditd();			/* start audit daemon */
60static 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 temporarily disable auditing reporting
76 *		  no errors.
77 *	audit -v filepath
78 *		- validate audit_control parameters but use filepath for
79 *		  the name.  Emit errors or "syntax ok"
80 *
81 *
82 * output:
83 *
84 * returns:	0 - command successful
85 *		>0 - command failed
86 */
87
88int
89main(int argc, char *argv[])
90{
91	char	c;
92	char	*first_option;
93
94	/* Internationalization */
95	(void) setlocale(LC_ALL, "");
96	(void) textdomain(TEXT_DOMAIN);
97
98	/* first option required */
99	if ((c = getopt(argc, argv, "nstTv:")) == -1) {
100		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
101		exit(3);
102	}
103	first_option = optarg;
104	/* second or more options not allowed; please pick one */
105	if (getopt(argc, argv, "nstTv:") != -1) {
106		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
107		exit(5);
108	}
109	switch (c) {
110	case 'n':
111		if (!is_valid_zone(1))	/* 1 == display error if any */
112			exit(10);
113
114		if (sig_auditd(AU_SIG_NEXT_DIR) != 0)
115			exit(1);
116		break;
117	case 's':
118		if (!is_valid_zone(1))	/* 1 == display error if any */
119			exit(10);
120		else if (!is_audit_control_ok(NULL))
121			exit(7);
122
123		return (start_auditd());
124	case 't':
125		if (!is_valid_zone(0))	/* 0 == no error message display */
126			exit(10);
127		if (smf_disable_instance(AUDITD_FMRI, 0) != 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
166static int
167sig_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
202static boolean_t
203is_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
312static boolean_t
313is_valid_zone(boolean_t show_err)
314{
315	uint32_t	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		}
323		return (0);
324	}
325	if (policy & AUDIT_PERZONE)
326		return (1);
327
328	if (getzoneid() != GLOBAL_ZONEID) {
329		if (show_err)
330			(void) fprintf(stderr,
331			    gettext("%s: Not valid in a local zone.\n"),
332			    progname);
333		return (0);
334	} else {
335		return (1);
336	}
337}
338
339/*
340 * if auditd isn't running, start it.  Otherwise refresh.
341 * First check to see if c2audit is loaded via the auditon()
342 * system call, then check SMF state.
343 */
344static int
345start_auditd()
346{
347	int	audit_state;
348	char	*state;
349
350	if (auditon(A_GETCOND, (caddr_t)&audit_state,
351	    sizeof (audit_state)) != 0)
352		return (12);
353
354	if ((state = smf_get_state(AUDITD_FMRI)) == NULL) {
355		display_smf_error();
356		return (13);
357	}
358	if (strcmp(SCF_STATE_STRING_ONLINE, state) != 0) {
359		if (smf_enable_instance(AUDITD_FMRI, 0) != 0) {
360			display_smf_error();
361			free(state);
362			return (14);
363		}
364	} else {
365		if (smf_refresh_instance(AUDITD_FMRI) != 0) {
366			display_smf_error();
367			free(state);
368			return (15);
369		}
370	}
371	free(state);
372	return (0);
373}
374
375static void
376display_smf_error()
377{
378	scf_error_t	rc = scf_error();
379
380	switch (rc) {
381	case SCF_ERROR_NOT_FOUND:
382		(void) fprintf(stderr,
383		    "SMF error: \"%s\" not found.\n",
384		    AUDITD_FMRI);
385		break;
386	default:
387		(void) fprintf(stderr, "SMF error: %s\n", scf_strerror(rc));
388		break;
389	}
390}
391