xref: /illumos-gate/usr/src/cmd/audit/audit.c (revision 5e989a96186a37eb528fb7bb4d28a150874ec799)
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 */
52 static char	*progname = "audit";
53 static char	*usage = "audit [-n] | [-s] | [-t] | [-v]";
54 static int	silent = 0;
55 
56 static void	display_smf_error();
57 
58 static boolean_t is_audit_config_ok();		/* config validation  */
59 static boolean_t is_valid_zone(boolean_t);	/* operation ok in this zone? */
60 static boolean_t contains_valid_dirs(char *);	/* p_dir contents validation */
61 static boolean_t validate_path(char *);		/* is it path to dir? */
62 static void	start_auditd();			/* start audit daemon */
63 static 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 
91 int
92 main(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 
169 static int
170 sig_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 
199 static boolean_t
200 is_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 
282 static boolean_t
283 is_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 
315 static boolean_t
316 contains_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 
354 static boolean_t
355 validate_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  */
382 static void
383 start_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 
412 static void
413 display_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