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 */
56static char	*progname = "audit";
57static char	*usage = "audit [-n] | [-s] | [-t] | [-v]";
58static int	silent = 0;
59
60static void	display_smf_error();
61
62static boolean_t is_audit_config_ok();		/* config validation  */
63static boolean_t is_valid_zone(boolean_t);	/* operation ok in this zone? */
64static boolean_t contains_valid_dirs(char *);	/* p_dir contents validation */
65static boolean_t validate_path(char *);		/* is it path to dir? */
66static void	start_auditd();			/* start audit daemon */
67static 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
95int
96main(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
173static int
174sig_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
203static boolean_t
204is_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
286static boolean_t
287is_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
319static boolean_t
320contains_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
358static boolean_t
359validate_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 */
386static void
387start_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
416static void
417display_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