1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 *
25 * convert binary audit records to syslog messages and
26 * send them off to syslog
27 *
28 */
29
30/*
31 * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close()
32 * implement a replacable library for use by auditd; they are a
33 * project private interface and may change without notice.
34 *
35 */
36#define	DEBUG	0
37#if DEBUG
38#define	DPRINT(x) { (void) fprintf x; }
39#else
40#define	DPRINT(x)
41#endif
42
43#include <arpa/inet.h>
44#include <assert.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <grp.h>
48#include <libintl.h>
49#include <netdb.h>
50#include <netinet/in.h>
51#include <pthread.h>
52#include <pwd.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
56#include <time.h>
57#include <syslog.h>
58#include <sys/types.h>
59#include <sys/socket.h>
60#include <unistd.h>
61
62#include <bsm/audit.h>
63#include <bsm/audit_record.h>
64#include <security/auditd.h>
65
66#include "toktable.h"
67#include "sysplugin.h"
68#include "systoken.h"
69#include <audit_plugin.h>
70
71/* gettext() obfuscation routine for lint */
72#ifdef __lint
73#define	gettext(x)	x
74#endif
75
76#if DEBUG
77static FILE	*dbfp;			/* debug file */
78#endif
79
80extern void init_tokens();
81extern int parse_token(parse_context_t *);
82
83static au_mask_t	mask;
84static int		initialized = 0;
85static size_t		maxavail;
86static pthread_mutex_t	log_mutex;
87
88#define	ELLIPSIS	"..."
89#define	ELLIPSIS_SIZE	(sizeof (ELLIPSIS) - 1)
90
91/*
92 * simple hashing for uid and hostname lookup
93 *
94 * performance tests showed that cacheing the hostname, uid, and gid
95 * make about a 40% difference for short audit records and regularly
96 * repeating hostname, uid, etc
97 *
98 * ht_type and ht_ip are only used for hostname lookup cacheing.
99 */
100typedef struct hashtable {
101	uint32_t	ht_key;
102	uint32_t	ht_type;
103	uint32_t	ht_ip[4];
104	char		*ht_value;
105	size_t		ht_length;
106} hashtable_t;
107#define	HOSTHASHSIZE	128
108#define	UIDHASHSIZE	128
109#define	GIDHASHSIZE	32
110
111static hashtable_t	uidhash[UIDHASHSIZE];
112static hashtable_t	gidhash[GIDHASHSIZE];
113static hashtable_t	hosthash[HOSTHASHSIZE];
114
115#define	STRCONSTARGS(s)	(s), (sizeof (s) - 1)
116/*
117 * the hash "handles" collisions by overwriting the old
118 * hash entry with the new.  Perfection is not the goal.
119 *
120 * the key (s) is a 32 bit integer, handled here as
121 * four bytes.  If the hash size is increased beyond
122 * 256, this macro will need some work.
123 */
124#define	HASH(s,  r, m)	{\
125				uint32_t	_mush = 0;\
126				int		_i;\
127				for (_i = 0; _i < 4; _i++) {\
128					_mush ^= *(s)++;\
129				}\
130				r = _mush % m;\
131			}
132
133
134/*
135 * The default mask for sysplugin is to reject all record types.
136 * The parameters input here select which classes to allow.
137 *
138 * getauditflgsbin() outputs error messages to syslog.
139 *
140 * caller must hold log_mutex
141 */
142
143static auditd_rc_t
144setmask(const char *flags)
145{
146	au_mask_t tmask;
147	char	*input, *ip, c;
148	auditd_rc_t	rc = AUDITD_SUCCESS;
149
150	mask.am_success = 0x0;
151	mask.am_failure = 0x0;
152
153	if (flags != NULL) {
154		/*
155		 * getauditflagsbin doesn't like blanks, but admins do
156		 */
157		input = malloc(strlen(flags) + 1);
158		if (input == NULL)
159			return (AUDITD_NO_MEMORY);
160
161		ip = input;
162
163		for (; (c = *flags) != '\0'; flags++) {
164			if (c == ' ')
165				continue;
166			*ip++ = c;
167		}
168		*ip = '\0';
169		if (getauditflagsbin(input, &tmask) == 0) {
170			mask.am_success |= tmask.am_success;
171			mask.am_failure |= tmask.am_failure;
172		}
173	}
174	if ((mask.am_success | mask.am_failure) == 0) {
175		rc = AUDITD_INVALID;
176		__audit_syslog("audit_syslog.so", LOG_CONS | LOG_NDELAY,
177		    LOG_DAEMON, LOG_ERR,
178		    gettext("plugin is configured with empty class mask\n"));
179	}
180	free(input);
181	return (rc);
182}
183
184/*
185 * based on the current value of mask, either keep or toss the
186 * current audit record.  The input is 1 for success, -1 for
187 * failure.  0 means no exit or return token was seen.
188 *
189 * au_preselect returns 1 for keep it, 0 for delete it, and
190 * -1 for some sort of error.  Here, 1 and -1 are considered
191 * equivalent.  tossit() returns 1 for delete it and 0 for
192 * keep it.
193 */
194
195static int
196tossit(au_event_t id, int passfail)
197{
198	int	rc;
199	int	selFlag;
200
201	switch (passfail) {
202	case 1:
203		selFlag = AU_PRS_SUCCESS;
204		break;
205	case -1:
206		selFlag = AU_PRS_FAILURE;
207		break;
208	default:		/* no exit or return token */
209		selFlag = AU_PRS_BOTH;
210		break;
211	}
212	(void) pthread_mutex_lock(&log_mutex);
213	rc = au_preselect(id, &mask, selFlag, AU_PRS_USECACHE);
214	(void) pthread_mutex_unlock(&log_mutex);
215
216	return (rc == 0);
217}
218
219/*
220 * the three bytes for ellipsis could potentially be longer than the
221 * space available for text if maxavail is within two bytes of
222 * OUTPUT_BUF_SIZE, which can happen if the hostname is one or two
223 * characters long.  If there isn't room for ellipsis, there isn't
224 * room for the data, so it is simply dropped.
225 */
226
227static size_t
228fromleft(char *p, size_t avail, char *attrname, size_t attrlen, char *txt,
229	size_t txtlen)
230{
231	size_t	len;
232
233	if (avail < attrlen + ELLIPSIS_SIZE)
234		return (0);
235
236	(void) memcpy(p, attrname, attrlen);
237	p += attrlen;
238	avail -= attrlen;
239	if (txtlen > avail) {
240		(void) memcpy(p, ELLIPSIS, ELLIPSIS_SIZE);
241		txt += txtlen - (avail - ELLIPSIS_SIZE);
242		(void) memcpy(p + ELLIPSIS_SIZE, txt, avail - ELLIPSIS_SIZE);
243		len = attrlen + avail;
244		p += avail;
245	} else {
246		(void) memcpy(p, txt, txtlen);
247		len = attrlen + txtlen;
248		p += txtlen;
249	}
250	*p = '\0';
251	return (len);
252}
253
254static size_t
255fromright(char *p, size_t avail, char *attrname, size_t attrlen, char *txt,
256	size_t txtlen)
257{
258	size_t	len;
259
260	if (avail < attrlen + ELLIPSIS_SIZE)
261		return (0);
262
263	(void) memcpy(p, attrname, attrlen);
264	p += attrlen;
265	avail -= attrlen;
266	if (txtlen > avail) {
267		(void) memcpy(p, txt, avail - ELLIPSIS_SIZE);
268		(void) memcpy(p + (avail - ELLIPSIS_SIZE),
269		    ELLIPSIS, ELLIPSIS_SIZE);
270		len = attrlen + avail;
271		p += avail;
272	} else {
273		(void) memcpy(p, txt, txtlen);
274		p += txtlen;
275		len = attrlen + txtlen;
276	}
277	*p = '\0';
278	return (len);
279}
280
281static int
282init_hash(hashtable_t *table, int bad_key, int table_length,
283    size_t max_value)
284{
285	int	i;
286
287	for (i = 0; i < table_length; i++) {
288		table[i].ht_value = malloc(max_value + 1);
289		table[i].ht_key = bad_key;
290		table[i].ht_length = 0;
291		if (table[i].ht_value == NULL) {
292			int	j;
293			for (j = 0; j < i; j++)
294				free(table[j].ht_value);
295			return (-1);
296		}
297		*(table[i].ht_value) = '\0';
298	}
299	return (0);
300}
301
302static void
303free_hash(hashtable_t *table, int table_length)
304{
305	int	i;
306
307	for (i = 0; i < table_length; i++) {
308		free(table[i].ht_value);
309	}
310}
311
312
313/*
314 * do IP -> hostname lookup
315 */
316#define	UNKNOWN		"unknown"
317#define	UNKNOWN_LEN	(sizeof (UNKNOWN))
318
319static size_t
320gethname(au_tid_addr_t *tid, char *p, size_t max, char *prefix,
321    size_t prefix_len)
322{
323	size_t			len, l;
324	struct hostent		*host;
325	int			rc;
326	int			af;
327	int			ix;
328	char			*hash_key;
329	uint32_t		key;
330	int			match;
331
332	if (prefix_len > max)
333		return (0);
334
335	(void) memcpy(p, prefix, prefix_len);
336	p += prefix_len;
337	max -= prefix_len;
338
339	if (tid->at_type == AU_IPv6) {
340		key = tid->at_addr[0] ^
341		    tid->at_addr[1] ^
342		    tid->at_addr[2] ^
343		    tid->at_addr[3];
344	} else
345		key = (tid->at_addr[0]);
346
347	hash_key = (char *)&key;
348
349	HASH(hash_key, ix, HOSTHASHSIZE);
350
351	match = 0;
352
353	if (key == 0) {
354		l = UNKNOWN_LEN;	/* includes end of string */
355		if (l > max)
356			l = max;
357		len = prefix_len + strlcpy(p, UNKNOWN, l);
358		return (len);
359	}
360
361	if (tid->at_type == AU_IPv6) {
362		if ((key == hosthash[ix].ht_key) &&
363		    (hosthash[ix].ht_type == tid->at_type)) {
364			int i;
365			match = 1;
366			for (i = 0; i < 4; i++) {
367				if (hosthash[ix].ht_ip[i] != tid->at_addr[i]) {
368					match = 0;
369					break;
370				}
371			}
372		}
373	} else if (key == hosthash[ix].ht_key) {
374		match = 1;
375	}
376	if (!match) {
377		hosthash[ix].ht_key = key;
378		hosthash[ix].ht_type = tid->at_type;
379
380		if (tid->at_type == AU_IPv4) {
381			hosthash[ix].ht_ip[0] = tid->at_addr[0];
382			af = AF_INET;
383		} else {
384			(void) memcpy((char *)hosthash[ix].ht_ip,
385			    (char *)tid->at_addr, AU_IPv6);
386			af = AF_INET6;
387		}
388		host = getipnodebyaddr((const void *)tid->at_addr,
389		    tid->at_type, af, &rc);
390
391		if (host == NULL) {
392			(void) inet_ntop(af, (void *)tid->at_addr,
393			    hosthash[ix].ht_value, MAXHOSTNAMELEN);
394			hosthash[ix].ht_length = strlen(hosthash[ix].ht_value);
395		} else {
396			hosthash[ix].ht_length = strlcpy(hosthash[ix].ht_value,
397			    host->h_name,  MAXHOSTNAMELEN);
398			freehostent(host);
399		}
400	}
401	l = hosthash[ix].ht_length + 1;
402	if (l > max)
403		l = max;
404
405	len = prefix_len + strlcpy(p, hosthash[ix].ht_value, l);
406
407	return (len);
408}
409/*
410 * the appropriate buffer length for getpwuid_r() isn't documented;
411 * 1024 should be enough.
412 */
413#define	GETPWUID_BUFF_LEN	1024
414#define	USERNAMELEN		256
415#define	GIDNAMELEN		256
416
417static size_t
418getuname(uid_t uid, gid_t gid, char *p, size_t max, char *prefix,
419    size_t prefix_len)
420{
421	struct passwd		pw;
422	char			pw_buf[GETPWUID_BUFF_LEN];
423	size_t			len, l;
424	struct group		gr;
425	int			ix;
426	char			*hash_key;
427
428	if (prefix_len > max)
429		return (0);
430
431	len = prefix_len;
432
433	(void) memcpy(p, prefix, len);
434	p += len;
435	max -= len;
436
437	hash_key = (char *)&uid;
438
439	HASH(hash_key, ix, UIDHASHSIZE);
440
441	if (uid != uidhash[ix].ht_key) {
442		uidhash[ix].ht_key = uid;
443
444		if ((getpwuid_r(uid, &pw, pw_buf, GETPWUID_BUFF_LEN)) == NULL)
445			l = snprintf(uidhash[ix].ht_value, USERNAMELEN,
446			    "%d", uid);
447		else
448			l = strlcpy(uidhash[ix].ht_value, pw.pw_name,
449			    USERNAMELEN);
450
451		uidhash[ix].ht_length = l;
452	}
453	l = uidhash[ix].ht_length + 1;
454	if (l > max)
455		l = max;
456	(void) memcpy(p, uidhash[ix].ht_value, l);
457	len += l - 1;
458
459	if (gid != (gid_t)-2) {
460		p += l - 1;
461		max -= l - 1;
462		if (max < 2)
463			return (len);
464
465		hash_key = (char *)&gid;
466		HASH(hash_key, ix, GIDHASHSIZE);
467
468		if (gid != gidhash[ix].ht_key) {
469			gidhash[ix].ht_key = gid;
470
471			if (getgrgid_r(gid, &gr,  pw_buf, GETPWUID_BUFF_LEN) ==
472			    NULL)
473				gidhash[ix].ht_length =
474				    snprintf(gidhash[ix].ht_value, GIDNAMELEN,
475				    "%d", gid);
476			else
477				gidhash[ix].ht_length =
478				    strlcpy(gidhash[ix].ht_value,
479				    gr.gr_name, GIDNAMELEN);
480		}
481		*p++ = ':';
482		len++;
483		max--;
484
485		l = gidhash[ix].ht_length + 1;
486		if (l > max)
487			l = max;
488		(void) memcpy(p, gidhash[ix].ht_value, l);
489		len += l - 1;
490	}
491	return (len);
492}
493
494/*
495 * filter() parse input; toss if not wanted.
496 *
497 * the input value sequence is a number generated when the buffer
498 * was queued.  ctx.out.sf_sequence, if not -1, is the sequence number
499 * generated in c2audit.  It is not part of the "official" syslog
500 * output but is included if DEBUG is on.
501 */
502#define	EVENT_NAME_LEN	32
503
504static auditd_rc_t
505filter(const char *input, uint64_t sequence, char *output,
506    size_t in_len, size_t out_len)
507{
508	parse_context_t		ctx;
509	char			*bp;
510	auditd_rc_t		rc = AUDITD_SUCCESS;
511	auditd_rc_t		rc_ret = AUDITD_SUCCESS;
512	size_t			used, remaining;
513	char			*last_adr; /* infinite loop check */
514	int			token_count = 0;
515	int			parse_rc;
516
517	static parse_context_t	initial_ctx;
518	static int		first = 1;
519
520	if (first) {
521		first = 0;
522
523		/*
524		 * Any member or submember of parse_context_t which utilizes
525		 * allocated memory must free() the memory after calling
526		 * parse_token() for both the preselected and non-preselected
527		 * cases.
528		 * New additions to parse_context_t or its submembers need to
529		 * have this same treatment.
530		 */
531		initial_ctx.out.sf_eventid = 0;
532		initial_ctx.out.sf_reclen = 0;
533		initial_ctx.out.sf_pass = 0;
534		initial_ctx.out.sf_asid = 0;
535		initial_ctx.out.sf_auid = (uid_t)-2;
536		initial_ctx.out.sf_euid = (uid_t)-2;
537		initial_ctx.out.sf_egid = (gid_t)-2;
538		initial_ctx.out.sf_tid.at_type = 0;
539		initial_ctx.out.sf_pauid = (uid_t)-2;
540		initial_ctx.out.sf_peuid = (uid_t)-2;
541		initial_ctx.out.sf_uauthlen = 0;
542		initial_ctx.out.sf_uauth = NULL;
543		initial_ctx.out.sf_pathlen = 0;
544		initial_ctx.out.sf_path = NULL;
545		initial_ctx.out.sf_atpathlen = 0;
546		initial_ctx.out.sf_atpath = NULL;
547		initial_ctx.out.sf_textlen = 0;
548		initial_ctx.out.sf_text = NULL;
549		initial_ctx.out.sf_sequence = -1;
550		initial_ctx.out.sf_zonelen = 0;
551		initial_ctx.out.sf_zonename = NULL;
552
553		init_tokens();		/* cmd/praudit/toktable.c */
554	}
555	(void) memcpy(&ctx, &initial_ctx, sizeof (parse_context_t));
556	ctx.id = sequence;
557	ctx.adr.adr_stream = (char *)input;
558	ctx.adr.adr_now = (char *)input;
559
560	last_adr = NULL;
561	while ((ctx.adr.adr_now - ctx.adr.adr_stream) < in_len) {
562		assert(last_adr != ctx.adr.adr_now);
563		token_count++;
564		last_adr = ctx.adr.adr_now;
565		if ((parse_rc = parse_token(&ctx)) != 0) {
566			char	message[256];
567			au_event_ent_t	*event;
568			char	event_name[EVENT_NAME_LEN];
569			char	sequence_str[EVENT_NAME_LEN];
570
571			if (cacheauevent(&event, ctx.out.sf_eventid) < 0)
572				(void) snprintf(event_name, EVENT_NAME_LEN,
573				    "%hu", ctx.out.sf_eventid);
574			else
575				(void) strlcpy(event_name, event->ae_desc,
576				    EVENT_NAME_LEN);
577
578			if (token_count < 2)
579				/* leave rc_ret unchanged */
580				rc = AUDITD_INVALID;
581
582			if (ctx.out.sf_sequence != -1)
583				(void) snprintf(sequence_str, EVENT_NAME_LEN,
584				    " (seq=%u) ", ctx.out.sf_sequence);
585			else
586				sequence_str[0] = '\0';
587
588			(void) snprintf(message, 256,
589			    gettext("error before token %d (previous token=%d)"
590			    " of record type %s%s\n"),
591			    token_count, parse_rc, event_name, sequence_str);
592
593#if DEBUG
594			/*LINTED*/
595			(void) fprintf(dbfp, message);
596#endif
597
598			__audit_syslog("audit_syslog.so",
599			    LOG_PID | LOG_ODELAY | LOG_CONS,
600			    LOG_DAEMON, LOG_ALERT, message);
601			break;
602		}
603	}
604	if (rc == AUDITD_SUCCESS) {
605		if (tossit(ctx.out.sf_eventid, ctx.out.sf_pass)) {
606#if DEBUG
607			if (ctx.out.sf_sequence != -1)
608				(void) fprintf(dbfp,
609				    "syslog tossed (event=%hu) record %u "
610				    "/ buffer %llu\n",
611				    ctx.out.sf_eventid, ctx.out.sf_sequence,
612				    sequence);
613			else
614				(void) fprintf(dbfp,
615				    "syslog tossed (event=%hu) buffer %llu\n",
616				    ctx.out.sf_eventid, sequence);
617#endif
618
619			/*
620			 * Members or submembers of parse_context_t which
621			 * utilize allocated memory need to free() the memory
622			 * here to handle the case of not being preselected as
623			 * well as below for when the event is preselected.
624			 * New additions to parse_context_t or any of its
625			 * submembers need to get the same treatment.
626			 */
627			if (ctx.out.sf_uauthlen > 0) {
628				free(ctx.out.sf_uauth);
629				ctx.out.sf_uauth = NULL;
630				ctx.out.sf_uauthlen = 0;
631			}
632			if (ctx.out.sf_pathlen > 0) {
633				free(ctx.out.sf_path);
634				ctx.out.sf_path = NULL;
635				ctx.out.sf_pathlen = 0;
636			}
637			if (ctx.out.sf_atpathlen > 0) {
638				free(ctx.out.sf_atpath);
639				ctx.out.sf_atpath = NULL;
640				ctx.out.sf_atpathlen = 0;
641			}
642			if (ctx.out.sf_textlen > 0) {
643				free(ctx.out.sf_text);
644				ctx.out.sf_text = NULL;
645				ctx.out.sf_textlen = 0;
646			}
647			if (ctx.out.sf_zonelen > 0) {
648				free(ctx.out.sf_zonename);
649				ctx.out.sf_zonename = NULL;
650				ctx.out.sf_zonelen = 0;
651			}
652
653			return (-1);	/* tell caller it was tossed */
654		}
655		bp = output;
656		remaining = out_len;
657
658		if (ctx.out.sf_eventid != 0) {
659			au_event_ent_t	*event;
660
661			if (cacheauevent(&event, ctx.out.sf_eventid) < 0)
662				used = snprintf(bp, remaining, "%hu",
663				    ctx.out.sf_eventid);
664			else
665				used = strlcpy(bp, event->ae_desc, remaining);
666			bp += used;
667			remaining -= used;
668		}
669		if (ctx.out.sf_pass != 0) {
670			if (ctx.out.sf_pass < 0)
671				used = strlcpy(bp, " failed", remaining);
672			else
673				used = strlcpy(bp, " ok", remaining);
674			bp += used;
675			remaining -= used;
676		}
677		if (ctx.out.sf_asid != 0) {
678			used = snprintf(bp, remaining, " session %u",
679			    ctx.out.sf_asid);
680			remaining -= used;
681			bp += used;
682		}
683		if (ctx.out.sf_auid != (uid_t)-2) {
684			used = getuname(ctx.out.sf_auid, -2, bp, remaining,
685			    STRCONSTARGS(" by "));
686			bp += used;
687			remaining -= used;
688		}
689		if (ctx.out.sf_euid != (uid_t)-2) {
690			/* 4 = strlen(" as ") */
691			used = getuname(ctx.out.sf_euid, ctx.out.sf_egid, bp,
692			    remaining, STRCONSTARGS(" as "));
693			bp += used;
694			remaining -= used;
695		}
696		if (ctx.out.sf_zonename != NULL) {
697			used = fromright(bp, remaining,
698			    STRCONSTARGS(" in "),
699			    ctx.out.sf_zonename, ctx.out.sf_zonelen);
700			free(ctx.out.sf_zonename);
701			bp += used;
702			remaining -= used;
703		}
704		if (ctx.out.sf_tid.at_type != 0) {
705			/* 6 = strlen(" from ") */
706			used = gethname(&(ctx.out.sf_tid), bp, remaining,
707			    STRCONSTARGS(" from "));
708			bp += used;
709			remaining -= used;
710		}
711		if (ctx.out.sf_pauid != (uid_t)-2) {
712			/* 11 = strlen(" proc_auid ") */
713			used = getuname(ctx.out.sf_pauid, -2, bp, remaining,
714			    STRCONSTARGS(" proc_auid "));
715			bp += used;
716			remaining -= used;
717		}
718		if (ctx.out.sf_peuid != (uid_t)-2) {
719			used = getuname(ctx.out.sf_peuid, -2, bp, remaining,
720			    STRCONSTARGS(" proc_uid "));
721			bp += used;
722			remaining -= used;
723		}
724#if DEBUG
725		/*
726		 * with performance testing, this has the effect of
727		 * making that each message is unique, so syslogd
728		 * won't collect a series of messages as "last message
729		 * repeated n times," another reason why DEBUG 0
730		 * should perform better than DEBUG 1.  However the
731		 * intention is to help debug lost data problems
732		 */
733		if (ctx.out.sf_sequence != -1) {
734			(void) fprintf(dbfp,
735			    "syslog writing record %u / buffer %llu\n",
736			    ctx.out.sf_sequence, sequence);
737			used = snprintf(bp, remaining, "  seq %u",
738			    ctx.out.sf_sequence, sequence);
739			remaining -= used;
740			bp += used;
741		} else
742			(void) fprintf(dbfp, "syslog writing buffer %llu\n",
743			    sequence);
744#endif
745		/*
746		 * Long fields that may need truncation go here in
747		 * order of decreasing priority.  Paths are truncated
748		 * from the left, text from the right.
749		 */
750		if (ctx.out.sf_path != NULL) {
751			used = fromleft(bp, remaining, STRCONSTARGS(" obj "),
752			    ctx.out.sf_path, ctx.out.sf_pathlen);
753			free(ctx.out.sf_path);
754			bp += used;
755			remaining -= used;
756		}
757		if (ctx.out.sf_atpath != NULL) {
758			used = fromleft(bp, remaining,
759			    STRCONSTARGS(" attr_obj "),
760			    ctx.out.sf_atpath, ctx.out.sf_atpathlen);
761			free(ctx.out.sf_atpath);
762			bp += used;
763			remaining -= used;
764		}
765		if (ctx.out.sf_uauth != NULL) {
766			used = fromright(bp, remaining, STRCONSTARGS(" uauth "),
767			    ctx.out.sf_uauth, ctx.out.sf_uauthlen);
768			free(ctx.out.sf_path);
769			bp += used;
770			remaining -= used;
771		}
772		if (ctx.out.sf_text != NULL) {
773			used = fromright(bp, remaining,
774			    STRCONSTARGS(AU_TEXT_NAME),
775			    ctx.out.sf_text, ctx.out.sf_textlen);
776			free(ctx.out.sf_text);
777			bp += used;
778			remaining -= used;
779		}
780	}
781	return (rc_ret);
782}
783
784/*
785 * 1024 is max syslog record size, 48 is minimum header length,
786 * assuming a hostname length of 0.  maxavail reduces use of the
787 * allocated space by the length of the hostname (see maxavail)
788 */
789#define	OUTPUT_BUF_SIZE	1024 - 48
790
791/* ARGSUSED */
792auditd_rc_t
793auditd_plugin(const char *input, size_t in_len, uint64_t sequence, char **error)
794{
795	char		*outbuf;
796	auditd_rc_t	rc = AUDITD_SUCCESS;
797#if DEBUG
798	static	uint64_t	last_sequence = 0;
799	static	uint64_t	write_count = 0;
800	static	uint64_t	toss_count = 0;
801
802	if ((last_sequence > 0) && (sequence != last_sequence + 1))
803		(void) fprintf(dbfp,
804		    "syslog: buffer sequence=%llu but prev=%llu\n",
805		    sequence, last_sequence);
806	last_sequence = sequence;
807#endif
808
809	*error = NULL;
810
811	outbuf = malloc(OUTPUT_BUF_SIZE);
812	if (outbuf == NULL) {
813		DPRINT((dbfp, "syslog: out of memory; seq=%llu\n",
814		    sequence));
815		rc = AUDITD_NO_MEMORY;
816		*error = strdup(gettext("Can't allocate buffers"));
817	} else {
818		rc = filter(input, sequence, outbuf, in_len, maxavail);
819
820		if (rc == AUDITD_SUCCESS) {
821			__audit_syslog("audit", LOG_NDELAY,
822			    LOG_AUDIT, LOG_NOTICE, outbuf);
823			DPRINT((dbfp, "syslog: write_count=%llu, "
824			    "buffer=%llu, tossed=%llu\n",
825			    ++write_count, sequence, toss_count));
826		} else if (rc > 0) {	/* -1 == discard it */
827			DPRINT((dbfp, "syslog: parse failed for buffer %llu\n",
828			    sequence));
829			*error = strdup(gettext(
830			    "Unable to parse audit record"));
831		} else {
832			DPRINT((dbfp, "syslog: rc = %d (-1 is discard), "
833			    "sequence=%llu, toss_count=%llu\n",
834			    rc, sequence, ++toss_count));
835			rc = 0;
836		}
837		free(outbuf);
838	}
839	return (rc);
840}
841
842auditd_rc_t
843auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error)
844{
845	char		localname[MAXHOSTNAMELEN + 1];
846	auditd_rc_t	rc;
847	char		*value;
848	/* kva_match doesn't do const, so copy the pointer */
849	kva_t		*kva = (kva_t *)kvlist;
850
851	*error = NULL;
852	*ret_list = NULL;
853
854	if ((kvlist == NULL) || ((value = kva_match(kva, "p_flags")) == NULL)) {
855		*error = strdup(gettext(
856		    "The \"p_flags\" attribute is missing."));
857		return (AUDITD_INVALID);
858	}
859	if (!initialized) {
860#if DEBUG
861		dbfp = __auditd_debug_file_open();
862#endif
863		initialized = 1;
864		(void) pthread_mutex_init(&log_mutex, NULL);
865		/*
866		 * calculate length of the local hostname for adjusting the
867		 * estimate of how much space is taken by the syslog header.
868		 * If the local hostname isn't available, leave some room
869		 * anyway.  (The -2 is for the blanks on either side of the
870		 * hostname in the syslog message.)
871		 */
872		(void) pthread_mutex_lock(&log_mutex);
873		if (gethostname(localname, MAXHOSTNAMELEN))
874			maxavail = OUTPUT_BUF_SIZE - 20;
875		else
876			maxavail = OUTPUT_BUF_SIZE - strlen(localname) - 2;
877		(void) pthread_mutex_unlock(&log_mutex);
878
879		if (init_hash(hosthash, 0, HOSTHASHSIZE, MAXHOSTNAMELEN))
880			return (AUDITD_NO_MEMORY);
881
882		if (init_hash(uidhash, -2, UIDHASHSIZE, USERNAMELEN))
883			return (AUDITD_NO_MEMORY);
884
885		if (init_hash(gidhash, -2, GIDHASHSIZE, GIDNAMELEN))
886			return (AUDITD_NO_MEMORY);
887	}
888	(void) pthread_mutex_lock(&log_mutex);
889	if ((rc = setmask(value)) != AUDITD_SUCCESS)
890		*error = strdup(gettext(
891		    "incorrect p_flags setting; no records will be output"));
892
893	(void) pthread_mutex_unlock(&log_mutex);
894
895	return (rc);
896}
897
898auditd_rc_t
899auditd_plugin_close(char **error)
900{
901	*error = NULL;
902
903	if (initialized) {
904		(void) pthread_mutex_destroy(&log_mutex);
905
906		free_hash(hosthash, HOSTHASHSIZE);
907		free_hash(uidhash, UIDHASHSIZE);
908		free_hash(gidhash, GIDHASHSIZE);
909	}
910	initialized = 0;
911
912	return (AUDITD_SUCCESS);
913}
914