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