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