xref: /illumos-gate/usr/src/cmd/uadmin/uadmin.c (revision edb348833aaacfa1176e502ad38875fd0b2717ab)
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 
52 static const char *Usage = "Usage: %s cmd fcn [mdep]\n";
53 
54 static int closeout_audit(int, int);
55 static int turnoff_auditd(void);
56 static void wait_for_auqueue();
57 static int change_audit_file(void);
58 
59 int
60 main(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 
307 static int
308 closeout_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 
356 static int
357 turnoff_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 
395 static int
396 change_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 
432 static void
433 wait_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