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
setmask(const char * flags)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
tossit(au_event_t id,int passfail)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
fromleft(char * p,size_t avail,char * attrname,size_t attrlen,char * txt,size_t txtlen)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
fromright(char * p,size_t avail,char * attrname,size_t attrlen,char * txt,size_t txtlen)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
init_hash(hashtable_t * table,int bad_key,int table_length,size_t max_value)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
free_hash(hashtable_t * table,int table_length)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
gethname(au_tid_addr_t * tid,char * p,size_t max,char * prefix,size_t prefix_len)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
getuname(uid_t uid,gid_t gid,char * p,size_t max,char * prefix,size_t prefix_len)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
filter(const char * input,uint64_t sequence,char * output,size_t in_len,size_t out_len)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
auditd_plugin(const char * input,size_t in_len,uint64_t sequence,char ** error)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
auditd_plugin_open(const kva_t * kvlist,char ** ret_list,char ** error)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
auditd_plugin_close(char ** error)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