xref: /illumos-gate/usr/src/cmd/audit/audit.c (revision 6d02032db7b674f185405d42cc8bf10a46a9ab3a)
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 2010 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 
54 static void	display_smf_error();
55 
56 static boolean_t is_audit_control_ok(char *);	/* file validation  */
57 static boolean_t is_valid_zone(boolean_t);	/* operation ok in this zone? */
58 static int	start_auditd();			/* start audit daemon */
59 static int	sig_auditd(int);		/* send signal to auditd */
60 
61 /*
62  * audit() - This program serves as a general administrator's interface to
63  *	the audit trail.  Only one option is valid at a time.
64  *
65  * input:
66  *	audit -s
67  *		- signal audit daemon to read audit_control file and
68  *		  start auditd if needed.
69  *	audit -n
70  *		- signal audit daemon to use next audit_control audit directory.
71  *	audit -t
72  *		- signal audit daemon to permanently disable auditing.
73  *	audit -v filepath
74  *		- validate audit_control parameters but use filepath for
75  *		  the name.  Emit errors or "syntax ok"
76  *
77  *
78  * output:
79  *
80  * returns:	0 - command successful
81  *		>0 - command failed
82  */
83 
84 int
85 main(int argc, char *argv[])
86 {
87 	char	c;
88 	char	*first_option;
89 
90 	/* Internationalization */
91 	(void) setlocale(LC_ALL, "");
92 	(void) textdomain(TEXT_DOMAIN);
93 
94 	/* first option required */
95 	if ((c = getopt(argc, argv, "nstv:")) == -1) {
96 		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
97 		exit(3);
98 	}
99 	first_option = optarg;
100 	/* second or more options not allowed; please pick one */
101 	if (getopt(argc, argv, "nstv:") != -1) {
102 		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
103 		exit(5);
104 	}
105 	switch (c) {
106 	case 'n':
107 		if (!is_valid_zone(1))	/* 1 == display error if any */
108 			exit(10);
109 
110 		if (sig_auditd(AU_SIG_NEXT_DIR) != 0)
111 			exit(1);
112 		break;
113 	case 's':
114 		if (!is_valid_zone(1))	/* 1 == display error if any */
115 			exit(10);
116 		else if (!is_audit_control_ok(NULL))
117 			exit(7);
118 
119 		return (start_auditd());
120 	case 't':
121 		if (!is_valid_zone(0))	/* 0 == no error message display */
122 			exit(10);
123 		if (smf_disable_instance(AUDITD_FMRI, 0) != 0) {
124 			display_smf_error();
125 			exit(11);
126 		}
127 		break;
128 	case 'v':
129 		if (is_audit_control_ok(first_option)) {
130 			(void) fprintf(stderr, gettext("syntax ok\n"));
131 			exit(0);
132 		} else {
133 			exit(8);
134 		}
135 		break;
136 	default:
137 		(void) fprintf(stderr, gettext("usage: %s\n"), usage);
138 		exit(6);
139 	}
140 
141 	return (0);
142 }
143 
144 /*
145  * sig_auditd(sig)
146  *
147  * send a signal to auditd service
148  *
149  * returns:	0 - successful
150  *		1 - error
151  */
152 
153 static int
154 sig_auditd(int sig)
155 {
156 	scf_simple_prop_t *prop = NULL;
157 	uint64_t	*cid = NULL;
158 
159 	if ((prop = scf_simple_prop_get(NULL, AUDITD_FMRI, SCF_PG_RESTARTER,
160 	    SCF_PROPERTY_CONTRACT)) == NULL) {
161 		display_smf_error();
162 		return (1);
163 	}
164 	if ((scf_simple_prop_numvalues(prop) < 0) ||
165 	    (cid = scf_simple_prop_next_count(prop)) == NULL) {
166 		scf_simple_prop_free(prop);
167 		display_smf_error();
168 		return (1);
169 	}
170 	if (sigsend(P_CTID, (ctid_t)*cid, sig) != 0) {
171 		perror("audit: can't signal auditd");
172 		scf_simple_prop_free(prop);
173 		return (1);
174 	}
175 	scf_simple_prop_free(prop);
176 	return (0);
177 }
178 
179 /*
180  * perform reasonableness check on audit_control or its standin; goal
181  * is that "audit -s" (1) not crash the system and (2) c2audit/auditd
182  * actually generates data.
183  *
184  * A NULL input is ok -- it is used to tell _openac() to use the
185  * real audit_control file, not a substitute.
186  */
187 #define	TRADITIONAL_MAX	1024
188 
189 static boolean_t
190 is_audit_control_ok(char *filename) {
191 	char		buf[TRADITIONAL_MAX];
192 	int		outputs = 0;
193 	int		state = 1;	/* 1 is ok, 0 is not */
194 	int		rc;
195 	int		min;
196 	kva_t		*kvlist;
197 	char		*plugin_name;
198 	char		*plugin_dir;
199 	au_acinfo_t	*ach;
200 
201 	ach = _openac(filename);	/* open audit_control */
202 	if (ach == NULL) {
203 		perror(progname);
204 		exit(9);
205 	}
206 	/*
207 	 * There must be at least one directory or one plugin
208 	 * defined.
209 	 */
210 	if ((rc = _getacdir(ach, buf, TRADITIONAL_MAX)) == 0) {
211 		outputs++;
212 	} else if (rc < -1) {	/* -1 is not found, others are errors */
213 		(void) fprintf(stderr,
214 			gettext("%s: audit_control \"dir:\" spec invalid\n"),
215 				progname);
216 		state = 0;	/* is_not_ok */
217 	}
218 
219 	/*
220 	 * _getacplug -- all that is of interest is the return code.
221 	 */
222 	_rewindac(ach);	/* rewind audit_control */
223 	while ((rc = _getacplug(ach, &kvlist)) == 0) {
224 		plugin_name = kva_match(kvlist, "name");
225 		if (plugin_name == NULL) {
226 			(void) fprintf(stderr, gettext("%s: audit_control "
227 			    "\"plugin:\" missing name\n"), progname);
228 			state = 0;	/* is_not_ok */
229 		} else {
230 			if (strcmp(plugin_name, "audit_binfile.so") == 0) {
231 				plugin_dir = kva_match(kvlist, "p_dir");
232 				if ((plugin_dir == NULL) && (outputs == 0)) {
233 					(void) fprintf(stderr,
234 					    gettext("%s: audit_control "
235 					    "\"plugin:\" missing p_dir\n"),
236 					    progname);
237 					state = 0;	/* is_not_ok */
238 				} else {
239 					outputs++;
240 				}
241 			}
242 		}
243 		_kva_free(kvlist);
244 	}
245 	if (rc < -1) {
246 		(void) fprintf(stderr,
247 			gettext("%s: audit_control \"plugin:\" spec invalid\n"),
248 				progname);
249 		state = 0;	/* is_not_ok */
250 	}
251 	if (outputs == 0) {
252 		(void) fprintf(stderr,
253 			gettext("%s: audit_control must have either a "
254 				"valid \"dir:\" entry or a valid \"plugin:\" "
255 				"entry with \"p_dir:\" specified.\n"),
256 				progname);
257 		state = 0;	/* is_not_ok */
258 	}
259 	/* minfree is not required */
260 	_rewindac(ach);
261 	if ((rc = _getacmin(ach, &min)) < -1) {
262 		(void) fprintf(stderr,
263 			gettext(
264 			    "%s: audit_control \"minfree:\" spec invalid\n"),
265 			    progname);
266 		state = 0;	/* is_not_ok */
267 	}
268 	/* flags is not required */
269 	_rewindac(ach);
270 	if ((rc = _getacflg(ach, buf, TRADITIONAL_MAX)) < -1) {
271 		(void) fprintf(stderr,
272 			gettext("%s: audit_control \"flags:\" spec invalid\n"),
273 				progname);
274 		state = 0;	/* is_not_ok */
275 	}
276 	/* naflags is not required */
277 	_rewindac(ach);
278 	if ((rc = _getacna(ach, buf, TRADITIONAL_MAX)) < -1) {
279 		(void) fprintf(stderr,
280 			gettext(
281 			    "%s: audit_control \"naflags:\" spec invalid\n"),
282 			    progname);
283 		state = 0;	/* is_not_ok */
284 	}
285 	_endac(ach);
286 	return (state);
287 }
288 
289 /*
290  * The operations that call this function are only valid in the global
291  * zone unless the perzone audit policy is set.
292  */
293 
294 static boolean_t
295 is_valid_zone(boolean_t show_err)
296 {
297 	uint32_t	policy;
298 
299 	if (auditon(A_GETPOLICY, (char *)&policy, 0) == -1) {
300 		(void) fprintf(stderr, gettext(
301 		    "%s: Cannot read audit policy:  %s\n"),
302 		    progname, strerror(errno));
303 		return (0);
304 	}
305 	if (policy & AUDIT_PERZONE)
306 		return (1);
307 
308 	if (getzoneid() != GLOBAL_ZONEID) {
309 		if (show_err)
310 			(void) fprintf(stderr,
311 			    gettext("%s: Not valid in a local zone.\n"),
312 			    progname);
313 		return (0);
314 	} else {
315 		return (1);
316 	}
317 }
318 
319 /*
320  * if auditd isn't running, start it.  Otherwise refresh.
321  * First check to see if c2audit is loaded via the auditon()
322  * system call, then check SMF state.
323  */
324 static int
325 start_auditd()
326 {
327 	int	audit_state;
328 	char	*state;
329 
330 	if (auditon(A_GETCOND, (caddr_t)&audit_state,
331 	    sizeof (audit_state)) != 0)
332 		return (12);
333 
334 	if ((state = smf_get_state(AUDITD_FMRI)) == NULL) {
335 		display_smf_error();
336 		return (13);
337 	}
338 	if (strcmp(SCF_STATE_STRING_ONLINE, state) != 0) {
339 		if (smf_enable_instance(AUDITD_FMRI, 0) != 0) {
340 			display_smf_error();
341 			free(state);
342 			return (14);
343 		}
344 	} else {
345 		if (smf_refresh_instance(AUDITD_FMRI) != 0) {
346 			display_smf_error();
347 			free(state);
348 			return (15);
349 		}
350 	}
351 	free(state);
352 	return (0);
353 }
354 
355 static void
356 display_smf_error()
357 {
358 	scf_error_t	rc = scf_error();
359 
360 	switch (rc) {
361 	case SCF_ERROR_NOT_FOUND:
362 		(void) fprintf(stderr,
363 		    "SMF error: \"%s\" not found.\n",
364 		    AUDITD_FMRI);
365 		break;
366 	default:
367 		(void) fprintf(stderr, "SMF error: %s\n", scf_strerror(rc));
368 		break;
369 	}
370 }
371