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