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]
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.
38 #define DPRINT(x) { (void) fprintf x; }
43 #include <arpa/inet.h>
50 #include <netinet/in.h>
58 #include <sys/types.h>
59 #include <sys/socket.h>
62 #include <bsm/audit.h>
63 #include <bsm/audit_record.h>
64 #include <security/auditd.h>
67 #include "sysplugin.h"
69 #include <audit_plugin.h>
71 /* gettext() obfuscation routine for lint */
77 static FILE *dbfp
; /* debug file */
80 extern void init_tokens();
81 extern int parse_token(parse_context_t
*);
83 static au_mask_t mask
;
84 static int initialized
= 0;
85 static size_t maxavail
;
86 static pthread_mutex_t log_mutex
;
88 #define ELLIPSIS "..."
89 #define ELLIPSIS_SIZE (sizeof (ELLIPSIS) - 1)
92 * simple hashing for uid and hostname lookup
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
98 * ht_type and ht_ip are only used for hostname lookup cacheing.
100 typedef struct hashtable
{
107 #define HOSTHASHSIZE 128
108 #define UIDHASHSIZE 128
109 #define GIDHASHSIZE 32
111 static hashtable_t uidhash
[UIDHASHSIZE
];
112 static hashtable_t gidhash
[GIDHASHSIZE
];
113 static hashtable_t hosthash
[HOSTHASHSIZE
];
115 #define STRCONSTARGS(s) (s), (sizeof (s) - 1)
117 * the hash "handles" collisions by overwriting the old
118 * hash entry with the new. Perfection is not the goal.
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.
124 #define HASH(s, r, m) {\
127 for (_i = 0; _i < 4; _i++) {\
135 * The default mask for sysplugin is to reject all record types.
136 * The parameters input here select which classes to allow.
138 * getauditflgsbin() outputs error messages to syslog.
140 * caller must hold log_mutex
144 setmask(const char *flags
)
148 auditd_rc_t rc
= AUDITD_SUCCESS
;
150 mask
.am_success
= 0x0;
151 mask
.am_failure
= 0x0;
155 * getauditflagsbin doesn't like blanks, but admins do
157 input
= malloc(strlen(flags
) + 1);
159 return (AUDITD_NO_MEMORY
);
163 for (; (c
= *flags
) != '\0'; flags
++) {
169 if (getauditflagsbin(input
, &tmask
) == 0) {
170 mask
.am_success
|= tmask
.am_success
;
171 mask
.am_failure
|= tmask
.am_failure
;
174 if ((mask
.am_success
| mask
.am_failure
) == 0) {
176 __audit_syslog("audit_syslog.so", LOG_CONS
| LOG_NDELAY
,
178 gettext("plugin is configured with empty class mask\n"));
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.
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
196 tossit(au_event_t id
, int passfail
)
203 selFlag
= AU_PRS_SUCCESS
;
206 selFlag
= AU_PRS_FAILURE
;
208 default: /* no exit or return token */
209 selFlag
= AU_PRS_BOTH
;
212 (void) pthread_mutex_lock(&log_mutex
);
213 rc
= au_preselect(id
, &mask
, selFlag
, AU_PRS_USECACHE
);
214 (void) pthread_mutex_unlock(&log_mutex
);
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.
228 fromleft(char *p
, size_t avail
, char *attrname
, size_t attrlen
, char *txt
,
233 if (avail
< attrlen
+ ELLIPSIS_SIZE
)
236 (void) memcpy(p
, attrname
, 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
;
246 (void) memcpy(p
, txt
, txtlen
);
247 len
= attrlen
+ txtlen
;
255 fromright(char *p
, size_t avail
, char *attrname
, size_t attrlen
, char *txt
,
260 if (avail
< attrlen
+ ELLIPSIS_SIZE
)
263 (void) memcpy(p
, attrname
, 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
;
273 (void) memcpy(p
, txt
, txtlen
);
275 len
= attrlen
+ txtlen
;
282 init_hash(hashtable_t
*table
, int bad_key
, int table_length
,
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
) {
293 for (j
= 0; j
< i
; j
++)
294 free(table
[j
].ht_value
);
297 *(table
[i
].ht_value
) = '\0';
303 free_hash(hashtable_t
*table
, int table_length
)
307 for (i
= 0; i
< table_length
; i
++) {
308 free(table
[i
].ht_value
);
314 * do IP -> hostname lookup
316 #define UNKNOWN "unknown"
317 #define UNKNOWN_LEN (sizeof (UNKNOWN))
320 gethname(au_tid_addr_t
*tid
, char *p
, size_t max
, char *prefix
,
324 struct hostent
*host
;
332 if (prefix_len
> max
)
335 (void) memcpy(p
, prefix
, prefix_len
);
339 if (tid
->at_type
== AU_IPv6
) {
340 key
= tid
->at_addr
[0] ^
345 key
= (tid
->at_addr
[0]);
347 hash_key
= (char *)&key
;
349 HASH(hash_key
, ix
, HOSTHASHSIZE
);
354 l
= UNKNOWN_LEN
; /* includes end of string */
357 len
= prefix_len
+ strlcpy(p
, UNKNOWN
, l
);
361 if (tid
->at_type
== AU_IPv6
) {
362 if ((key
== hosthash
[ix
].ht_key
) &&
363 (hosthash
[ix
].ht_type
== tid
->at_type
)) {
366 for (i
= 0; i
< 4; i
++) {
367 if (hosthash
[ix
].ht_ip
[i
] != tid
->at_addr
[i
]) {
373 } else if (key
== hosthash
[ix
].ht_key
) {
377 hosthash
[ix
].ht_key
= key
;
378 hosthash
[ix
].ht_type
= tid
->at_type
;
380 if (tid
->at_type
== AU_IPv4
) {
381 hosthash
[ix
].ht_ip
[0] = tid
->at_addr
[0];
384 (void) memcpy((char *)hosthash
[ix
].ht_ip
,
385 (char *)tid
->at_addr
, AU_IPv6
);
388 host
= getipnodebyaddr((const void *)tid
->at_addr
,
389 tid
->at_type
, af
, &rc
);
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
);
396 hosthash
[ix
].ht_length
= strlcpy(hosthash
[ix
].ht_value
,
397 host
->h_name
, MAXHOSTNAMELEN
);
401 l
= hosthash
[ix
].ht_length
+ 1;
405 len
= prefix_len
+ strlcpy(p
, hosthash
[ix
].ht_value
, l
);
410 * the appropriate buffer length for getpwuid_r() isn't documented;
411 * 1024 should be enough.
413 #define GETPWUID_BUFF_LEN 1024
414 #define USERNAMELEN 256
415 #define GIDNAMELEN 256
418 getuname(uid_t uid
, gid_t gid
, char *p
, size_t max
, char *prefix
,
422 char pw_buf
[GETPWUID_BUFF_LEN
];
428 if (prefix_len
> max
)
433 (void) memcpy(p
, prefix
, len
);
437 hash_key
= (char *)&uid
;
439 HASH(hash_key
, ix
, UIDHASHSIZE
);
441 if (uid
!= uidhash
[ix
].ht_key
) {
442 uidhash
[ix
].ht_key
= uid
;
444 if ((getpwuid_r(uid
, &pw
, pw_buf
, GETPWUID_BUFF_LEN
)) == NULL
)
445 l
= snprintf(uidhash
[ix
].ht_value
, USERNAMELEN
,
448 l
= strlcpy(uidhash
[ix
].ht_value
, pw
.pw_name
,
451 uidhash
[ix
].ht_length
= l
;
453 l
= uidhash
[ix
].ht_length
+ 1;
456 (void) memcpy(p
, uidhash
[ix
].ht_value
, l
);
459 if (gid
!= (gid_t
)-2) {
465 hash_key
= (char *)&gid
;
466 HASH(hash_key
, ix
, GIDHASHSIZE
);
468 if (gid
!= gidhash
[ix
].ht_key
) {
469 gidhash
[ix
].ht_key
= gid
;
471 if (getgrgid_r(gid
, &gr
, pw_buf
, GETPWUID_BUFF_LEN
) ==
473 gidhash
[ix
].ht_length
=
474 snprintf(gidhash
[ix
].ht_value
, GIDNAMELEN
,
477 gidhash
[ix
].ht_length
=
478 strlcpy(gidhash
[ix
].ht_value
,
479 gr
.gr_name
, GIDNAMELEN
);
485 l
= gidhash
[ix
].ht_length
+ 1;
488 (void) memcpy(p
, gidhash
[ix
].ht_value
, l
);
495 * filter() parse input; toss if not wanted.
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.
502 #define EVENT_NAME_LEN 32
505 filter(const char *input
, uint64_t sequence
, char *output
,
506 size_t in_len
, size_t out_len
)
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 */
517 static parse_context_t initial_ctx
;
518 static int first
= 1;
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
528 * New additions to parse_context_t or its submembers need to
529 * have this same treatment.
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
;
553 init_tokens(); /* cmd/praudit/toktable.c */
555 (void) memcpy(&ctx
, &initial_ctx
, sizeof (parse_context_t
));
557 ctx
.adr
.adr_stream
= (char *)input
;
558 ctx
.adr
.adr_now
= (char *)input
;
561 while ((ctx
.adr
.adr_now
- ctx
.adr
.adr_stream
) < in_len
) {
562 assert(last_adr
!= ctx
.adr
.adr_now
);
564 last_adr
= ctx
.adr
.adr_now
;
565 if ((parse_rc
= parse_token(&ctx
)) != 0) {
567 au_event_ent_t
*event
;
568 char event_name
[EVENT_NAME_LEN
];
569 char sequence_str
[EVENT_NAME_LEN
];
571 if (cacheauevent(&event
, ctx
.out
.sf_eventid
) < 0)
572 (void) snprintf(event_name
, EVENT_NAME_LEN
,
573 "%hu", ctx
.out
.sf_eventid
);
575 (void) strlcpy(event_name
, event
->ae_desc
,
579 /* leave rc_ret unchanged */
582 if (ctx
.out
.sf_sequence
!= -1)
583 (void) snprintf(sequence_str
, EVENT_NAME_LEN
,
584 " (seq=%u) ", ctx
.out
.sf_sequence
);
586 sequence_str
[0] = '\0';
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
);
595 (void) fprintf(dbfp
, message
);
598 __audit_syslog("audit_syslog.so",
599 LOG_PID
| LOG_ODELAY
| LOG_CONS
,
600 LOG_DAEMON
, LOG_ALERT
, message
);
604 if (rc
== AUDITD_SUCCESS
) {
605 if (tossit(ctx
.out
.sf_eventid
, ctx
.out
.sf_pass
)) {
607 if (ctx
.out
.sf_sequence
!= -1)
609 "syslog tossed (event=%hu) record %u "
611 ctx
.out
.sf_eventid
, ctx
.out
.sf_sequence
,
615 "syslog tossed (event=%hu) buffer %llu\n",
616 ctx
.out
.sf_eventid
, sequence
);
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.
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;
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;
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;
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;
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;
653 return (-1); /* tell caller it was tossed */
658 if (ctx
.out
.sf_eventid
!= 0) {
659 au_event_ent_t
*event
;
661 if (cacheauevent(&event
, ctx
.out
.sf_eventid
) < 0)
662 used
= snprintf(bp
, remaining
, "%hu",
665 used
= strlcpy(bp
, event
->ae_desc
, remaining
);
669 if (ctx
.out
.sf_pass
!= 0) {
670 if (ctx
.out
.sf_pass
< 0)
671 used
= strlcpy(bp
, " failed", remaining
);
673 used
= strlcpy(bp
, " ok", remaining
);
677 if (ctx
.out
.sf_asid
!= 0) {
678 used
= snprintf(bp
, remaining
, " session %u",
683 if (ctx
.out
.sf_auid
!= (uid_t
)-2) {
684 used
= getuname(ctx
.out
.sf_auid
, -2, bp
, remaining
,
685 STRCONSTARGS(" by "));
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 "));
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
);
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 "));
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 "));
718 if (ctx
.out
.sf_peuid
!= (uid_t
)-2) {
719 used
= getuname(ctx
.out
.sf_peuid
, -2, bp
, remaining
,
720 STRCONSTARGS(" proc_uid "));
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
733 if (ctx
.out
.sf_sequence
!= -1) {
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
);
742 (void) fprintf(dbfp
, "syslog writing buffer %llu\n",
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.
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
);
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
);
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
);
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
);
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)
789 #define OUTPUT_BUF_SIZE 1024 - 48
793 auditd_plugin(const char *input
, size_t in_len
, uint64_t sequence
, char **error
)
796 auditd_rc_t rc
= AUDITD_SUCCESS
;
798 static uint64_t last_sequence
= 0;
799 static uint64_t write_count
= 0;
800 static uint64_t toss_count
= 0;
802 if ((last_sequence
> 0) && (sequence
!= last_sequence
+ 1))
804 "syslog: buffer sequence=%llu but prev=%llu\n",
805 sequence
, last_sequence
);
806 last_sequence
= sequence
;
811 outbuf
= malloc(OUTPUT_BUF_SIZE
);
812 if (outbuf
== NULL
) {
813 DPRINT((dbfp
, "syslog: out of memory; seq=%llu\n",
815 rc
= AUDITD_NO_MEMORY
;
816 *error
= strdup(gettext("Can't allocate buffers"));
818 rc
= filter(input
, sequence
, outbuf
, in_len
, maxavail
);
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",
829 *error
= strdup(gettext(
830 "Unable to parse audit record"));
832 DPRINT((dbfp
, "syslog: rc = %d (-1 is discard), "
833 "sequence=%llu, toss_count=%llu\n",
834 rc
, sequence
, ++toss_count
));
843 auditd_plugin_open(const kva_t
*kvlist
, char **ret_list
, char **error
)
845 char localname
[MAXHOSTNAMELEN
+ 1];
848 /* kva_match doesn't do const, so copy the pointer */
849 kva_t
*kva
= (kva_t
*)kvlist
;
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
);
861 dbfp
= __auditd_debug_file_open();
864 (void) pthread_mutex_init(&log_mutex
, NULL
);
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.)
872 (void) pthread_mutex_lock(&log_mutex
);
873 if (gethostname(localname
, MAXHOSTNAMELEN
))
874 maxavail
= OUTPUT_BUF_SIZE
- 20;
876 maxavail
= OUTPUT_BUF_SIZE
- strlen(localname
) - 2;
877 (void) pthread_mutex_unlock(&log_mutex
);
879 if (init_hash(hosthash
, 0, HOSTHASHSIZE
, MAXHOSTNAMELEN
))
880 return (AUDITD_NO_MEMORY
);
882 if (init_hash(uidhash
, -2, UIDHASHSIZE
, USERNAMELEN
))
883 return (AUDITD_NO_MEMORY
);
885 if (init_hash(gidhash
, -2, GIDHASHSIZE
, GIDNAMELEN
))
886 return (AUDITD_NO_MEMORY
);
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"));
893 (void) pthread_mutex_unlock(&log_mutex
);
899 auditd_plugin_close(char **error
)
904 (void) pthread_mutex_destroy(&log_mutex
);
906 free_hash(hosthash
, HOSTHASHSIZE
);
907 free_hash(uidhash
, UIDHASHSIZE
);
908 free_hash(gidhash
, GIDHASHSIZE
);
912 return (AUDITD_SUCCESS
);