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