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