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