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