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>
72 static FILE *dbfp
; /* debug file */
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
{
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) {\
122 for (_i = 0; _i < 4; _i++) {\
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
139 setmask(const char *flags
)
143 auditd_rc_t rc
= AUDITD_SUCCESS
;
145 mask
.am_success
= 0x0;
146 mask
.am_failure
= 0x0;
150 * getauditflagsbin doesn't like blanks, but admins do
152 input
= malloc(strlen(flags
) + 1);
154 return (AUDITD_NO_MEMORY
);
158 for (; (c
= *flags
) != '\0'; flags
++) {
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) {
171 __audit_syslog("audit_syslog.so", LOG_CONS
| LOG_NDELAY
,
173 gettext("plugin is configured with empty class mask\n"));
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
191 tossit(au_event_t id
, int passfail
)
198 selFlag
= AU_PRS_SUCCESS
;
201 selFlag
= AU_PRS_FAILURE
;
203 default: /* no exit or return token */
204 selFlag
= AU_PRS_BOTH
;
207 (void) pthread_mutex_lock(&log_mutex
);
208 rc
= au_preselect(id
, &mask
, selFlag
, AU_PRS_USECACHE
);
209 (void) pthread_mutex_unlock(&log_mutex
);
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.
223 fromleft(char *p
, size_t avail
, char *attrname
, size_t attrlen
, char *txt
,
228 if (avail
< attrlen
+ ELLIPSIS_SIZE
)
231 (void) memcpy(p
, attrname
, 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
;
241 (void) memcpy(p
, txt
, txtlen
);
242 len
= attrlen
+ txtlen
;
250 fromright(char *p
, size_t avail
, char *attrname
, size_t attrlen
, char *txt
,
255 if (avail
< attrlen
+ ELLIPSIS_SIZE
)
258 (void) memcpy(p
, attrname
, 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
;
268 (void) memcpy(p
, txt
, txtlen
);
270 len
= attrlen
+ txtlen
;
277 init_hash(hashtable_t
*table
, int bad_key
, int table_length
,
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
) {
288 for (j
= 0; j
< i
; j
++)
289 free(table
[j
].ht_value
);
292 *(table
[i
].ht_value
) = '\0';
298 free_hash(hashtable_t
*table
, int table_length
)
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))
315 gethname(au_tid_addr_t
*tid
, char *p
, size_t max
, char *prefix
,
319 struct hostent
*host
;
327 if (prefix_len
> max
)
330 (void) memcpy(p
, prefix
, prefix_len
);
334 if (tid
->at_type
== AU_IPv6
) {
335 key
= tid
->at_addr
[0] ^
340 key
= (tid
->at_addr
[0]);
342 hash_key
= (char *)&key
;
344 HASH(hash_key
, ix
, HOSTHASHSIZE
);
349 l
= UNKNOWN_LEN
; /* includes end of string */
352 len
= prefix_len
+ strlcpy(p
, UNKNOWN
, l
);
356 if (tid
->at_type
== AU_IPv6
) {
357 if ((key
== hosthash
[ix
].ht_key
) &&
358 (hosthash
[ix
].ht_type
== tid
->at_type
)) {
361 for (i
= 0; i
< 4; i
++) {
362 if (hosthash
[ix
].ht_ip
[i
] != tid
->at_addr
[i
]) {
368 } else if (key
== hosthash
[ix
].ht_key
) {
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];
379 (void) memcpy((char *)hosthash
[ix
].ht_ip
,
380 (char *)tid
->at_addr
, AU_IPv6
);
383 host
= getipnodebyaddr((const void *)tid
->at_addr
,
384 tid
->at_type
, af
, &rc
);
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
);
391 hosthash
[ix
].ht_length
= strlcpy(hosthash
[ix
].ht_value
,
392 host
->h_name
, MAXHOSTNAMELEN
);
396 l
= hosthash
[ix
].ht_length
+ 1;
400 len
= prefix_len
+ strlcpy(p
, hosthash
[ix
].ht_value
, l
);
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
413 getuname(uid_t uid
, gid_t gid
, char *p
, size_t max
, char *prefix
,
417 char pw_buf
[GETPWUID_BUFF_LEN
];
423 if (prefix_len
> max
)
428 (void) memcpy(p
, prefix
, 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
);
442 l
= snprintf(uidhash
[ix
].ht_value
, USERNAMELEN
,
445 l
= strlcpy(uidhash
[ix
].ht_value
, pw
.pw_name
,
448 uidhash
[ix
].ht_length
= l
;
450 l
= uidhash
[ix
].ht_length
+ 1;
453 (void) memcpy(p
, uidhash
[ix
].ht_value
, l
);
456 if (gid
!= (gid_t
)-2) {
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
) ==
470 gidhash
[ix
].ht_length
=
471 snprintf(gidhash
[ix
].ht_value
, GIDNAMELEN
,
474 gidhash
[ix
].ht_length
=
475 strlcpy(gidhash
[ix
].ht_value
,
476 gr
.gr_name
, GIDNAMELEN
);
482 l
= gidhash
[ix
].ht_length
+ 1;
485 (void) memcpy(p
, gidhash
[ix
].ht_value
, l
);
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
502 filter(const char *input
, uint64_t sequence
, char *output
,
503 size_t in_len
, size_t out_len
)
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 */
514 static parse_context_t initial_ctx
;
515 static int first
= 1;
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
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
));
554 ctx
.adr
.adr_stream
= (char *)input
;
555 ctx
.adr
.adr_now
= (char *)input
;
558 while ((ctx
.adr
.adr_now
- ctx
.adr
.adr_stream
) < in_len
) {
559 assert(last_adr
!= ctx
.adr
.adr_now
);
561 last_adr
= ctx
.adr
.adr_now
;
562 if ((parse_rc
= parse_token(&ctx
)) != 0) {
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
);
572 (void) strlcpy(event_name
, event
->ae_desc
,
576 /* leave rc_ret unchanged */
579 if (ctx
.out
.sf_sequence
!= -1)
580 (void) snprintf(sequence_str
, EVENT_NAME_LEN
,
581 " (seq=%u) ", ctx
.out
.sf_sequence
);
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
);
592 (void) fprintf(dbfp
, message
);
595 __audit_syslog("audit_syslog.so",
596 LOG_PID
| LOG_ODELAY
| LOG_CONS
,
597 LOG_DAEMON
, LOG_ALERT
, message
);
601 if (rc
== AUDITD_SUCCESS
) {
602 if (tossit(ctx
.out
.sf_eventid
, ctx
.out
.sf_pass
)) {
604 if (ctx
.out
.sf_sequence
!= -1)
606 "syslog tossed (event=%hu) record %u "
608 ctx
.out
.sf_eventid
, ctx
.out
.sf_sequence
,
612 "syslog tossed (event=%hu) buffer %llu\n",
613 ctx
.out
.sf_eventid
, sequence
);
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 */
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",
662 used
= strlcpy(bp
, event
->ae_desc
, remaining
);
666 if (ctx
.out
.sf_pass
!= 0) {
667 if (ctx
.out
.sf_pass
< 0)
668 used
= strlcpy(bp
, " failed", remaining
);
670 used
= strlcpy(bp
, " ok", remaining
);
674 if (ctx
.out
.sf_asid
!= 0) {
675 used
= snprintf(bp
, remaining
, " session %u",
680 if (ctx
.out
.sf_auid
!= (uid_t
)-2) {
681 used
= getuname(ctx
.out
.sf_auid
, -2, bp
, remaining
,
682 STRCONSTARGS(" by "));
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 "));
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
);
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 "));
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 "));
715 if (ctx
.out
.sf_peuid
!= (uid_t
)-2) {
716 used
= getuname(ctx
.out
.sf_peuid
, -2, bp
, remaining
,
717 STRCONSTARGS(" proc_uid "));
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) {
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
);
739 (void) fprintf(dbfp
, "syslog writing buffer %llu\n",
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
);
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
);
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
);
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
);
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
790 auditd_plugin(const char *input
, size_t in_len
, uint64_t sequence
, char **error
)
793 auditd_rc_t rc
= AUDITD_SUCCESS
;
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))
801 "syslog: buffer sequence=%llu but prev=%llu\n",
802 sequence
, last_sequence
);
803 last_sequence
= sequence
;
808 outbuf
= malloc(OUTPUT_BUF_SIZE
);
809 if (outbuf
== NULL
) {
810 DPRINT((dbfp
, "syslog: out of memory; seq=%llu\n",
812 rc
= AUDITD_NO_MEMORY
;
813 *error
= strdup(gettext("Can't allocate buffers"));
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",
826 *error
= strdup(gettext(
827 "Unable to parse audit record"));
829 DPRINT((dbfp
, "syslog: rc = %d (-1 is discard), "
830 "sequence=%llu, toss_count=%llu\n",
831 rc
, sequence
, ++toss_count
));
840 auditd_plugin_open(const kva_t
*kvlist
, char **ret_list
, char **error
)
842 char localname
[MAXHOSTNAMELEN
+ 1];
845 /* kva_match doesn't do const, so copy the pointer */
846 kva_t
*kva
= (kva_t
*)kvlist
;
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
);
858 dbfp
= __auditd_debug_file_open();
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;
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
);
896 auditd_plugin_close(char **error
)
901 (void) pthread_mutex_destroy(&log_mutex
);
903 free_hash(hosthash
, HOSTHASHSIZE
);
904 free_hash(uidhash
, UIDHASHSIZE
);
905 free_hash(gidhash
, GIDHASHSIZE
);
909 return (AUDITD_SUCCESS
);