/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <config.h>

#ifdef HAVE_SOLARIS_AUDIT

#include <sys/types.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

#include "sudoers.h"
#include "solaris_audit.h"

static adt_session_data_t *ah;		/* audit session handle */
static adt_event_data_t	*event;		/* event to be generated */
static char		cwd[MAXPATHLEN];
static char		cmdpath[PATH_MAX];

static int
adt_sudo_common(int argc, char *argv[])
{
	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
		log_warning(SLOG_NO_STDERR, "adt_start_session");
		return -1;
	}
	if ((event = adt_alloc_event(ah, ADT_sudo)) == NULL) {
		log_warning(SLOG_NO_STDERR, "alloc_event");
		(void) adt_end_session(ah);
		return -1;
	}
	if ((event->adt_sudo.cwdpath = getcwd(cwd, sizeof(cwd))) == NULL) {
		log_warning(SLOG_NO_STDERR, _("unable to get current working directory"));
	}

	/* get the real executable name */
	if (user_cmnd != NULL) {
		if (strlcpy(cmdpath, (const char *)user_cmnd,
		    sizeof(cmdpath)) >= sizeof(cmdpath)) {
			log_warningx(SLOG_NO_STDERR,
			    _("truncated audit path user_cmnd: %s"),
			    user_cmnd);
		}
	} else {
		if (strlcpy(cmdpath, (const char *)argv[0],
		    sizeof(cmdpath)) >= sizeof(cmdpath)) {
			log_warningx(SLOG_NO_STDERR,
			    _("truncated audit path argv[0]: %s"),
			    argv[0]);
		}
	}

	event->adt_sudo.cmdpath = cmdpath;
	event->adt_sudo.argc = argc - 1;
	event->adt_sudo.argv = &argv[1];
	event->adt_sudo.envp = env_get();

	return 0;
}


/*
 * Returns 0 on success or -1 on error.
 */
int
solaris_audit_success(int argc, char *argv[])
{
	int rc = -1;

	if (adt_sudo_common(argc, argv) != 0) {
		return -1;
	}
	if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
		log_warning(SLOG_NO_STDERR, "adt_put_event(ADT_SUCCESS)");
	} else {
		rc = 0;
	}
	adt_free_event(event);
	(void) adt_end_session(ah);

	return rc;
}

/*
 * Returns 0 on success or -1 on error.
 */
int
solaris_audit_failure(int argc, char *argv[], char const *const fmt, va_list ap)
{
	int rc = -1;

	if (adt_sudo_common(argc, argv) != 0) {
		return -1;
	}
	if (vasprintf(&event->adt_sudo.errmsg, fmt, ap) == -1) {
		log_warning(SLOG_NO_STDERR,
		    _("audit_failure message too long"));
	}
	if (adt_put_event(event, ADT_FAILURE, ADT_FAIL_VALUE_PROGRAM) != 0) {
		log_warning(SLOG_NO_STDERR, "adt_put_event(ADT_FAILURE)");
	} else {
		rc = 0;
	}
	free(event->adt_sudo.errmsg);
	adt_free_event(event);
	(void) adt_end_session(ah);

	return 0;
}

#endif /* HAVE_SOLARIS_AUDIT */
