Home | History | Annotate | Download | only in uadmin
      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