audit.c revision 12918:32a41a5f8110
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/stat.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 <zone.h>
43#include <audit_scf.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	*progname = "audit";
53static char	*usage = "audit [-n] | [-s] | [-t] | [-v]";
54static int	silent = 0;
55
56static void	display_smf_error();
57
58static boolean_t is_audit_config_ok();		/* config validation  */
59static boolean_t is_valid_zone(boolean_t);	/* operation ok in this zone? */
60static boolean_t contains_valid_dirs(char *);	/* p_dir contents validation */
61static boolean_t validate_path(char *);		/* is it path to dir? */
62static void	start_auditd();			/* start audit daemon */
63static int	sig_auditd(int);		/* send signal to auditd */
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 configuration and
72 *		  start auditd if needed.
73 *	audit -n
74 *		- signal audit daemon to use next audit_binfile directory.
75 *	audit -t
76 *		- signal audit daemon to disable auditing.
77 *	audit -T
78 *		- signal audit daemon to temporarily disable auditing reporting
79 *		  no errors.
80 *	audit -v
81 *		- validate audit configuration parameters;
82 *		  Print errors or "configuration ok".
83 *
84 *
85 * output:
86 *
87 * returns:	0 - command successful
88 *		>0 - command failed
89 */
90
91int
92main(int argc, char *argv[])
93{
94	int	c;
95
96	/* Internationalization */
97	(void) setlocale(LC_ALL, "");
98	(void) textdomain(TEXT_DOMAIN);
99
100	/* second or more options not allowed; please pick one */
101	if (argc > 2) {
102		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
103		exit(1);
104	}
105
106	/* first option required */
107	if ((c = getopt(argc, argv, "nstTv")) == -1) {
108		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
109		exit(1);
110	}
111
112	switch (c) {
113	case 'n':
114		if (!is_valid_zone(1))	/* 1 == display error if any */
115			exit(1);
116
117		if (sig_auditd(SIGUSR1) != 0)
118			exit(1);
119		break;
120	case 's':
121		if (!is_valid_zone(1))	/* 1 == display error if any */
122			exit(1);
123		else if (!is_audit_config_ok())
124			exit(1);
125
126		start_auditd();
127		return (0);
128	case 't':
129		if (!is_valid_zone(0))	/* 0 == no error message display */
130			exit(1);
131		if (smf_disable_instance(AUDITD_FMRI, 0) != 0) {
132			display_smf_error();
133			exit(1);
134		}
135		break;
136	case 'T':
137		silent = 1;
138		if (!is_valid_zone(0))	/* 0 == no error message display */
139			exit(1);
140		if (smf_disable_instance(AUDITD_FMRI, SMF_TEMPORARY) != 0) {
141			exit(1);
142		}
143		break;
144	case 'v':
145		if (is_audit_config_ok()) {
146			(void) fprintf(stderr, gettext("configuration ok\n"));
147			exit(0);
148		} else {
149			exit(1);
150		}
151		break;
152	default:
153		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
154		exit(1);
155	}
156
157	return (0);
158}
159
160/*
161 * sig_auditd(sig)
162 *
163 * send a signal to auditd service
164 *
165 * returns:	0 - successful
166 *		1 - error
167 */
168
169static int
170sig_auditd(int sig)
171{
172	scf_simple_prop_t *prop = NULL;
173	uint64_t	*cid = NULL;
174
175	if ((prop = scf_simple_prop_get(NULL, AUDITD_FMRI, SCF_PG_RESTARTER,
176	    SCF_PROPERTY_CONTRACT)) == NULL) {
177		display_smf_error();
178		return (1);
179	}
180	if ((scf_simple_prop_numvalues(prop) < 0) ||
181	    (cid = scf_simple_prop_next_count(prop)) == NULL) {
182		scf_simple_prop_free(prop);
183		display_smf_error();
184		return (1);
185	}
186	if (sigsend(P_CTID, (ctid_t)*cid, sig) != 0) {
187		perror("audit: can't signal auditd");
188		scf_simple_prop_free(prop);
189		return (1);
190	}
191	scf_simple_prop_free(prop);
192	return (0);
193}
194
195/*
196 * perform reasonableness check on audit configuration
197 */
198
199static boolean_t
200is_audit_config_ok() {
201	int			state = B_TRUE;	/* B_TRUE/B_FALSE = ok/not_ok */
202	char			*cval_str;
203	int			cval_int;
204	kva_t			*kvlist;
205	scf_plugin_kva_node_t   *plugin_kva_ll;
206	scf_plugin_kva_node_t   *plugin_kva_ll_head;
207	boolean_t		one_plugin_enabled = B_FALSE;
208
209	/*
210	 * There must be at least one active plugin configured; if the
211	 * configured plugin is audit_binfile(5), then the p_dir must not be
212	 * empty.
213	 */
214	if (!do_getpluginconfig_scf(NULL, &plugin_kva_ll)) {
215		(void) fprintf(stderr,
216		    gettext("Could not get plugin configuration.\n"));
217		exit(1);
218	}
219
220	plugin_kva_ll_head = plugin_kva_ll;
221
222	while (plugin_kva_ll != NULL) {
223		kvlist = plugin_kva_ll->plugin_kva;
224
225		if (!one_plugin_enabled) {
226			cval_str = kva_match(kvlist, "active");
227			if (atoi(cval_str) == 1) {
228				one_plugin_enabled = B_TRUE;
229			}
230		}
231
232		if (strcmp((char *)&(*plugin_kva_ll).plugin_name,
233		    "audit_binfile") == 0) {
234			cval_str = kva_match(kvlist, "p_dir");
235			if (*cval_str == '\0' || cval_str == NULL) {
236				(void) fprintf(stderr,
237				    gettext("%s: audit_binfile(5) \"p_dir:\" "
238				    "attribute empty\n"), progname);
239				state = B_FALSE;
240			} else if (!contains_valid_dirs(cval_str)) {
241				(void) fprintf(stderr,
242				    gettext("%s: audit_binfile(5) \"p_dir:\" "
243				    "attribute invalid\n"), progname);
244				state = B_FALSE;
245			}
246
247			cval_str = kva_match(kvlist, "p_minfree");
248			cval_int = atoi(cval_str);
249			if (cval_int < 0 || cval_int > 100) {
250				(void) fprintf(stderr,
251				    gettext("%s: audit_binfile(5) "
252				    "\"p_minfree:\" attribute invalid\n"),
253				    progname);
254				state = B_FALSE;
255			}
256		}
257
258		plugin_kva_ll = plugin_kva_ll->next;
259	}
260
261	plugin_kva_ll_free(plugin_kva_ll_head);
262
263	if (!one_plugin_enabled) {
264		(void) fprintf(stderr, gettext("%s: no active plugin found\n"),
265		    progname);
266		state = B_FALSE;
267	}
268
269	return (state);
270}
271
272/*
273 * The operations that call this function are only valid in the global
274 * zone unless the perzone audit policy is set.
275 *
276 * "!silent" and "show_err" are slightly different; silent is from
277 * -T for which no error messages should be displayed and show_err
278 * applies to more options (including -T)
279 *
280 */
281
282static boolean_t
283is_valid_zone(boolean_t show_err)
284{
285	uint32_t	policy;
286
287	if (auditon(A_GETPOLICY, (char *)&policy, 0) == -1) {
288		if (!silent) {
289			(void) fprintf(stderr, gettext(
290			    "%s: Cannot read audit policy:  %s\n"),
291			    progname, strerror(errno));
292		}
293		return (0);
294	}
295	if (policy & AUDIT_PERZONE)
296		return (1);
297
298	if (getzoneid() != GLOBAL_ZONEID) {
299		if (show_err)
300			(void) fprintf(stderr,
301			    gettext("%s: Not valid in a local zone.\n"),
302			    progname);
303		return (0);
304	} else {
305		return (1);
306	}
307}
308
309/*
310 * Verify, whether the dirs_str contains at least one currently valid path to
311 * the directory. All invalid paths are reported. In case no valid directory
312 * path is found function returns B_FALSE, otherwise B_TRUE.
313 */
314
315static boolean_t
316contains_valid_dirs(char *dirs_str)
317{
318	boolean_t	rc = B_FALSE;
319	boolean_t	rc_validate_path = B_TRUE;
320	char		*tok_ptr;
321	char		*tok_lasts;
322
323	if (dirs_str == NULL) {
324		return (rc);
325	}
326
327	if ((tok_ptr = strtok_r(dirs_str, ",", &tok_lasts)) != NULL) {
328		if (validate_path(tok_ptr)) {
329			rc = B_TRUE;
330		} else {
331			rc_validate_path = B_FALSE;
332		}
333		while ((tok_ptr = strtok_r(NULL, ",", &tok_lasts)) != NULL) {
334			if (validate_path(tok_ptr)) {
335				rc = B_TRUE;
336			} else {
337				rc_validate_path = B_FALSE;
338			}
339		}
340	}
341
342	if (rc && !rc_validate_path) {
343		(void) fprintf(stderr, gettext("%s: at least one valid "
344		    "directory path found\n"), progname);
345	}
346
347	return (rc);
348}
349
350/*
351 * Verify, that the dir_path is path to a directory.
352 */
353
354static boolean_t
355validate_path(char *dir_path)
356{
357	boolean_t	rc = B_FALSE;
358	struct stat	statbuf;
359
360	if (dir_path == NULL) {
361		return (rc);
362	}
363
364	if (stat(dir_path, &statbuf) == -1) {
365		(void) fprintf(stderr, gettext("%s: %s error: %s\n"), progname,
366		    dir_path, strerror(errno));
367	} else if (statbuf.st_mode & S_IFDIR) {
368			rc = B_TRUE;
369	} else {
370		(void) fprintf(stderr, gettext("%s: %s is not a directory\n"),
371		    progname, dir_path);
372	}
373
374	return (rc);
375}
376
377/*
378 * if auditd isn't running, start it.  Otherwise refresh.
379 * First check to see if c2audit is loaded via the auditon()
380 * system call, then check SMF state.
381 */
382static void
383start_auditd()
384{
385	int	audit_state;
386	char	*state;
387
388	if (auditon(A_GETCOND, (caddr_t)&audit_state,
389	    sizeof (audit_state)) != 0)
390		exit(1);
391
392	if ((state = smf_get_state(AUDITD_FMRI)) == NULL) {
393		display_smf_error();
394		exit(1);
395	}
396	if (strcmp(SCF_STATE_STRING_ONLINE, state) != 0) {
397		if (smf_enable_instance(AUDITD_FMRI, 0) != 0) {
398			display_smf_error();
399			free(state);
400			exit(1);
401		}
402	} else {
403		if (smf_refresh_instance(AUDITD_FMRI) != 0) {
404			display_smf_error();
405			free(state);
406			exit(1);
407		}
408	}
409	free(state);
410}
411
412static void
413display_smf_error()
414{
415	scf_error_t	rc = scf_error();
416
417	switch (rc) {
418	case SCF_ERROR_NOT_FOUND:
419		(void) fprintf(stderr,
420		    "SMF error: \"%s\" not found.\n",
421		    AUDITD_FMRI);
422		break;
423	default:
424		(void) fprintf(stderr, "SMF error: %s\n", scf_strerror(rc));
425		break;
426	}
427}
428