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