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