/*
* 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
* 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
*/
/*
*/
/*
* Convert binary audit records to syslog messages and send them off to syslog.
*
* auditd_plugin_open(), auditd_plugin() and auditd_plugin_close() implement a
* replaceable library for use by auditd; they are a project private interface
* and may change without notice.
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <libintl.h>
#include <netdb.h>
#include <pthread.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <syslog.h>
#include <unistd.h>
#include <bsm/audit_record.h>
#include <toktable.h>
#include "sysplugin.h"
#include <audit_plugin.h>
extern void init_tokens();
extern int parse_token(parse_context_t *);
static int initialized = 0;
/*
* Simple hashing for uid and hostname lookup.
*
* Performance tests showed that caching the hostname, uid, and gid
* make about a 40% difference for short audit records and regularly
* repeating hostname, uid, etc.
*
* ht_type and ht_ip are only used for hostname lookup caching.
*/
typedef struct hashtable {
char *ht_value;
} hashtable_t;
/*
* The hash "handles" collisions by overwriting the old
* hash entry with the new. Perfection is not the goal.
*
* The key (s) is a 32 bit integer, handled here as
* four bytes. If the hash size is increased beyond
* 256, this macro will need some work.
*/
#define HASH(s, r, m) {\
int _i;\
_mush ^= *(s)++;\
}\
r = _mush % m;\
}
/*
* setmask - the default mask for sysplugin is to reject all record types.
* The parameters input here select which classes to allow.
*
* getauditflgsbin() outputs error messages to syslog.
* Caller must hold log_mutex.
*/
static auditd_rc_t
{
/* getauditflagsbin() doesn't like blanks, but admins do */
return (AUDITD_NO_MEMORY);
if (c == ' ')
continue;
*ip++ = c;
}
*ip = '\0';
}
}
rc = AUDITD_INVALID;
"plugin is configured with empty class mask");
}
return (rc);
}
/*
* tossit - based on the current value of mask, either keep or toss the current
* audit record. The input is 1 for success, -1 for failure. 0 means no exit or
* return token was seen.
*
* au_preselect() returns 0 for delete it. -1 for some sort of error.
* Here, non-zero and -1 are considered equivalent. tossit() returns 1 for
* delete it and 0 for keep it.
*/
static int
{
int rc;
int selFlag;
switch (passfail) {
case 1:
break;
case -1:
break;
default: /* no exit or return token */
break;
}
(void) pthread_mutex_lock(&log_mutex);
(void) pthread_mutex_unlock(&log_mutex);
return (rc == 0);
}
/*
* fromleft - the three bytes for ellipsis could potentially be longer
* than the space available for text if maxavail is within two bytes of
* OUTPUT_BUF_SIZE, which can happen if the hostname is one or two
* characters long. If there isn't room for ellipsis, there isn't
* room for the data, so it is simply dropped.
*/
static size_t
{
return (0);
p += attrlen;
p += avail;
} else {
p += txtlen;
}
*p = '\0';
return (len);
}
static size_t
{
return (0);
p += attrlen;
p += avail;
} else {
p += txtlen;
}
*p = '\0';
return (len);
}
static int
{
int i;
for (i = 0; i < table_length; i++) {
int j;
for (j = 0; j < i; j++)
return (-1);
}
}
return (0);
}
static void
{
int i;
for (i = 0; i < table_length; i++) {
}
}
/* do IP -> hostname lookup */
static size_t
{
int rc;
int af;
int ix;
char *hash_key;
int match;
if (prefix_len > max)
return (0);
p += prefix_len;
max -= prefix_len;
} else
match = 0;
if (key == 0) {
l = UNKNOWN_LEN; /* includes end of string */
if (l > max)
l = max;
return (len);
}
int i;
match = 1;
for (i = 0; i < 4; i++) {
match = 0;
break;
}
}
}
match = 1;
}
if (!match) {
} else {
}
} else {
}
}
if (l > max)
l = max;
return (len);
}
/*
* The appropriate buffer length for getpwuid_r() isn't documented;
* 1024 should be enough.
*/
static size_t
{
int ix;
char *hash_key;
if (prefix_len > max)
return (0);
len = prefix_len;
p += len;
"%d", uid);
else
}
if (l > max)
l = max;
len += l - 1;
p += l - 1;
max -= l - 1;
if (max < 2)
return (len);
NULL)
"%d", gid);
else
}
*p++ = ':';
len++;
max--;
if (l > max)
l = max;
len += l - 1;
}
return (len);
}
/*
* filter - parse input; toss if not wanted.
*
* The input value sequence is a number generated when the buffer
* was queued. ctx.out.sf_sequence, if not -1, is the sequence number
* generated in c2audit. It is not part of the "official" syslog
* output but is included if DEBUG is on.
*/
static auditd_rc_t
{
char *bp;
int token_count = 0;
int parse_rc;
if (first) {
first = 0;
/*
* Any member or submember of parse_context_t which utilizes
* allocated memory must free() the memory after calling
* parse_token() for both the preselected and non-preselected
* cases.
* New additions to parse_context_t or its submembers need to
* have this same treatment.
*/
init_tokens(); /* cmd/praudit/toktable.c */
}
token_count++;
else
if (token_count < 2)
/* leave rc_ret unchanged */
rc = AUDITD_INVALID;
else
sequence_str[0] = '\0';
"token=%d) of record type %s%s\n", token_count,
"(previous token=%d) of record type %s%s",
break;
}
}
if (rc == AUDITD_SUCCESS) {
#if DEBUG
"syslog tossed (event=%hu) record %u "
"/ buffer %llu\n",
sequence);
else
"syslog tossed (event=%hu) buffer %llu\n",
#endif
/*
* Members or submembers of parse_context_t which
* utilize allocated memory need to free() the memory
* here to handle the case of not being preselected as
* well as below for when the event is preselected.
* New additions to parse_context_t or any of its
* submembers need to get the same treatment.
*/
}
}
}
}
}
return (-1); /* tell caller it was tossed */
}
else
}
else
}
}
STRCONSTARGS(" by "));
}
/* 4 = strlen(" as ") */
}
STRCONSTARGS(" in "),
}
/* 6 = strlen(" from ") */
STRCONSTARGS(" from "));
}
/* 11 = strlen(" proc_auid ") */
STRCONSTARGS(" proc_auid "));
}
STRCONSTARGS(" proc_uid "));
}
#if DEBUG
/*
* with performance testing, this has the effect of
* making that each message is unique, so syslogd
* won't collect a series of messages as "last message
* repeated n times," another reason why DEBUG 0
* should perform better than DEBUG 1. However the
* intention is to help debug lost data problems
*/
"syslog writing record %u / buffer %llu\n",
} else
sequence);
#endif
/*
* Long fields that may need truncation go here in
* order of decreasing priority. Paths are truncated
* from the left, text from the right.
*/
}
STRCONSTARGS(" attr_obj "),
}
}
}
}
return (rc_ret);
}
/*
* 1024 is max syslog record size, 48 is minimum header length,
* assuming a hostname length of 0. maxavail reduces use of the
* allocated space by the length of the hostname (see maxavail).
*/
/* ARGSUSED */
{
char *outbuf;
#if DEBUG
"syslog: buffer sequence=%llu but prev=%llu\n",
#endif
sequence));
} else {
if (rc == AUDITD_SUCCESS) {
"buffer=%llu, tossed=%llu\n",
} else if (rc > 0) { /* -1 == discard it */
sequence));
"Unable to parse audit record"));
} else {
"sequence=%llu, toss_count=%llu\n",
rc = 0;
}
}
return (rc);
}
/* ARGSUSED */
char **error)
{
char *value;
/* kva_match doesn't do const, so copy the pointer */
"The \"p_flags\" attribute is missing."));
return (AUDITD_INVALID);
}
if (!initialized) {
#if DEBUG
#endif
initialized = 1;
/*
* calculate length of the local hostname for adjusting the
* estimate of how much space is taken by the syslog header.
* If the local hostname isn't available, leave some room
* anyway. (The -2 is for the blanks on either side of the
* hostname in the syslog message.)
*/
(void) pthread_mutex_lock(&log_mutex);
else
(void) pthread_mutex_unlock(&log_mutex);
return (AUDITD_NO_MEMORY);
return (AUDITD_NO_MEMORY);
return (AUDITD_NO_MEMORY);
}
(void) pthread_mutex_lock(&log_mutex);
"incorrect p_flags setting; no records will be output"));
(void) pthread_mutex_unlock(&log_mutex);
return (rc);
}
/* ARGSUSED */
{
if (initialized) {
(void) pthread_mutex_destroy(&log_mutex);
}
initialized = 0;
return (AUDITD_SUCCESS);
}