/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #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_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; 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; } } 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; 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: case A_REBOOT: case A_DUMP: /* system shutting down, turn off auditd */ return (turnoff_auditd()); case A_REMOUNT: case A_SWAPCTL: case A_FTRACE: /* 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); } }