2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1992, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * au_preselect.c
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <bsm/audit.h>
2N/A#include <bsm/audit_kevents.h>
2N/A#include <bsm/libbsm.h>
2N/A#include <bsm/audit_kevents.h>
2N/A#include <synch.h>
2N/A
2N/A#define ALLOC_INIT (600) /* initially allocate ALLOC_INIT map entries */
2N/A#define ALLOC_INCR (100) /* if more map entries are needed, realloc */
2N/A /* in ALLOC_INCR increments */
2N/A
2N/Astatic int alloc_map();
2N/Astatic int load_map();
2N/Astatic int realloc_map();
2N/A
2N/Atypedef struct event_map {
2N/A au_event_t event; /* audit event number */
2N/A au_class_t class; /* audit event class mask */
2N/A} event_map_t;
2N/A
2N/Astatic event_map_t *event_map; /* the map */
2N/Astatic uint_t alloc_count; /* number of entries currently allocated */
2N/Astatic uint_t event_count; /* number of entries in map */
2N/Astatic mutex_t mutex_au_preselect = DEFAULTMUTEX;
2N/Astatic au_class_t frcp_class = AU_MASK_NONE; /* forced class */
2N/A
2N/A/*
2N/A * au_preselect:
2N/A *
2N/A * Keep a dynamic array of event<-->class mappings.
2N/A * Refresh the map when the value of flag is AU_PRS_REREAD.
2N/A * Return:
2N/A * 0: event is not preselected.
2N/A * 1: event is normal preselected.
2N/A * 2: event is forced preselected.
2N/A * -1: There was an error:
2N/A * Couldn't allocate memory.
2N/A * Couldn't find event.
2N/A */
2N/Aint
2N/Aau_preselect(au_event_t au_event, au_mask_t *au_mask_p, int sorf, int flag)
2N/A{
2N/A static boolean_t been_here_before = B_FALSE; /* we cache the map */
2N/A int i;
2N/A au_class_t comp_class;
2N/A
2N/A (void) mutex_lock(&mutex_au_preselect);
2N/A if (!been_here_before) {
2N/A if (alloc_map() == -1) {
2N/A (void) mutex_unlock(&mutex_au_preselect);
2N/A return (-1);
2N/A }
2N/A
2N/A if (load_map() == -1) {
2N/A (void) mutex_unlock(&mutex_au_preselect);
2N/A return (-1);
2N/A }
2N/A
2N/A been_here_before = B_TRUE;
2N/A /*
2N/A * We have just loaded the map. Disable reloading for now, no
2N/A * matter if 'flag' is AU_PRS_REREAD or not.
2N/A */
2N/A flag = AU_PRS_USECACHE;
2N/A }
2N/A
2N/A /*
2N/A * Don't use the cache. Re-read the audit_event(4) db.
2N/A */
2N/A if (flag == AU_PRS_REREAD) {
2N/A if (load_map() == -1) {
2N/A (void) mutex_unlock(&mutex_au_preselect);
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A /* Determine what portion of the preselection mask to check. */
2N/A if (sorf == AU_PRS_SUCCESS) {
2N/A comp_class = au_mask_p->am_success;
2N/A } else if (sorf == AU_PRS_FAILURE) {
2N/A comp_class = au_mask_p->am_failure;
2N/A } else {
2N/A comp_class = au_mask_p->am_success | au_mask_p->am_failure;
2N/A }
2N/A
2N/A for (i = 0; i < event_count; i++) {
2N/A if (event_map[i].event == au_event) {
2N/A if (event_map[i].class & comp_class) {
2N/A (void) mutex_unlock(&mutex_au_preselect);
2N/A return (1);
2N/A } else if (event_map[i].class & frcp_class) {
2N/A /* force this event */
2N/A (void) mutex_unlock(&mutex_au_preselect);
2N/A return (2);
2N/A }
2N/A (void) mutex_unlock(&mutex_au_preselect);
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A (void) mutex_unlock(&mutex_au_preselect);
2N/A return (-1); /* could not find event in the table */
2N/A}
2N/A
2N/A/*
2N/A * Initially allocate about as many map entries as are there
2N/A * are audit events shipped with the system. For sites
2N/A * that don't add audit events, this should be enough.
2N/A */
2N/Astatic int
2N/Aalloc_map()
2N/A{
2N/A if ((event_map = (event_map_t *)
2N/A calloc(ALLOC_INIT, (size_t)sizeof (event_map_t))) == NULL)
2N/A return (-1);
2N/A else
2N/A alloc_count = ALLOC_INIT;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * load the event<->class map into memory
2N/A */
2N/Astatic int
2N/Aload_map()
2N/A{
2N/A register au_event_ent_t *evp;
2N/A static struct stat evf_timestamp;
2N/A static struct stat clf_timestamp;
2N/A struct stat st = {0};
2N/A
2N/A /*
2N/A * Check if audit_class(4) has changed since the last call. If
2N/A * so, refresh the forced preselection class and the event to
2N/A * class mappings even if audit_event(4) hasn't changed.
2N/A */
2N/A
2N/A if (stat(AUDITCLASSFILE, &st) == 0) {
2N/A if (clf_timestamp.st_mtim.tv_sec != st.st_mtim.tv_sec ||
2N/A clf_timestamp.st_mtim.tv_nsec != st.st_mtim.tv_nsec) {
2N/A au_class_ent_t frcp;
2N/A char name[AU_CLASS_NAME_MAX];
2N/A char desc[AU_CLASS_DESC_MAX];
2N/A
2N/A /* get the "frcp" class definition */
2N/A frcp.ac_name = name;
2N/A frcp.ac_desc = desc;
2N/A if (getauclassnam_r(&frcp, "frcp") != NULL) {
2N/A frcp_class = frcp.ac_class;
2N/A } else {
2N/A frcp_class = AU_MASK_NONE;
2N/A }
2N/A clf_timestamp = st;
2N/A
2N/A evf_timestamp.st_mtim.tv_sec = 0;
2N/A evf_timestamp.st_mtim.tv_nsec = 0;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Check if audit_event(4) has changed since the last call. If not, do
2N/A * nothing as the map is up to date and there is no reason to refresh
2N/A * it. When an error occurs, we are in a "don't know" state, st remains
2N/A * zeroed and the map will be again reloaded next time we call
2N/A * load_map(), just in case.
2N/A */
2N/A if (stat(AUDITEVENTFILE, &st) == 0) {
2N/A if (evf_timestamp.st_mtim.tv_sec == st.st_mtim.tv_sec &&
2N/A evf_timestamp.st_mtim.tv_nsec == st.st_mtim.tv_nsec) {
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A /* Load the map */
2N/A event_count = 0;
2N/A setauevent();
2N/A while ((evp = getauevent()) != NULL) {
2N/A /*
2N/A * Skip kernel audit events (event numbers 0-2047 are reserved
2N/A * for them, actual number in use is MAX_KEVENTS) since they
2N/A * aren't needed here for userland event lookups.
2N/A */
2N/A if (evp->ae_number > MAX_KEVENTS) {
2N/A if (event_count >= alloc_count) {
2N/A if (realloc_map() == -1) {
2N/A endauevent();
2N/A return (-1);
2N/A }
2N/A }
2N/A event_map[event_count].event = evp->ae_number;
2N/A event_map[event_count].class = evp->ae_class;
2N/A ++event_count;
2N/A }
2N/A }
2N/A endauevent();
2N/A
2N/A /* Update the timestamp to avoid unnecessary reloads. */
2N/A evf_timestamp = st;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * realloc the event map in ALLOC_INCR increments
2N/A */
2N/Astatic int
2N/Arealloc_map()
2N/A{
2N/A register size_t rsize;
2N/A rsize = sizeof (event_map_t) * (alloc_count + ALLOC_INCR);
2N/A
2N/A if ((event_map = (event_map_t *)realloc(event_map, rsize)) == NULL)
2N/A return (-1);
2N/A
2N/A alloc_count += ALLOC_INCR;
2N/A
2N/A return (0);
2N/A}