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 (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
26/*	  All Rights Reserved  	*/
27
28
29
30#include <errno.h>
31#include <fcntl.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <strings.h>
36#include <signal.h>
37#include <unistd.h>
38#ifdef	__i386
39#include <libscf_priv.h>
40#endif /* __i386 */
41
42#include <bsm/adt.h>
43#include <bsm/adt_event.h>
44
45#include <sys/types.h>
46#include <sys/uadmin.h>
47#include <sys/wait.h>
48
49#define	SMF_RST	"/etc/svc/volatile/resetting"
50#define	RETRY_COUNT 15	/* number of 1 sec retries for audit(1M) to complete */
51
52static const char *Usage = "Usage: %s cmd fcn [mdep]\n";
53
54static int closeout_audit(int, int);
55static int turnoff_auditd(void);
56static void wait_for_auqueue();
57static int change_audit_file(void);
58
59int
60main(int argc, char *argv[])
61{
62	int cmd, fcn;
63	uintptr_t mdep = NULL;
64	sigset_t set;
65	adt_session_data_t *ah;  /* audit session handle */
66	adt_event_data_t *event = NULL; /* event to be generated */
67	au_event_t event_id;
68	enum adt_uadmin_fcn fcn_id;
69
70	if (argc < 3 || argc > 4) {
71		(void) fprintf(stderr, Usage, argv[0]);
72		return (1);
73	}
74
75	(void) sigfillset(&set);
76	(void) sigprocmask(SIG_BLOCK, &set, NULL);
77
78	cmd = atoi(argv[1]);
79	fcn = atoi(argv[2]);
80	if (argc == 4) {	/* mdep argument given */
81		if (cmd != A_REBOOT && cmd != A_SHUTDOWN && cmd != A_DUMP &&
82		    cmd != A_FREEZE) {
83			(void) fprintf(stderr, "%s: mdep argument not "
84			    "allowed for this cmd value\n", argv[0]);
85			(void) fprintf(stderr, Usage, argv[0]);
86			return (1);
87		} else {
88			mdep = (uintptr_t)argv[3];
89		}
90	}
91
92	/* set up audit session and event */
93	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
94		(void) fprintf(stderr, "%s: can't start audit session\n",
95		    argv[0]);
96	}
97	switch (cmd) {
98	case A_SHUTDOWN:
99		event_id = ADT_uadmin_shutdown;
100		break;
101	case A_REBOOT:
102		event_id = ADT_uadmin_reboot;
103		break;
104	case A_DUMP:
105		event_id = ADT_uadmin_dump;
106		break;
107	case A_REMOUNT:
108		event_id = ADT_uadmin_remount;
109		break;
110	case A_FREEZE:
111		event_id = ADT_uadmin_freeze;
112		break;
113	case A_FTRACE:
114		event_id = ADT_uadmin_ftrace;
115		break;
116	case A_CONFIG:
117		event_id = ADT_uadmin_config;
118		break;
119	case A_SWAPCTL:
120		event_id = ADT_uadmin_swapctl;
121		break;
122	default:
123		event_id = 0;
124	}
125	if ((event_id != 0) &&
126	    (event = adt_alloc_event(ah, event_id)) == NULL) {
127		(void) fprintf(stderr, "%s: can't allocate audit event\n",
128		    argv[0]);
129	}
130	switch (fcn) {
131	case AD_HALT:
132		fcn_id = ADT_UADMIN_FCN_AD_HALT;
133		break;
134	case AD_POWEROFF:
135		fcn_id = ADT_UADMIN_FCN_AD_POWEROFF;
136		break;
137	case AD_BOOT:
138		fcn_id = ADT_UADMIN_FCN_AD_BOOT;
139		break;
140	case AD_IBOOT:
141		fcn_id = ADT_UADMIN_FCN_AD_IBOOT;
142		break;
143	case AD_SBOOT:
144		fcn_id = ADT_UADMIN_FCN_AD_SBOOT;
145		break;
146	case AD_SIBOOT:
147		fcn_id = ADT_UADMIN_FCN_AD_SIBOOT;
148		break;
149	case AD_NOSYNC:
150		fcn_id = ADT_UADMIN_FCN_AD_NOSYNC;
151		break;
152	case AD_FASTREBOOT:
153#ifdef __i386
154		fcn_id = ADT_UADMIN_FCN_AD_FASTREBOOT;
155		mdep = NULL;	/* Ignore all arguments */
156#else /* __i386 */
157		fcn = AD_BOOT;
158		fcn_id = ADT_UADMIN_FCN_AD_BOOT;
159#endif /* __i386 */
160		break;
161	case AD_FASTREBOOT_DRYRUN:
162		fcn_id = ADT_UADMIN_FCN_AD_FASTREBOOT_DRYRUN;
163		mdep = NULL;	/* Ignore all arguments */
164		break;
165	default:
166		fcn_id = 0;
167	}
168	if (cmd == A_FREEZE) {
169		switch (fcn) {
170		case AD_SUSPEND_TO_DISK:
171			fcn_id = ADT_UADMIN_FCN_AD_SUSPEND_TO_DISK;
172			break;
173		case AD_CHECK_SUSPEND_TO_DISK:
174			fcn_id = ADT_UADMIN_FCN_AD_CHECK_SUSPEND_TO_DISK;
175			break;
176		case AD_FORCE:
177			fcn_id = ADT_UADMIN_FCN_AD_FORCE;
178			break;
179		case AD_SUSPEND_TO_RAM:
180			fcn_id = ADT_UADMIN_FCN_AD_SUSPEND_TO_RAM;
181			break;
182		case AD_CHECK_SUSPEND_TO_RAM:
183			fcn_id = ADT_UADMIN_FCN_AD_CHECK_SUSPEND_TO_RAM;
184			break;
185		case AD_REUSEINIT:
186			fcn_id = ADT_UADMIN_FCN_AD_REUSEINIT;
187			break;
188		case AD_REUSABLE:
189			fcn_id = ADT_UADMIN_FCN_AD_REUSABLE;
190			break;
191		case AD_REUSEFINI:
192			fcn_id = ADT_UADMIN_FCN_AD_REUSEFINI;
193			break;
194		}
195	} else if (cmd == A_FTRACE) {
196		switch (fcn) {
197		case AD_FTRACE_START:
198			fcn_id = ADT_UADMIN_FCN_AD_FTRACE_START;
199			break;
200		case AD_FTRACE_STOP:
201			fcn_id = ADT_UADMIN_FCN_AD_FTRACE_STOP;
202			break;
203		}
204#ifdef	__i386
205	} else if (cmd == A_CONFIG) {
206		uint8_t boot_config = 0;
207		uint8_t boot_config_ovr = 0;
208
209		switch (fcn) {
210		case AD_UPDATE_BOOT_CONFIG:
211			fcn_id = ADT_UADMIN_FCN_AD_UPDATE_BOOT_CONFIG;
212			scf_get_boot_config(&boot_config);
213			boot_config_ovr = boot_config;
214			scf_get_boot_config_ovr(&boot_config_ovr);
215			boot_config &= boot_config_ovr;
216			mdep = (uintptr_t)(&boot_config);
217			break;
218		}
219#endif /* __i386 */
220	}
221
222	if (geteuid() == 0) {
223		if (event != NULL) {
224			switch (cmd) {
225			case A_SHUTDOWN:
226				event->adt_uadmin_shutdown.fcn = fcn_id;
227				event->adt_uadmin_shutdown.mdep = (char *)mdep;
228				break;
229			case A_REBOOT:
230				event->adt_uadmin_reboot.fcn = fcn_id;
231				event->adt_uadmin_reboot.mdep = (char *)mdep;
232				break;
233			case A_DUMP:
234				event->adt_uadmin_dump.fcn = fcn_id;
235				event->adt_uadmin_dump.mdep = (char *)mdep;
236				break;
237			case A_REMOUNT:
238				/* no parameters */
239				break;
240			case A_FREEZE:
241				event->adt_uadmin_freeze.fcn = fcn_id;
242				event->adt_uadmin_freeze.mdep = (char *)mdep;
243				break;
244			case A_FTRACE:
245				event->adt_uadmin_ftrace.fcn = fcn_id;
246				event->adt_uadmin_ftrace.mdep = (char *)mdep;
247				break;
248			case A_CONFIG:
249				event->adt_uadmin_config.fcn = fcn_id;
250				event->adt_uadmin_config.mdep = (char *)mdep;
251				break;
252			case A_SWAPCTL:
253				event->adt_uadmin_swapctl.fcn = fcn_id;
254				break;
255			}
256
257			if (adt_put_event(event, ADT_SUCCESS, 0) != 0) {
258				(void) fprintf(stderr,
259				    "%s: can't put audit event\n", argv[0]);
260			}
261			/*
262			 * allow audit record to be processed in the kernel
263			 * audit queue
264			 */
265			wait_for_auqueue();
266		}
267
268		if (closeout_audit(cmd, fcn) == -1)
269			(void) fprintf(stderr, "%s: can't turn off auditd\n",
270			    argv[0]);
271
272		if (cmd == A_SHUTDOWN || cmd == A_REBOOT)
273			(void) creat(SMF_RST, 0777);
274	}
275
276	(void) adt_free_event(event);
277	if (uadmin(cmd, fcn, mdep) < 0) {
278		perror("uadmin");
279
280		(void) unlink(SMF_RST);
281
282		return (1);
283	}
284
285	/* If returning from a suspend, audit thaw */
286	if ((cmd == A_FREEZE) &&
287	    ((fcn == AD_FORCE) ||
288	    (fcn == AD_REUSABLE) ||
289	    (fcn == AD_SUSPEND_TO_DISK) ||
290	    (fcn == AD_SUSPEND_TO_RAM))) {
291		if ((event = adt_alloc_event(ah, ADT_uadmin_thaw)) == NULL) {
292			(void) fprintf(stderr, "%s: can't allocate thaw audit "
293			    "event\n", argv[0]);
294		}
295		event->adt_uadmin_thaw.fcn = fcn_id;
296		if (adt_put_event(event, ADT_SUCCESS, 0) != 0) {
297			(void) fprintf(stderr, "%s: can't put thaw audit "
298			    "event\n", argv[0]);
299		}
300		(void) adt_free_event(event);
301	}
302	(void) adt_end_session(ah);
303
304	return (0);
305}
306
307static int
308closeout_audit(int cmd, int fcn)
309{
310	if (!adt_audit_state(AUC_AUDITING)) {
311		/* auditd not running, just return */
312		return (0);
313	}
314	switch (cmd) {
315	case A_SHUTDOWN:
316		switch (fcn) {
317		case AD_FASTREBOOT_DRYRUN:
318			/* No system discontinuity, don't turn off auditd */
319			return (0);
320		default:
321			break;	/* For all the other shutdown functions */
322		}
323		/* FALLTHROUGH */
324	case A_REBOOT:
325	case A_DUMP:
326		/* system shutting down, turn off auditd */
327		return (turnoff_auditd());
328	case A_REMOUNT:
329	case A_SWAPCTL:
330	case A_FTRACE:
331	case A_CONFIG:
332		/* No system discontinuity, don't turn off auditd */
333		return (0);
334	case A_FREEZE:
335		switch (fcn) {
336		case AD_CHECK_SUSPEND_TO_DISK:	/* AD_CHECK */
337		case AD_CHECK_SUSPEND_TO_RAM:
338		case AD_REUSEINIT:
339		case AD_REUSEFINI:
340			/* No system discontinuity, don't turn off auditd */
341			return (0);
342		case AD_REUSABLE:
343		case AD_SUSPEND_TO_DISK:	/* AD_COMPRESS */
344		case AD_SUSPEND_TO_RAM:
345		case AD_FORCE:
346			/* suspend the system, change audit files */
347			return (change_audit_file());
348		default:
349			return (0);	/* not an audit error */
350		}
351	default:
352		return (0);	/* not an audit error */
353	}
354}
355
356static int
357turnoff_auditd(void)
358{
359	int	rc;
360	int	retries = RETRY_COUNT;
361
362	if ((rc = (int)fork()) == 0) {
363		(void) execl("/usr/sbin/audit", "audit", "-T", NULL);
364		(void) fprintf(stderr, "error disabling auditd: %s\n",
365		    strerror(errno));
366		_exit(-1);
367	} else if (rc == -1) {
368		(void) fprintf(stderr, "error disabling auditd: %s\n",
369		    strerror(errno));
370		return (-1);
371	}
372
373	/*
374	 * wait for auditd to finish its work.  auditd will change the
375	 * auditstart from AUC_AUDITING (auditd up and running) to
376	 * AUC_NOAUDIT.  Other states are errors, so we're done as well.
377	 */
378	do {
379		int	auditstate;
380
381		rc = -1;
382		if ((auditon(A_GETCOND, (caddr_t)&auditstate,
383		    sizeof (auditstate)) == 0) &&
384		    (auditstate == AUC_AUDITING)) {
385			retries--;
386			(void) sleep(1);
387		} else {
388			rc = 0;
389		}
390	} while ((rc != 0) && (retries != 0));
391
392	return (rc);
393}
394
395static int
396change_audit_file(void)
397{
398	pid_t	pid;
399
400	if ((pid = fork()) == 0) {
401		(void) execl("/usr/sbin/audit", "audit", "-n", NULL);
402		(void) fprintf(stderr, "error changing audit files: %s\n",
403		    strerror(errno));
404		_exit(-1);
405	} else if (pid == -1) {
406		(void) fprintf(stderr, "error changing audit files: %s\n",
407		    strerror(errno));
408		return (-1);
409	} else {
410		pid_t	rc;
411		int	retries = RETRY_COUNT;
412
413		/*
414		 * Wait for audit(1M) -n process to complete
415		 *
416		 */
417		do {
418			if ((rc = waitpid(pid, NULL, WNOHANG)) == pid) {
419				return (0);
420			} else if (rc == -1) {
421				return (-1);
422			} else {
423				(void) sleep(1);
424				retries--;
425			}
426
427		} while (retries != 0);
428	}
429	return (-1);
430}
431
432static void
433wait_for_auqueue()
434{
435	au_stat_t	au_stat;
436	int		retries = 10;
437
438	while (retries-- && auditon(A_GETSTAT, (caddr_t)&au_stat, NULL) == 0) {
439		if (au_stat.as_enqueue == au_stat.as_written) {
440			break;
441		}
442		(void) sleep(1);
443	}
444}
445