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, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Interfaces to audit_class(5) (/etc/security/audit_class)
2N/A */
2N/A
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <limits.h>
2N/A#include <sys/types.h>
2N/A#include <bsm/audit.h>
2N/A#include <bsm/libbsm.h>
2N/A#include <string.h>
2N/A#include <synch.h>
2N/A
2N/A/*
2N/A * Macros to produce a quoted string containing the value of a
2N/A * preprocessor macro. For example, if SIZE is defined to be 256,
2N/A * VAL2STR(SIZE) is "256". This is used to construct format
2N/A * strings for scanf-family functions below.
2N/A */
2N/A#define QUOTE(x) #x
2N/A#define VAL2STR(x) QUOTE(x)
2N/A
2N/Astatic char au_class_fname[PATH_MAX] = AUDITCLASSFILE;
2N/Astatic FILE *au_class_file = NULL;
2N/Astatic mutex_t mutex_classfile = DEFAULTMUTEX;
2N/Astatic mutex_t mutex_classcache = DEFAULTMUTEX;
2N/A
2N/A#ifdef DEBUG2
2N/Avoid
2N/Aprintclass(au_class_ent_t *p_c)
2N/A{
2N/A (void) printf("%llx:%s:%s\n", p_c->ac_class, p_c->ac_name,
2N/A p_c->ac_desc);
2N/A (void) fflush(stdout);
2N/A}
2N/A#endif
2N/A
2N/Avoid
2N/Asetauclass()
2N/A{
2N/A (void) mutex_lock(&mutex_classfile);
2N/A if (au_class_file != NULL) {
2N/A (void) fseek(au_class_file, 0L, 0);
2N/A }
2N/A (void) mutex_unlock(&mutex_classfile);
2N/A}
2N/A
2N/A
2N/Avoid
2N/Aendauclass()
2N/A{
2N/A (void) mutex_lock(&mutex_classfile);
2N/A if (au_class_file != NULL) {
2N/A (void) fclose(au_class_file);
2N/A au_class_file = NULL;
2N/A }
2N/A (void) mutex_unlock(&mutex_classfile);
2N/A}
2N/A
2N/A/*
2N/A * getauclassent():
2N/A * This is not MT-safe because of the static variables.
2N/A */
2N/Aau_class_ent_t *
2N/Agetauclassent()
2N/A{
2N/A static au_class_ent_t e;
2N/A static char cname[AU_CLASS_NAME_MAX];
2N/A static char cdesc[AU_CLASS_DESC_MAX];
2N/A
2N/A e.ac_name = cname;
2N/A e.ac_desc = cdesc;
2N/A
2N/A return (getauclassent_r(&e));
2N/A}
2N/A
2N/A/*
2N/A * getauclassent_r
2N/A * This is MT-safe if each thread passes in its own pointer
2N/A * to the space where the class entry is returned. Be careful
2N/A * to also allocate space from the cname and cdesc pointers
2N/A * in the au_class_ent structure.
2N/A */
2N/Aau_class_ent_t *
2N/Agetauclassent_r(au_class_ent_t *au_class_entry)
2N/A{
2N/A int i;
2N/A int c_int;
2N/A char *s, input[AU_CLASS_LINE_MAX+1];
2N/A char trim_buf[AU_CLASS_NAME_MAX+1];
2N/A au_class_t v;
2N/A boolean_t found = B_FALSE;
2N/A
2N/A /* open audit class file if it isn't already */
2N/A (void) mutex_lock(&mutex_classfile);
2N/A if (au_class_file == NULL) {
2N/A if ((au_class_file = fopen(au_class_fname, "rF")) == NULL) {
2N/A (void) mutex_unlock(&mutex_classfile);
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A for (;;) {
2N/A
2N/A i = 0;
2N/A while ((c_int = fgetc(au_class_file)) != EOF && c_int != '\n') {
2N/A if (i < AU_CLASS_LINE_MAX) {
2N/A input[i++] = c_int;
2N/A }
2N/A }
2N/A input[i] = '\0';
2N/A
2N/A s = input + strspn(input, " \t\r\n");
2N/A if ((*s != '\0') && (*s != '#')) {
2N/A found = B_TRUE;
2N/A s = input;
2N/A
2N/A /* parse bitfield */
2N/A i = strcspn(s, ":");
2N/A s[i] = '\0';
2N/A if (strncmp(s, "0x", 2) == 0) {
2N/A (void) sscanf(&s[2], "%" SCNx64, &v);
2N/A } else {
2N/A (void) sscanf(s, "%" SCNu64, &v);
2N/A }
2N/A au_class_entry->ac_class = v;
2N/A s = &s[i+1];
2N/A
2N/A /* parse class name */
2N/A i = strcspn(s, ":");
2N/A s[i] = '\0';
2N/A (void) sscanf(s, "%" VAL2STR(AU_CLASS_NAME_MAX) "s",
2N/A trim_buf);
2N/A (void) strncpy(au_class_entry->ac_name, trim_buf,
2N/A AU_CLASS_NAME_MAX);
2N/A s = &s[i+1];
2N/A
2N/A /* parse class description */
2N/A i = strcspn(s, "\n\0");
2N/A s[i] = '\0';
2N/A (void) strncpy(au_class_entry->ac_desc, s,
2N/A AU_CLASS_DESC_MAX);
2N/A
2N/A break;
2N/A }
2N/A
2N/A if (c_int == EOF) {
2N/A break;
2N/A }
2N/A }
2N/A
2N/A (void) mutex_unlock(&mutex_classfile);
2N/A
2N/A return (found ? au_class_entry : NULL);
2N/A}
2N/A
2N/A
2N/Aau_class_ent_t *
2N/Agetauclassnam(char *name)
2N/A{
2N/A static au_class_ent_t e;
2N/A static char cname[AU_CLASS_NAME_MAX];
2N/A static char cdesc[AU_CLASS_DESC_MAX];
2N/A
2N/A e.ac_name = cname;
2N/A e.ac_desc = cdesc;
2N/A
2N/A return (getauclassnam_r(&e, name));
2N/A}
2N/A
2N/Aau_class_ent_t *
2N/Agetauclassnam_r(au_class_ent_t *e, char *name)
2N/A{
2N/A while (getauclassent_r(e) != NULL) {
2N/A if (strncmp(e->ac_name, name, AU_CLASS_NAME_MAX) == 0) {
2N/A return (e);
2N/A }
2N/A }
2N/A return (NULL);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * xcacheauclass:
2N/A * Read the entire audit_class file into memory.
2N/A * Return a pointer to the requested entry in the cache
2N/A * or a pointer to an invalid entry if the the class
2N/A * requested is not known.
2N/A *
2N/A * Return < 0, do not set result pointer, if error.
2N/A * Return 0, set result pointer to invalid entry, if class not in cache.
2N/A * Return 1, set result pointer to a valid entry, if class is in cache.
2N/A */
2N/Astatic int
2N/Axcacheauclass(au_class_ent_t **result, char *class_name, au_class_t class_no,
2N/A int flags)
2N/A{
2N/A static int invalid;
2N/A static au_class_ent_t **class_tbl;
2N/A static int called_once;
2N/A static int lines = 0;
2N/A
2N/A char line[AU_CLASS_LINE_MAX+1];
2N/A FILE *fp;
2N/A au_class_ent_t *p_class;
2N/A int i;
2N/A int c_int;
2N/A int hit = 0;
2N/A char *s;
2N/A
2N/A (void) mutex_lock(&mutex_classcache);
2N/A if (called_once == 0) {
2N/A
2N/A /* Count number of lines in the class file */
2N/A if ((fp = fopen(au_class_fname, "rF")) == NULL) {
2N/A (void) mutex_unlock(&mutex_classcache);
2N/A return (-1);
2N/A }
2N/A for (;;) {
2N/A
2N/A i = 0;
2N/A while ((c_int = fgetc(fp)) != EOF && (c_int != '\n')) {
2N/A if (i < AU_CLASS_LINE_MAX) {
2N/A line[i++] = c_int;
2N/A }
2N/A }
2N/A line[i] = '\0';
2N/A
2N/A s = line + strspn(line, " \t\r\n");
2N/A if ((*s != '\0') && (*s != '#')) {
2N/A lines++;
2N/A }
2N/A
2N/A if (c_int == EOF) {
2N/A break;
2N/A }
2N/A }
2N/A (void) fclose(fp);
2N/A class_tbl = (au_class_ent_t **)calloc((size_t)lines + 1,
2N/A sizeof (class_tbl));
2N/A if (class_tbl == NULL) {
2N/A (void) mutex_unlock(&mutex_classcache);
2N/A return (-2);
2N/A }
2N/A
2N/A lines = 0;
2N/A setauclass();
2N/A /*
2N/A * This call to getauclassent is protected by
2N/A * mutex_classcache, so we don't need to use the thread-
2N/A * safe version (getauclassent_r).
2N/A */
2N/A while ((p_class = getauclassent()) != NULL) {
2N/A class_tbl[lines] = (au_class_ent_t *)
2N/A malloc(sizeof (au_class_ent_t));
2N/A if (class_tbl[lines] == NULL) {
2N/A (void) mutex_unlock(&mutex_classcache);
2N/A return (-3);
2N/A }
2N/A class_tbl[lines]->ac_name = strdup(p_class->ac_name);
2N/A class_tbl[lines]->ac_class = p_class->ac_class;
2N/A class_tbl[lines]->ac_desc = strdup(p_class->ac_desc);
2N/A#ifdef DEBUG2
2N/A printclass(class_tbl[lines]);
2N/A#endif
2N/A lines++;
2N/A }
2N/A endauclass();
2N/A invalid = lines;
2N/A class_tbl[invalid] = (au_class_ent_t *)
2N/A malloc(sizeof (au_class_ent_t));
2N/A if (class_tbl[invalid] == NULL) {
2N/A (void) mutex_unlock(&mutex_classcache);
2N/A return (-4);
2N/A }
2N/A class_tbl[invalid]->ac_name = "invalid class";
2N/A class_tbl[invalid]->ac_class = AU_MASK_NONE;
2N/A class_tbl[invalid]->ac_desc = class_tbl[invalid]->ac_name;
2N/A
2N/A called_once = 1;
2N/A
2N/A#ifdef DEBUG2
2N/A for (i = 0; i <= lines; i++) {
2N/A printclass(class_tbl[i]);
2N/A }
2N/A#endif
2N/A
2N/A } /* END if called_once */
2N/A *result = class_tbl[invalid];
2N/A if (flags & AU_CACHE_NAME) {
2N/A for (i = 0; i < lines; i++) {
2N/A if (strncmp(class_name, class_tbl[i]->ac_name,
2N/A AU_CLASS_NAME_MAX) == 0) {
2N/A *result = class_tbl[i];
2N/A hit = 1;
2N/A break;
2N/A }
2N/A }
2N/A } else if (flags & AU_CACHE_NUMBER) {
2N/A for (i = 0; i < lines; i++) {
2N/A if (class_no == class_tbl[i]->ac_class) {
2N/A *result = class_tbl[i];
2N/A hit = 1;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A (void) mutex_unlock(&mutex_classcache);
2N/A return (hit);
2N/A}
2N/A
2N/Aint
2N/Acacheauclass(au_class_ent_t **result, au_class_t class_no)
2N/A{
2N/A return (xcacheauclass(result, "", class_no, AU_CACHE_NUMBER));
2N/A}
2N/A
2N/Aint
2N/Acacheauclassnam(au_class_ent_t **result, char *class_name)
2N/A{
2N/A return (xcacheauclass(result, class_name, (au_class_t)AU_MASK_NONE,
2N/A AU_CACHE_NAME));
2N/A}