OpenSolaris_b135/cmd/uadmin/uadmin.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/



#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <unistd.h>
#ifdef	__i386
#include <libscf_priv.h>
#endif /* __i386 */

#include <bsm/adt.h>
#include <bsm/adt_event.h>

#include <sys/types.h>
#include <sys/uadmin.h>
#include <sys/wait.h>

#define	SMF_RST	"/etc/svc/volatile/resetting"
#define	RETRY_COUNT 15	/* number of 1 sec retries for audit(1M) to complete */

static const char *Usage = "Usage: %s cmd fcn [mdep]\n";

static int closeout_audit(int, int);
static int turnoff_auditd(void);
static void wait_for_auqueue();
static int change_audit_file(void);

int
main(int argc, char *argv[])
{
	int cmd, fcn;
	uintptr_t mdep = NULL;
	sigset_t set;
	adt_session_data_t *ah;  /* audit session handle */
	adt_event_data_t *event = NULL; /* event to be generated */
	au_event_t event_id;
	enum adt_uadmin_fcn fcn_id;

	if (argc < 3 || argc > 4) {
		(void) fprintf(stderr, Usage, argv[0]);
		return (1);
	}

	(void) sigfillset(&set);
	(void) sigprocmask(SIG_BLOCK, &set, NULL);

	cmd = atoi(argv[1]);
	fcn = atoi(argv[2]);
	if (argc == 4) {	/* mdep argument given */
		if (cmd != A_REBOOT && cmd != A_SHUTDOWN && cmd != A_DUMP &&
		    cmd != A_FREEZE) {
			(void) fprintf(stderr, "%s: mdep argument not "
			    "allowed for this cmd value\n", argv[0]);
			(void) fprintf(stderr, Usage, argv[0]);
			return (1);
		} else {
			mdep = (uintptr_t)argv[3];
		}
	}

	/* set up audit session and event */
	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
		(void) fprintf(stderr, "%s: can't start audit session\n",
		    argv[0]);
	}
	switch (cmd) {
	case A_SHUTDOWN:
		event_id = ADT_uadmin_shutdown;
		break;
	case A_REBOOT:
		event_id = ADT_uadmin_reboot;
		break;
	case A_DUMP:
		event_id = ADT_uadmin_dump;
		break;
	case A_REMOUNT:
		event_id = ADT_uadmin_remount;
		break;
	case A_FREEZE:
		event_id = ADT_uadmin_freeze;
		break;
	case A_FTRACE:
		event_id = ADT_uadmin_ftrace;
		break;
	case A_CONFIG:
		event_id = ADT_uadmin_config;
		break;
	case A_SWAPCTL:
		event_id = ADT_uadmin_swapctl;
		break;
	default:
		event_id = 0;
	}
	if ((event_id != 0) &&
	    (event = adt_alloc_event(ah, event_id)) == NULL) {
		(void) fprintf(stderr, "%s: can't allocate audit event\n",
		    argv[0]);
	}
	switch (fcn) {
	case AD_HALT:
		fcn_id = ADT_UADMIN_FCN_AD_HALT;
		break;
	case AD_POWEROFF:
		fcn_id = ADT_UADMIN_FCN_AD_POWEROFF;
		break;
	case AD_BOOT:
		fcn_id = ADT_UADMIN_FCN_AD_BOOT;
		break;
	case AD_IBOOT:
		fcn_id = ADT_UADMIN_FCN_AD_IBOOT;
		break;
	case AD_SBOOT:
		fcn_id = ADT_UADMIN_FCN_AD_SBOOT;
		break;
	case AD_SIBOOT:
		fcn_id = ADT_UADMIN_FCN_AD_SIBOOT;
		break;
	case AD_NOSYNC:
		fcn_id = ADT_UADMIN_FCN_AD_NOSYNC;
		break;
	case AD_FASTREBOOT:
#ifdef __i386
		fcn_id = ADT_UADMIN_FCN_AD_FASTREBOOT;
		mdep = NULL;	/* Ignore all arguments */
#else /* __i386 */
		fcn = AD_BOOT;
		fcn_id = ADT_UADMIN_FCN_AD_BOOT;
#endif /* __i386 */
		break;
	case AD_FASTREBOOT_DRYRUN:
		fcn_id = ADT_UADMIN_FCN_AD_FASTREBOOT_DRYRUN;
		mdep = NULL;	/* Ignore all arguments */
		break;
	default:
		fcn_id = 0;
	}
	if (cmd == A_FREEZE) {
		switch (fcn) {
		case AD_SUSPEND_TO_DISK:
			fcn_id = ADT_UADMIN_FCN_AD_SUSPEND_TO_DISK;
			break;
		case AD_CHECK_SUSPEND_TO_DISK:
			fcn_id = ADT_UADMIN_FCN_AD_CHECK_SUSPEND_TO_DISK;
			break;
		case AD_FORCE:
			fcn_id = ADT_UADMIN_FCN_AD_FORCE;
			break;
		case AD_SUSPEND_TO_RAM:
			fcn_id = ADT_UADMIN_FCN_AD_SUSPEND_TO_RAM;
			break;
		case AD_CHECK_SUSPEND_TO_RAM:
			fcn_id = ADT_UADMIN_FCN_AD_CHECK_SUSPEND_TO_RAM;
			break;
		case AD_REUSEINIT:
			fcn_id = ADT_UADMIN_FCN_AD_REUSEINIT;
			break;
		case AD_REUSABLE:
			fcn_id = ADT_UADMIN_FCN_AD_REUSABLE;
			break;
		case AD_REUSEFINI:
			fcn_id = ADT_UADMIN_FCN_AD_REUSEFINI;
			break;
		}
	} else if (cmd == A_FTRACE) {
		switch (fcn) {
		case AD_FTRACE_START:
			fcn_id = ADT_UADMIN_FCN_AD_FTRACE_START;
			break;
		case AD_FTRACE_STOP:
			fcn_id = ADT_UADMIN_FCN_AD_FTRACE_STOP;
			break;
		}
#ifdef	__i386
	} else if (cmd == A_CONFIG) {
		uint8_t boot_config = 0;
		uint8_t boot_config_ovr = 0;

		switch (fcn) {
		case AD_UPDATE_BOOT_CONFIG:
			fcn_id = ADT_UADMIN_FCN_AD_UPDATE_BOOT_CONFIG;
			scf_get_boot_config(&boot_config);
			boot_config_ovr = boot_config;
			scf_get_boot_config_ovr(&boot_config_ovr);
			boot_config &= boot_config_ovr;
			mdep = (uintptr_t)(&boot_config);
			break;
		}
#endif /* __i386 */
	}

	if (geteuid() == 0) {
		if (event != NULL) {
			switch (cmd) {
			case A_SHUTDOWN:
				event->adt_uadmin_shutdown.fcn = fcn_id;
				event->adt_uadmin_shutdown.mdep = (char *)mdep;
				break;
			case A_REBOOT:
				event->adt_uadmin_reboot.fcn = fcn_id;
				event->adt_uadmin_reboot.mdep = (char *)mdep;
				break;
			case A_DUMP:
				event->adt_uadmin_dump.fcn = fcn_id;
				event->adt_uadmin_dump.mdep = (char *)mdep;
				break;
			case A_REMOUNT:
				/* no parameters */
				break;
			case A_FREEZE:
				event->adt_uadmin_freeze.fcn = fcn_id;
				event->adt_uadmin_freeze.mdep = (char *)mdep;
				break;
			case A_FTRACE:
				event->adt_uadmin_ftrace.fcn = fcn_id;
				event->adt_uadmin_ftrace.mdep = (char *)mdep;
				break;
			case A_CONFIG:
				event->adt_uadmin_config.fcn = fcn_id;
				event->adt_uadmin_config.mdep = (char *)mdep;
				break;
			case A_SWAPCTL:
				event->adt_uadmin_swapctl.fcn = fcn_id;
				break;
			}

			if (adt_put_event(event, ADT_SUCCESS, 0) != 0) {
				(void) fprintf(stderr,
				    "%s: can't put audit event\n", argv[0]);
			}
			/*
			 * allow audit record to be processed in the kernel
			 * audit queue
			 */
			wait_for_auqueue();
		}

		if (closeout_audit(cmd, fcn) == -1)
			(void) fprintf(stderr, "%s: can't turn off auditd\n",
			    argv[0]);

		if (cmd == A_SHUTDOWN || cmd == A_REBOOT)
			(void) creat(SMF_RST, 0777);
	}

	(void) adt_free_event(event);
	if (uadmin(cmd, fcn, mdep) < 0) {
		perror("uadmin");

		(void) unlink(SMF_RST);

		return (1);
	}

	/* If returning from a suspend, audit thaw */
	if ((cmd == A_FREEZE) &&
	    ((fcn == AD_FORCE) ||
	    (fcn == AD_REUSABLE) ||
	    (fcn == AD_SUSPEND_TO_DISK) ||
	    (fcn == AD_SUSPEND_TO_RAM))) {
		if ((event = adt_alloc_event(ah, ADT_uadmin_thaw)) == NULL) {
			(void) fprintf(stderr, "%s: can't allocate thaw audit "
			    "event\n", argv[0]);
		}
		event->adt_uadmin_thaw.fcn = fcn_id;
		if (adt_put_event(event, ADT_SUCCESS, 0) != 0) {
			(void) fprintf(stderr, "%s: can't put thaw audit "
			    "event\n", argv[0]);
		}
		(void) adt_free_event(event);
	}
	(void) adt_end_session(ah);

	return (0);
}

static int
closeout_audit(int cmd, int fcn)
{
	if (!adt_audit_state(AUC_AUDITING)) {
		/* auditd not running, just return */
		return (0);
	}
	switch (cmd) {
	case A_SHUTDOWN:
		switch (fcn) {
		case AD_FASTREBOOT_DRYRUN:
			/* No system discontinuity, don't turn off auditd */
			return (0);
		default:
			break;	/* For all the other shutdown functions */
		}
		/* FALLTHROUGH */
	case A_REBOOT:
	case A_DUMP:
		/* system shutting down, turn off auditd */
		return (turnoff_auditd());
	case A_REMOUNT:
	case A_SWAPCTL:
	case A_FTRACE:
	case A_CONFIG:
		/* No system discontinuity, don't turn off auditd */
		return (0);
	case A_FREEZE:
		switch (fcn) {
		case AD_CHECK_SUSPEND_TO_DISK:	/* AD_CHECK */
		case AD_CHECK_SUSPEND_TO_RAM:
		case AD_REUSEINIT:
		case AD_REUSEFINI:
			/* No system discontinuity, don't turn off auditd */
			return (0);
		case AD_REUSABLE:
		case AD_SUSPEND_TO_DISK:	/* AD_COMPRESS */
		case AD_SUSPEND_TO_RAM:
		case AD_FORCE:
			/* suspend the system, change audit files */
			return (change_audit_file());
		default:
			return (0);	/* not an audit error */
		}
	default:
		return (0);	/* not an audit error */
	}
}

static int
turnoff_auditd(void)
{
	int	rc;
	int	retries = RETRY_COUNT;

	if ((rc = (int)fork()) == 0) {
		(void) execl("/usr/sbin/audit", "audit", "-t", NULL);
		(void) fprintf(stderr, "error disabling auditd: %s\n",
		    strerror(errno));
		_exit(-1);
	} else if (rc == -1) {
		(void) fprintf(stderr, "error disabling auditd: %s\n",
		    strerror(errno));
		return (-1);
	}

	/*
	 * wait for auditd to finish its work.  auditd will change the
	 * auditstart from AUC_AUDITING (auditd up and running) to
	 * AUC_NOAUDIT.  Other states are errors, so we're done as well.
	 */
	do {
		int	auditstate;

		rc = -1;
		if ((auditon(A_GETCOND, (caddr_t)&auditstate,
		    sizeof (auditstate)) == 0) &&
		    (auditstate == AUC_AUDITING)) {
			retries--;
			(void) sleep(1);
		} else {
			rc = 0;
		}
	} while ((rc != 0) && (retries != 0));

	return (rc);
}

static int
change_audit_file(void)
{
	pid_t	pid;

	if ((pid = fork()) == 0) {
		(void) execl("/usr/sbin/audit", "audit", "-n", NULL);
		(void) fprintf(stderr, "error changing audit files: %s\n",
		    strerror(errno));
		_exit(-1);
	} else if (pid == -1) {
		(void) fprintf(stderr, "error changing audit files: %s\n",
		    strerror(errno));
		return (-1);
	} else {
		pid_t	rc;
		int	retries = RETRY_COUNT;

		/*
		 * Wait for audit(1M) -n process to complete
		 *
		 */
		do {
			if ((rc = waitpid(pid, NULL, WNOHANG)) == pid) {
				return (0);
			} else if (rc == -1) {
				return (-1);
			} else {
				(void) sleep(1);
				retries--;
			}

		} while (retries != 0);
	}
	return (-1);
}

static void
wait_for_auqueue()
{
	au_stat_t	au_stat;
	int		retries = 10;

	while (retries-- && auditon(A_GETSTAT, (caddr_t)&au_stat, NULL) == 0) {
		if (au_stat.as_enqueue == au_stat.as_written) {
			break;
		}
		(void) sleep(1);
	}
}