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