uadmin.c revision 753a6d457b330b1b29b2d3eefcd0831116ce950d
/*
* 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;
#ifdef __i386
uint8_t boot_config = 0;
#endif /* __i386 */
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) {
switch (fcn) {
case AD_UPDATE_BOOT_CONFIG:
fcn_id = ADT_UADMIN_FCN_AD_UPDATE_BOOT_CONFIG;
scf_get_boot_config(&boot_config);
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);
}
}