xref: /illumos-gate/usr/src/cmd/audit/audit.c (revision d67944fbe3fa0b31893a7116a09b0718eecf6078)
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