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