xref: /illumos-gate/usr/src/lib/auditd_plugins/syslog/sysplugin.c (revision 7ab4e62e3b5c454f248a38bec0d489e8f5543324)
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
77 static FILE	*dbfp;			/* debug file */
78 #endif
79 
80 extern void init_tokens();
81 extern int parse_token(parse_context_t *);
82 
83 static au_mask_t	mask;
84 static int		initialized = 0;
85 static size_t		maxavail;
86 static 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  */
100 typedef 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 
111 static hashtable_t	uidhash[UIDHASHSIZE];
112 static hashtable_t	gidhash[GIDHASHSIZE];
113 static 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 
143 static auditd_rc_t
144 setmask(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 
195 static int
196 tossit(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 
227 static size_t
228 fromleft(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 
254 static size_t
255 fromright(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 
281 static int
282 init_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 
302 static void
303 free_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 
319 static size_t
320 gethname(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 
417 static size_t
418 getuname(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 
504 static auditd_rc_t
505 filter(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 */
792 auditd_rc_t
793 auditd_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 
842 auditd_rc_t
843 auditd_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 
898 auditd_rc_t
899 auditd_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