1657a8c20SJan Friedel /*
2657a8c20SJan Friedel * CDDL HEADER START
3657a8c20SJan Friedel *
4657a8c20SJan Friedel * The contents of this file are subject to the terms of the
5657a8c20SJan Friedel * Common Development and Distribution License (the "License").
6657a8c20SJan Friedel * You may not use this file except in compliance with the License.
7657a8c20SJan Friedel *
8657a8c20SJan Friedel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9657a8c20SJan Friedel * or http://www.opensolaris.org/os/licensing.
10657a8c20SJan Friedel * See the License for the specific language governing permissions
11657a8c20SJan Friedel * and limitations under the License.
12657a8c20SJan Friedel *
13657a8c20SJan Friedel * When distributing Covered Code, include this CDDL HEADER in each
14657a8c20SJan Friedel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15657a8c20SJan Friedel * If applicable, add the following below this CDDL HEADER, with the
16657a8c20SJan Friedel * fields enclosed by brackets "[]" replaced with your own identifying
17657a8c20SJan Friedel * information: Portions Copyright [yyyy] [name of copyright owner]
18657a8c20SJan Friedel *
19657a8c20SJan Friedel * CDDL HEADER END
20657a8c20SJan Friedel */
21657a8c20SJan Friedel /*
229fa473b0SJan Friedel * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23657a8c20SJan Friedel *
24657a8c20SJan Friedel * send audit records to remote host
25657a8c20SJan Friedel *
26657a8c20SJan Friedel */
27657a8c20SJan Friedel
28657a8c20SJan Friedel /*
29657a8c20SJan Friedel * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close()
30657a8c20SJan Friedel * implement a replaceable library for use by auditd; they are a
31657a8c20SJan Friedel * project private interface and may change without notice.
32657a8c20SJan Friedel */
33657a8c20SJan Friedel
34657a8c20SJan Friedel #include <arpa/inet.h>
35657a8c20SJan Friedel #include <assert.h>
36657a8c20SJan Friedel #include <audit_plugin.h>
37657a8c20SJan Friedel #include <bsm/audit.h>
38657a8c20SJan Friedel #include <bsm/audit_record.h>
39657a8c20SJan Friedel #include <bsm/libbsm.h>
40657a8c20SJan Friedel #include <errno.h>
41657a8c20SJan Friedel #include <fcntl.h>
42657a8c20SJan Friedel #include <gssapi/gssapi.h>
43657a8c20SJan Friedel #include <libintl.h>
44657a8c20SJan Friedel #include <netdb.h>
45657a8c20SJan Friedel #include <pthread.h>
46657a8c20SJan Friedel #include <rpc/rpcsec_gss.h>
47657a8c20SJan Friedel #include <secdb.h>
48657a8c20SJan Friedel #include <signal.h>
49657a8c20SJan Friedel #include <stdio.h>
50657a8c20SJan Friedel #include <stdlib.h>
51657a8c20SJan Friedel #include <string.h>
52657a8c20SJan Friedel #include <strings.h>
53657a8c20SJan Friedel #include <ctype.h>
54657a8c20SJan Friedel #include <sys/param.h>
55657a8c20SJan Friedel #include <sys/socket.h>
56657a8c20SJan Friedel #include <sys/types.h>
57657a8c20SJan Friedel #include <unistd.h>
58657a8c20SJan Friedel #include <poll.h>
59657a8c20SJan Friedel
60657a8c20SJan Friedel #include "audit_remote.h"
61657a8c20SJan Friedel
62657a8c20SJan Friedel #define DEFAULT_TIMEOUT 5 /* default connection timeout (in secs) */
63657a8c20SJan Friedel #define NOSUCCESS_DELAY 20 /* unsuccessful delivery to all p_hosts */
64657a8c20SJan Friedel
65657a8c20SJan Friedel #define FL_SET B_TRUE /* set_fdfl(): set the flag */
66657a8c20SJan Friedel #define FL_UNSET B_FALSE /* set_fdfl(): unset the flag */
67657a8c20SJan Friedel
68657a8c20SJan Friedel static int nosuccess_cnt; /* unsuccessful delivery counter */
69657a8c20SJan Friedel
70f8994074SJan Friedel static int retries; /* connection retries */
71f8994074SJan Friedel int timeout; /* connection timeout */
72f8994074SJan Friedel static int timeout_p_timeout; /* p_timeout attr storage */
73657a8c20SJan Friedel
74657a8c20SJan Friedel /* semi-exponential timeout back off; x .. attempts, y .. timeout */
75657a8c20SJan Friedel #define BOFF_TIMEOUT(x, y) (x < 3 ? y * 2 * x : y * 8)
76657a8c20SJan Friedel
77657a8c20SJan Friedel /* general plugin lock */
78657a8c20SJan Friedel pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER;
79657a8c20SJan Friedel
80657a8c20SJan Friedel static struct hostlist_s *current_host;
81657a8c20SJan Friedel static struct hostlist_s *hosts;
82f8994074SJan Friedel static struct hostlist_s *hosts_prev;
83657a8c20SJan Friedel
84657a8c20SJan Friedel extern struct transq_hdr_s transq_hdr;
85657a8c20SJan Friedel static long transq_count_max;
86657a8c20SJan Friedel extern pthread_mutex_t transq_lock;
87657a8c20SJan Friedel
88657a8c20SJan Friedel extern pthread_t recv_tid;
89657a8c20SJan Friedel
90657a8c20SJan Friedel extern boolean_t notify_pipe_ready;
91657a8c20SJan Friedel extern int notify_pipe[2];
92657a8c20SJan Friedel
93657a8c20SJan Friedel #if DEBUG
94657a8c20SJan Friedel FILE *dfile; /* debug file */
95657a8c20SJan Friedel #endif
96657a8c20SJan Friedel
97657a8c20SJan Friedel /*
98657a8c20SJan Friedel * set_transq_count_max() - sets the transq_count_max value based on kernel
99657a8c20SJan Friedel * audit queue high water mark. This is backup solution for a case, when the
100*bbf21555SRichard Lowe * the default qsize zero value is (intentionally) set in the audit_remote(7)
101f8994074SJan Friedel * plugin configuration.
102657a8c20SJan Friedel */
103657a8c20SJan Friedel static auditd_rc_t
set_transq_count_max()104657a8c20SJan Friedel set_transq_count_max()
105657a8c20SJan Friedel {
106657a8c20SJan Friedel struct au_qctrl qctrl;
107657a8c20SJan Friedel
108657a8c20SJan Friedel if (auditon(A_GETQCTRL, (caddr_t)&qctrl, 0) != -1) {
109657a8c20SJan Friedel transq_count_max = qctrl.aq_hiwater;
110657a8c20SJan Friedel DPRINT((dfile, "Transmission queue max length set to %ld\n",
111657a8c20SJan Friedel transq_count_max));
112657a8c20SJan Friedel return (AUDITD_SUCCESS);
113657a8c20SJan Friedel }
114657a8c20SJan Friedel
115657a8c20SJan Friedel DPRINT((dfile, "Setting the transmission queue max length failed\n"));
116657a8c20SJan Friedel return (AUDITD_RETRY);
117657a8c20SJan Friedel }
118657a8c20SJan Friedel
119657a8c20SJan Friedel /*
120657a8c20SJan Friedel * get_port_default() - set the default port number; note, that "solaris-audit"
121657a8c20SJan Friedel * used below in the code is the IANA assigned service name for the secure
122657a8c20SJan Friedel * remote solaris audit logging.
123657a8c20SJan Friedel */
124657a8c20SJan Friedel static auditd_rc_t
get_port_default(int * port_default)125657a8c20SJan Friedel get_port_default(int *port_default)
126657a8c20SJan Friedel {
127657a8c20SJan Friedel
128657a8c20SJan Friedel struct servent serventry;
129657a8c20SJan Friedel char serventry_buf[1024];
130657a8c20SJan Friedel
131657a8c20SJan Friedel if (getservbyname_r("solaris-audit", "tcp", &serventry,
132657a8c20SJan Friedel (char *)&serventry_buf, sizeof (serventry_buf)) == NULL) {
133657a8c20SJan Friedel DPRINT((dfile, "unable to get default port number\n"));
134657a8c20SJan Friedel #if DEBUG
135657a8c20SJan Friedel if (errno == ERANGE) {
136657a8c20SJan Friedel DPRINT((dfile, "low on buffer\n"));
137657a8c20SJan Friedel }
138657a8c20SJan Friedel #endif
139657a8c20SJan Friedel return (AUDITD_INVALID);
140657a8c20SJan Friedel }
141657a8c20SJan Friedel *port_default = ntohs(serventry.s_port);
142657a8c20SJan Friedel DPRINT((dfile, "default port: %d\n", *port_default));
143657a8c20SJan Friedel
144657a8c20SJan Friedel return (AUDITD_SUCCESS);
145657a8c20SJan Friedel }
146657a8c20SJan Friedel
147657a8c20SJan Friedel /*
148657a8c20SJan Friedel * trim_me() - trims the white space characters around the specified string.
149657a8c20SJan Friedel * Inputs - pointer to the beginning of the string (str_ptr); returns - pointer
150657a8c20SJan Friedel * to the trimmed string. Function returns NULL pointer in case of received
151657a8c20SJan Friedel * empty string, NULL pointer or in case the pointed string consists of white
152657a8c20SJan Friedel * space characters only.
153657a8c20SJan Friedel */
154657a8c20SJan Friedel static char *
trim_me(char * str_ptr)155657a8c20SJan Friedel trim_me(char *str_ptr) {
156657a8c20SJan Friedel
157657a8c20SJan Friedel char *str_end;
158657a8c20SJan Friedel
159657a8c20SJan Friedel if (str_ptr == NULL || *str_ptr == '\0') {
160657a8c20SJan Friedel return (NULL);
161657a8c20SJan Friedel }
162657a8c20SJan Friedel
163657a8c20SJan Friedel while (isspace(*str_ptr)) {
164657a8c20SJan Friedel str_ptr++;
165657a8c20SJan Friedel }
166657a8c20SJan Friedel if (*str_ptr == '\0') {
167657a8c20SJan Friedel return (NULL);
168657a8c20SJan Friedel }
169657a8c20SJan Friedel
170657a8c20SJan Friedel str_end = str_ptr + strlen(str_ptr);
171657a8c20SJan Friedel
172657a8c20SJan Friedel while (str_end > str_ptr && isspace(str_end[-1])) {
173657a8c20SJan Friedel str_end--;
174657a8c20SJan Friedel }
175657a8c20SJan Friedel *str_end = '\0';
176657a8c20SJan Friedel
177657a8c20SJan Friedel return (str_ptr);
178657a8c20SJan Friedel }
179657a8c20SJan Friedel
180f8994074SJan Friedel /*
181f8994074SJan Friedel * Frees host list - should be called while keeping auditd_mutex.
182f8994074SJan Friedel */
183f8994074SJan Friedel static void
freehostlist(hostlist_t ** hostlist_ptr)184f8994074SJan Friedel freehostlist(hostlist_t **hostlist_ptr)
185f8994074SJan Friedel {
186f8994074SJan Friedel hostlist_t *h, *n;
187f8994074SJan Friedel
188f8994074SJan Friedel h = *hostlist_ptr;
189f8994074SJan Friedel
190f8994074SJan Friedel while (h != NULL) {
191f8994074SJan Friedel n = h->next_host;
192f8994074SJan Friedel freehostent(h->host);
193f8994074SJan Friedel free(h);
194f8994074SJan Friedel h = n;
195f8994074SJan Friedel }
196f8994074SJan Friedel *hostlist_ptr = NULL;
197f8994074SJan Friedel }
198657a8c20SJan Friedel
199657a8c20SJan Friedel /*
200657a8c20SJan Friedel * parsehosts() end parses the host string (hosts_str)
201657a8c20SJan Friedel */
202657a8c20SJan Friedel static auditd_rc_t
parsehosts(char * hosts_str,char ** error)203657a8c20SJan Friedel parsehosts(char *hosts_str, char **error)
204657a8c20SJan Friedel {
205657a8c20SJan Friedel char *hostportmech, *hpm;
206657a8c20SJan Friedel char *hostname;
207657a8c20SJan Friedel char *port_str;
208657a8c20SJan Friedel char *mech_str;
209657a8c20SJan Friedel int port;
210657a8c20SJan Friedel int port_default = -1;
211657a8c20SJan Friedel gss_OID mech_oid;
212657a8c20SJan Friedel char *lasts_hpm;
213657a8c20SJan Friedel hostlist_t *lasthost = NULL;
214f8994074SJan Friedel hostlist_t *hosts_new = NULL;
215657a8c20SJan Friedel hostlist_t *newhost;
216657a8c20SJan Friedel struct hostent *hostentry;
217657a8c20SJan Friedel int error_num;
218657a8c20SJan Friedel int rc;
219657a8c20SJan Friedel #if DEBUG
220657a8c20SJan Friedel char addr_buf[INET6_ADDRSTRLEN];
221657a8c20SJan Friedel int num_of_hosts = 0;
222657a8c20SJan Friedel #endif
223657a8c20SJan Friedel
224657a8c20SJan Friedel DPRINT((dfile, "parsing %s\n", hosts_str));
225657a8c20SJan Friedel while ((hostportmech = strtok_r(hosts_str, ",", &lasts_hpm)) != NULL) {
226657a8c20SJan Friedel
227657a8c20SJan Friedel hosts_str = NULL;
228657a8c20SJan Friedel hostname = NULL;
229657a8c20SJan Friedel port_str = NULL;
230657a8c20SJan Friedel port = port_default;
231657a8c20SJan Friedel mech_str = NULL;
232657a8c20SJan Friedel mech_oid = GSS_C_NO_OID;
233657a8c20SJan Friedel
234657a8c20SJan Friedel DPRINT((dfile, "parsing host:port:mech %s\n", hostportmech));
235657a8c20SJan Friedel
236657a8c20SJan Friedel if (strncmp(hostportmech, ":", 1 == 0)) { /* ":port:" case */
237657a8c20SJan Friedel *error = strdup(gettext("no hostname specified"));
238657a8c20SJan Friedel return (AUDITD_INVALID);
239657a8c20SJan Friedel }
240657a8c20SJan Friedel
241657a8c20SJan Friedel /* parse single host:port:mech target */
242657a8c20SJan Friedel while ((hpm = strsep(&hostportmech, ":")) != NULL) {
243657a8c20SJan Friedel
244657a8c20SJan Friedel if (hostname == NULL) {
245657a8c20SJan Friedel hostname = hpm;
246657a8c20SJan Friedel continue;
247657a8c20SJan Friedel }
248657a8c20SJan Friedel if (port_str == NULL) {
249657a8c20SJan Friedel port_str = hpm;
250657a8c20SJan Friedel continue;
251657a8c20SJan Friedel }
252657a8c20SJan Friedel if (mech_str == NULL) {
253657a8c20SJan Friedel mech_str = hpm;
254657a8c20SJan Friedel continue;
255657a8c20SJan Friedel }
256657a8c20SJan Friedel
257657a8c20SJan Friedel /* too many colons in the hostportmech string */
258657a8c20SJan Friedel *error = strdup(gettext("invalid host:port:mech "
259657a8c20SJan Friedel "specification"));
260657a8c20SJan Friedel return (AUDITD_INVALID);
261657a8c20SJan Friedel }
262657a8c20SJan Friedel
263657a8c20SJan Friedel if (hostname == NULL || *hostname == '\0') {
264657a8c20SJan Friedel *error = strdup(gettext("invalid hostname "
265657a8c20SJan Friedel "specification"));
266657a8c20SJan Friedel return (AUDITD_INVALID);
267657a8c20SJan Friedel }
268657a8c20SJan Friedel
269657a8c20SJan Friedel /* trim hostname */
270657a8c20SJan Friedel hostname = trim_me(hostname);
271657a8c20SJan Friedel if (hostname == NULL || *hostname == '\0') {
272657a8c20SJan Friedel *error = strdup(gettext("empty hostname "
273657a8c20SJan Friedel "specification"));
274657a8c20SJan Friedel return (AUDITD_INVALID);
275657a8c20SJan Friedel }
276657a8c20SJan Friedel
277657a8c20SJan Friedel DPRINT((dfile, "resolving address for %s\n", hostname));
278657a8c20SJan Friedel
279657a8c20SJan Friedel hostentry = getipnodebyname(hostname, AF_INET6, 0, &error_num);
280657a8c20SJan Friedel if (!hostentry) {
281657a8c20SJan Friedel hostentry = getipnodebyname(hostname, AF_INET, 0,
282657a8c20SJan Friedel &error_num);
283657a8c20SJan Friedel }
284657a8c20SJan Friedel if (!hostentry) {
285657a8c20SJan Friedel if (error_num == TRY_AGAIN) {
286657a8c20SJan Friedel *error = strdup(gettext("host not found, "
287657a8c20SJan Friedel "try later"));
288657a8c20SJan Friedel return (AUDITD_RETRY);
289657a8c20SJan Friedel } else {
290657a8c20SJan Friedel *error = strdup(gettext("host not found"));
291657a8c20SJan Friedel return (AUDITD_INVALID);
292657a8c20SJan Friedel }
293657a8c20SJan Friedel }
294657a8c20SJan Friedel DPRINT((dfile, "hostentry: h_name=%s, addr_len=%d, addr=%s\n",
295657a8c20SJan Friedel hostentry->h_name, hostentry->h_length,
296657a8c20SJan Friedel inet_ntop(hostentry->h_addrtype,
297657a8c20SJan Friedel hostentry->h_addr_list[0], addr_buf,
298657a8c20SJan Friedel INET6_ADDRSTRLEN)));
299657a8c20SJan Friedel
300657a8c20SJan Friedel /* trim port */
301657a8c20SJan Friedel port_str = trim_me(port_str);
302657a8c20SJan Friedel if (port_str == NULL || *port_str == '\0') {
303657a8c20SJan Friedel if (port_default == -1 &&
304657a8c20SJan Friedel (rc = get_port_default(&port_default))
305657a8c20SJan Friedel != AUDITD_SUCCESS) {
306657a8c20SJan Friedel *error = strdup(gettext(
307657a8c20SJan Friedel "unable to get default port number"));
308657a8c20SJan Friedel return (rc);
309657a8c20SJan Friedel }
310657a8c20SJan Friedel port = port_default;
311657a8c20SJan Friedel DPRINT((dfile, "port: %d (default)\n", port));
312657a8c20SJan Friedel } else {
313657a8c20SJan Friedel errno = 0;
314657a8c20SJan Friedel port = atoi(port_str);
315657a8c20SJan Friedel if (errno != 0 || port < 1 || port > USHRT_MAX) {
316657a8c20SJan Friedel *error = strdup(gettext("invalid port number"));
317657a8c20SJan Friedel return (AUDITD_INVALID);
318657a8c20SJan Friedel }
319657a8c20SJan Friedel DPRINT((dfile, "port: %d\n", port));
320657a8c20SJan Friedel }
321657a8c20SJan Friedel
322657a8c20SJan Friedel /* trim mechanism */
323657a8c20SJan Friedel mech_str = trim_me(mech_str);
324657a8c20SJan Friedel if (mech_str != NULL && *mech_str != '\0') {
325657a8c20SJan Friedel if (rpc_gss_mech_to_oid(mech_str, &mech_oid) != TRUE) {
326657a8c20SJan Friedel *error = strdup(gettext("unknown mechanism"));
327657a8c20SJan Friedel return (AUDITD_INVALID);
328657a8c20SJan Friedel }
329657a8c20SJan Friedel DPRINT((dfile, "mechanism: %s\n", mech_str));
330657a8c20SJan Friedel #if DEBUG
331657a8c20SJan Friedel } else {
332657a8c20SJan Friedel DPRINT((dfile, "mechanism: null (default)\n"));
333657a8c20SJan Friedel #endif
334657a8c20SJan Friedel }
335657a8c20SJan Friedel
336657a8c20SJan Friedel /* add this host to host list */
337657a8c20SJan Friedel newhost = malloc(sizeof (hostlist_t));
338657a8c20SJan Friedel if (newhost == NULL) {
339657a8c20SJan Friedel *error = strdup(gettext("no memory"));
340657a8c20SJan Friedel return (AUDITD_NO_MEMORY);
341657a8c20SJan Friedel }
342657a8c20SJan Friedel newhost->host = hostentry;
343657a8c20SJan Friedel newhost->port = htons(port);
344657a8c20SJan Friedel newhost->mech = mech_oid;
345657a8c20SJan Friedel newhost->next_host = NULL;
346657a8c20SJan Friedel if (lasthost != NULL) {
347657a8c20SJan Friedel lasthost->next_host = newhost;
348657a8c20SJan Friedel lasthost = lasthost->next_host;
349657a8c20SJan Friedel } else {
350657a8c20SJan Friedel lasthost = newhost;
351f8994074SJan Friedel hosts_new = newhost;
352657a8c20SJan Friedel }
353657a8c20SJan Friedel #if DEBUG
354657a8c20SJan Friedel num_of_hosts++;
355657a8c20SJan Friedel #endif
356657a8c20SJan Friedel }
357657a8c20SJan Friedel
358f8994074SJan Friedel (void) pthread_mutex_lock(&plugin_mutex);
359f8994074SJan Friedel if (hosts_prev == NULL) {
360f8994074SJan Friedel hosts_prev = hosts;
361f8994074SJan Friedel }
362f8994074SJan Friedel hosts = hosts_new;
363657a8c20SJan Friedel current_host = hosts;
364f8994074SJan Friedel (void) pthread_mutex_unlock(&plugin_mutex);
365f8994074SJan Friedel
366657a8c20SJan Friedel DPRINT((dfile, "Configured %d hosts.\n", num_of_hosts));
367657a8c20SJan Friedel
368657a8c20SJan Friedel return (AUDITD_SUCCESS);
369657a8c20SJan Friedel }
370657a8c20SJan Friedel
371657a8c20SJan Friedel
372657a8c20SJan Friedel #if DEBUG
373657a8c20SJan Friedel static char *
auditd_message(auditd_rc_t msg_code)374657a8c20SJan Friedel auditd_message(auditd_rc_t msg_code) {
375657a8c20SJan Friedel char *rc_msg;
376657a8c20SJan Friedel
377657a8c20SJan Friedel switch (msg_code) {
378657a8c20SJan Friedel case AUDITD_SUCCESS:
379657a8c20SJan Friedel rc_msg = strdup("ok");
380657a8c20SJan Friedel break;
381657a8c20SJan Friedel case AUDITD_RETRY:
382657a8c20SJan Friedel rc_msg = strdup("retry after a delay");
383657a8c20SJan Friedel break;
384657a8c20SJan Friedel case AUDITD_NO_MEMORY:
385657a8c20SJan Friedel rc_msg = strdup("can't allocate memory");
386657a8c20SJan Friedel break;
387657a8c20SJan Friedel case AUDITD_INVALID:
388657a8c20SJan Friedel rc_msg = strdup("bad input");
389657a8c20SJan Friedel break;
390657a8c20SJan Friedel case AUDITD_COMM_FAIL:
391657a8c20SJan Friedel rc_msg = strdup("communications failure");
392657a8c20SJan Friedel break;
393657a8c20SJan Friedel case AUDITD_FATAL:
394657a8c20SJan Friedel rc_msg = strdup("other error");
395657a8c20SJan Friedel break;
396657a8c20SJan Friedel case AUDITD_FAIL:
397657a8c20SJan Friedel rc_msg = strdup("other non-fatal error");
398657a8c20SJan Friedel break;
399657a8c20SJan Friedel }
400657a8c20SJan Friedel return (rc_msg);
401657a8c20SJan Friedel }
402657a8c20SJan Friedel #endif
403657a8c20SJan Friedel
404657a8c20SJan Friedel /*
405657a8c20SJan Friedel * rsn_to_msg() - translation of the reason of closure identifier to the more
406657a8c20SJan Friedel * human readable/understandable form.
407657a8c20SJan Friedel */
408657a8c20SJan Friedel static char *
rsn_to_msg(close_rsn_t reason)409657a8c20SJan Friedel rsn_to_msg(close_rsn_t reason)
410657a8c20SJan Friedel {
411657a8c20SJan Friedel char *rc_msg;
412657a8c20SJan Friedel
413657a8c20SJan Friedel switch (reason) {
414657a8c20SJan Friedel case RSN_UNDEFINED:
415657a8c20SJan Friedel rc_msg = strdup(gettext("not defined reason of failure"));
416657a8c20SJan Friedel break;
417657a8c20SJan Friedel case RSN_INIT_POLL:
418657a8c20SJan Friedel rc_msg = strdup(gettext("poll() initialization failed"));
419657a8c20SJan Friedel break;
420657a8c20SJan Friedel case RSN_TOK_RECV_FAILED:
421657a8c20SJan Friedel rc_msg = strdup(gettext("token receiving failed"));
422657a8c20SJan Friedel break;
423657a8c20SJan Friedel case RSN_TOK_TOO_BIG:
424657a8c20SJan Friedel rc_msg = strdup(gettext("unacceptable token size"));
425657a8c20SJan Friedel break;
426657a8c20SJan Friedel case RSN_TOK_UNVERIFIABLE:
427657a8c20SJan Friedel rc_msg = strdup(gettext("received unverifiable token"));
428657a8c20SJan Friedel break;
429657a8c20SJan Friedel case RSN_SOCKET_CLOSE:
430657a8c20SJan Friedel rc_msg = strdup(gettext("closed socket"));
431657a8c20SJan Friedel break;
432657a8c20SJan Friedel case RSN_SOCKET_CREATE:
433657a8c20SJan Friedel rc_msg = strdup(gettext("socket creation failed"));
434657a8c20SJan Friedel break;
435657a8c20SJan Friedel case RSN_CONNECTION_CREATE:
436657a8c20SJan Friedel rc_msg = strdup(gettext("connection creation failed"));
437657a8c20SJan Friedel break;
438657a8c20SJan Friedel case RSN_PROTOCOL_NEGOTIATE:
439657a8c20SJan Friedel rc_msg = strdup(gettext("protocol negotiation failed"));
440657a8c20SJan Friedel break;
441657a8c20SJan Friedel case RSN_GSS_CTX_ESTABLISH:
442657a8c20SJan Friedel rc_msg = strdup(gettext("context establishing failed"));
443657a8c20SJan Friedel break;
444657a8c20SJan Friedel case RSN_GSS_CTX_EXP:
445657a8c20SJan Friedel rc_msg = strdup(gettext("context expired"));
446657a8c20SJan Friedel break;
447657a8c20SJan Friedel case RSN_UNKNOWN_AF:
448657a8c20SJan Friedel rc_msg = strdup(gettext("unknown address family"));
449657a8c20SJan Friedel break;
450657a8c20SJan Friedel case RSN_MEMORY_ALLOCATE:
451657a8c20SJan Friedel rc_msg = strdup(gettext("memory allocation failed"));
452657a8c20SJan Friedel break;
453657a8c20SJan Friedel default: /* RSN_OTHER_ERR */
454657a8c20SJan Friedel rc_msg = strdup(gettext("other, not classified error"));
455657a8c20SJan Friedel break;
456657a8c20SJan Friedel }
457657a8c20SJan Friedel return (rc_msg);
458657a8c20SJan Friedel }
459657a8c20SJan Friedel
460657a8c20SJan Friedel /*
461657a8c20SJan Friedel * set_fdfl() - based on set_fl (FL_SET/FL_UNSET) un/sets the fl flag associated
462657a8c20SJan Friedel * with fd file descriptor.
463657a8c20SJan Friedel */
464657a8c20SJan Friedel static boolean_t
set_fdfl(int fd,int fl,boolean_t set_fl)465657a8c20SJan Friedel set_fdfl(int fd, int fl, boolean_t set_fl)
466657a8c20SJan Friedel {
467657a8c20SJan Friedel int flags;
468657a8c20SJan Friedel
469657a8c20SJan Friedel /* power of two test - only single bit flags are allowed */
470657a8c20SJan Friedel if (!fl || (fl & (fl-1))) {
471657a8c20SJan Friedel DPRINT((dfile, "incorrect flag - %d isn't power of two\n", fl));
472657a8c20SJan Friedel return (B_FALSE);
473657a8c20SJan Friedel }
474657a8c20SJan Friedel
475657a8c20SJan Friedel if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
476657a8c20SJan Friedel DPRINT((dfile, "cannot get file descriptor flags\n"));
477657a8c20SJan Friedel return (B_FALSE);
478657a8c20SJan Friedel }
479657a8c20SJan Friedel
480657a8c20SJan Friedel if (set_fl) { /* set the fl flag */
481657a8c20SJan Friedel if (flags & fl) {
482657a8c20SJan Friedel return (B_TRUE);
483657a8c20SJan Friedel }
484657a8c20SJan Friedel
485657a8c20SJan Friedel flags |= fl;
486657a8c20SJan Friedel
487657a8c20SJan Friedel } else { /* unset the fl flag */
488657a8c20SJan Friedel if (~flags & fl) {
489657a8c20SJan Friedel return (B_TRUE);
490657a8c20SJan Friedel }
491657a8c20SJan Friedel
492657a8c20SJan Friedel flags &= ~fl;
493657a8c20SJan Friedel }
494657a8c20SJan Friedel
495657a8c20SJan Friedel if (fcntl(fd, F_SETFL, flags) == -1) {
496657a8c20SJan Friedel DPRINT((dfile, "cannot %s file descriptor flags\n",
497657a8c20SJan Friedel (set_fl ? "set" : "unset")));
498657a8c20SJan Friedel return (B_FALSE);
499657a8c20SJan Friedel }
500657a8c20SJan Friedel
501657a8c20SJan Friedel DPRINT((dfile, "fd: %d - flag: 0%o was %s\n", fd, fl,
502657a8c20SJan Friedel (set_fl ? "set" : "unset")));
503657a8c20SJan Friedel return (B_TRUE);
504657a8c20SJan Friedel }
505657a8c20SJan Friedel
506657a8c20SJan Friedel
507657a8c20SJan Friedel /*
508657a8c20SJan Friedel * create_notify_pipe() - creates the notification pipe. Function returns
509657a8c20SJan Friedel * B_TRUE/B_FALSE on success/failure.
510657a8c20SJan Friedel */
511657a8c20SJan Friedel static boolean_t
create_notify_pipe(int * notify_pipe,char ** error)512657a8c20SJan Friedel create_notify_pipe(int *notify_pipe, char **error)
513657a8c20SJan Friedel {
514657a8c20SJan Friedel
515657a8c20SJan Friedel if (pipe(notify_pipe) < 0) {
516657a8c20SJan Friedel DPRINT((dfile, "Cannot create notify pipe: %s\n",
517657a8c20SJan Friedel strerror(errno)));
518657a8c20SJan Friedel *error = strdup(gettext("failed to create notification pipe"));
519657a8c20SJan Friedel return (B_FALSE);
520657a8c20SJan Friedel } else {
521657a8c20SJan Friedel DPRINT((dfile, "Pipe created in:%d out:%d\n", notify_pipe[0],
522657a8c20SJan Friedel notify_pipe[1]));
523657a8c20SJan Friedel /* make (only) the pipe "in" end nonblocking */
524657a8c20SJan Friedel if (!set_fdfl(notify_pipe[0], O_NONBLOCK, FL_UNSET) ||
525657a8c20SJan Friedel !set_fdfl(notify_pipe[1], O_NONBLOCK, FL_SET)) {
526657a8c20SJan Friedel DPRINT((dfile, "Cannot prepare blocking scheme on top "
527657a8c20SJan Friedel "of the notification pipe: %s\n", strerror(errno)));
528657a8c20SJan Friedel (void) close(notify_pipe[0]);
529657a8c20SJan Friedel (void) close(notify_pipe[1]);
530657a8c20SJan Friedel
531657a8c20SJan Friedel *error = strdup(gettext("failed to prepare blocking "
532657a8c20SJan Friedel "scheme on top of the notification pipe"));
533657a8c20SJan Friedel return (B_FALSE);
534657a8c20SJan Friedel }
535657a8c20SJan Friedel }
536657a8c20SJan Friedel
537657a8c20SJan Friedel return (B_TRUE);
538657a8c20SJan Friedel }
539657a8c20SJan Friedel
540657a8c20SJan Friedel
541657a8c20SJan Friedel /*
542657a8c20SJan Friedel * auditd_plugin() sends a record via a tcp connection.
543657a8c20SJan Friedel *
544657a8c20SJan Friedel * Operation:
545657a8c20SJan Friedel * - 1 tcp connection opened at a time, referenced by current_host->sockfd
546657a8c20SJan Friedel * - tries to (open and) send a record to the current_host where its address
547657a8c20SJan Friedel * is taken from the first hostent h_addr_list entry
548657a8c20SJan Friedel * - if connection times out, tries second host
549657a8c20SJan Friedel * - if all hosts where tried tries again for retries number of times
550657a8c20SJan Friedel * - if everything fails, it bails out with AUDITD_RETRY
551657a8c20SJan Friedel *
552657a8c20SJan Friedel * Note, that space on stack allocated for any error message returned along
553657a8c20SJan Friedel * with AUDITD_RETRY is subsequently freed by auditd.
554657a8c20SJan Friedel *
555657a8c20SJan Friedel */
556657a8c20SJan Friedel auditd_rc_t
auditd_plugin(const char * input,size_t in_len,uint64_t sequence,char ** error)5578f775e0aSJan Friedel auditd_plugin(const char *input, size_t in_len, uint64_t sequence, char **error)
558657a8c20SJan Friedel {
559657a8c20SJan Friedel int rc = AUDITD_FAIL;
560657a8c20SJan Friedel int send_record_rc = SEND_RECORD_FAIL;
561657a8c20SJan Friedel hostlist_t *start_host;
562657a8c20SJan Friedel int attempts = 0;
563657a8c20SJan Friedel char *ext_error; /* extended error string */
564657a8c20SJan Friedel close_rsn_t err_rsn = RSN_UNDEFINED;
565657a8c20SJan Friedel char *rsn_msg;
566657a8c20SJan Friedel
567657a8c20SJan Friedel #if DEBUG
568657a8c20SJan Friedel char *rc_msg;
5698f775e0aSJan Friedel static uint64_t last_sequence = 0;
570657a8c20SJan Friedel
571657a8c20SJan Friedel if ((last_sequence > 0) && (sequence != last_sequence + 1)) {
5728f775e0aSJan Friedel DPRINT((dfile, "audit_remote: buffer sequence=%llu "
5738f775e0aSJan Friedel "but prev=%llu\n", sequence, last_sequence));
574657a8c20SJan Friedel }
575657a8c20SJan Friedel last_sequence = sequence;
576657a8c20SJan Friedel
5778f775e0aSJan Friedel DPRINT((dfile, "audit_remote: input seq=%llu, len=%d\n",
578657a8c20SJan Friedel sequence, in_len));
579657a8c20SJan Friedel #endif
580657a8c20SJan Friedel
581657a8c20SJan Friedel (void) pthread_mutex_lock(&transq_lock);
582657a8c20SJan Friedel
583657a8c20SJan Friedel if (transq_hdr.count == transq_count_max) {
584657a8c20SJan Friedel DPRINT((dfile, "Transmission queue is full (%ld)\n",
585657a8c20SJan Friedel transq_hdr.count));
586657a8c20SJan Friedel (void) pthread_mutex_unlock(&transq_lock);
587657a8c20SJan Friedel *error = strdup(gettext("retransmission queue is full"));
588657a8c20SJan Friedel return (AUDITD_RETRY);
589657a8c20SJan Friedel }
590657a8c20SJan Friedel (void) pthread_mutex_unlock(&transq_lock);
591657a8c20SJan Friedel
592657a8c20SJan Friedel
593657a8c20SJan Friedel (void) pthread_mutex_lock(&plugin_mutex);
594657a8c20SJan Friedel
595657a8c20SJan Friedel /* cycle over the hosts and possibly deliver the record */
596657a8c20SJan Friedel start_host = current_host;
597657a8c20SJan Friedel while (rc != AUDITD_SUCCESS) {
598657a8c20SJan Friedel DPRINT((dfile, "Trying to send record to %s [attempt:%d/%d]\n",
599657a8c20SJan Friedel current_host->host->h_name, attempts + 1, retries));
600657a8c20SJan Friedel
601657a8c20SJan Friedel send_record_rc = send_record(current_host, input, in_len,
6028f775e0aSJan Friedel sequence, &err_rsn);
603657a8c20SJan Friedel DPRINT((dfile, "send_record() returned %d - ", send_record_rc));
604657a8c20SJan Friedel
605657a8c20SJan Friedel switch (send_record_rc) {
606657a8c20SJan Friedel case SEND_RECORD_SUCCESS:
607657a8c20SJan Friedel DPRINT((dfile, "success\n"));
608657a8c20SJan Friedel nosuccess_cnt = 0;
609657a8c20SJan Friedel rc = AUDITD_SUCCESS;
610f8994074SJan Friedel if (hosts_prev != NULL) {
611f8994074SJan Friedel freehostlist(&hosts_prev);
612f8994074SJan Friedel DPRINT((dfile, "stale host list freed\n"));
613f8994074SJan Friedel }
614657a8c20SJan Friedel break;
615657a8c20SJan Friedel case SEND_RECORD_NEXT:
616f8994074SJan Friedel DPRINT((dfile, "retry the same host: %s (penalty) "
617f8994074SJan Friedel "rsn:%d\n", current_host->host->h_name, err_rsn));
618657a8c20SJan Friedel attempts++;
619657a8c20SJan Friedel break;
620657a8c20SJan Friedel case SEND_RECORD_RETRY:
621f8994074SJan Friedel DPRINT((dfile, "retry the same host: %s (no penalty) "
622f8994074SJan Friedel "rsn:%d\n", current_host->host->h_name, err_rsn));
623657a8c20SJan Friedel break;
624657a8c20SJan Friedel }
625657a8c20SJan Friedel
626657a8c20SJan Friedel if (send_record_rc == SEND_RECORD_NEXT) {
627657a8c20SJan Friedel
628657a8c20SJan Friedel /* warn about unsuccessful auditd record delivery */
629657a8c20SJan Friedel rsn_msg = rsn_to_msg(err_rsn);
630657a8c20SJan Friedel (void) asprintf(&ext_error,
631657a8c20SJan Friedel "retry %d connection %s:%d %s", attempts + 1,
632657a8c20SJan Friedel current_host->host->h_name,
633657a8c20SJan Friedel ntohs(current_host->port), rsn_msg);
634657a8c20SJan Friedel if (ext_error == NULL) {
635657a8c20SJan Friedel free(rsn_msg);
636657a8c20SJan Friedel *error = strdup(gettext("no memory"));
637657a8c20SJan Friedel rc = AUDITD_NO_MEMORY;
638657a8c20SJan Friedel break;
639657a8c20SJan Friedel }
6409fa473b0SJan Friedel __audit_dowarn2("plugin", "audit_remote.so", "retry",
641657a8c20SJan Friedel ext_error, attempts + 1);
642657a8c20SJan Friedel free(rsn_msg);
643657a8c20SJan Friedel free(ext_error);
644657a8c20SJan Friedel
645657a8c20SJan Friedel if (attempts < retries) {
646657a8c20SJan Friedel /* semi-exponential timeout back off */
647657a8c20SJan Friedel timeout = BOFF_TIMEOUT(attempts, timeout);
648657a8c20SJan Friedel DPRINT((dfile, "New timeout=%d\n", timeout));
649657a8c20SJan Friedel } else {
650657a8c20SJan Friedel /* get next host */
651657a8c20SJan Friedel current_host = current_host->next_host;
652657a8c20SJan Friedel if (current_host == NULL) {
653657a8c20SJan Friedel current_host = hosts;
654657a8c20SJan Friedel }
655f8994074SJan Friedel timeout = timeout_p_timeout;
656657a8c20SJan Friedel DPRINT((dfile, "New timeout=%d\n", timeout));
657657a8c20SJan Friedel attempts = 0;
658657a8c20SJan Friedel }
659657a8c20SJan Friedel
660657a8c20SJan Friedel /* one cycle finished */
661657a8c20SJan Friedel if (current_host == start_host && attempts == 0) {
662657a8c20SJan Friedel nosuccess_cnt++;
663657a8c20SJan Friedel (void) asprintf(&ext_error, "all hosts defined "
664657a8c20SJan Friedel "as p_hosts were tried to deliver "
665657a8c20SJan Friedel "the audit record to with no success "
666657a8c20SJan Friedel "- sleeping for %d seconds",
667657a8c20SJan Friedel NOSUCCESS_DELAY);
668657a8c20SJan Friedel if (ext_error == NULL) {
669657a8c20SJan Friedel *error = strdup(gettext("no memory"));
670657a8c20SJan Friedel rc = AUDITD_NO_MEMORY;
671657a8c20SJan Friedel break;
672657a8c20SJan Friedel }
673657a8c20SJan Friedel __audit_dowarn2("plugin", "audit_remote.so",
6749fa473b0SJan Friedel "retry", ext_error, nosuccess_cnt);
675657a8c20SJan Friedel free(ext_error);
676657a8c20SJan Friedel (void) sleep(NOSUCCESS_DELAY);
677657a8c20SJan Friedel }
678657a8c20SJan Friedel
679657a8c20SJan Friedel } /* if (send_record_rc == SEND_RECORD_NEXT) */
680657a8c20SJan Friedel
681657a8c20SJan Friedel err_rsn = RSN_UNDEFINED;
682657a8c20SJan Friedel
683657a8c20SJan Friedel } /* while (rc != AUDITD_SUCCESS) */
684657a8c20SJan Friedel
685657a8c20SJan Friedel (void) pthread_mutex_unlock(&plugin_mutex);
686657a8c20SJan Friedel
687657a8c20SJan Friedel #if DEBUG
688657a8c20SJan Friedel rc_msg = auditd_message(rc);
689657a8c20SJan Friedel DPRINT((dfile, "audit_remote: returning: %s\n", rc_msg));
690657a8c20SJan Friedel free(rc_msg);
691657a8c20SJan Friedel #endif
692657a8c20SJan Friedel
693657a8c20SJan Friedel return (rc);
694657a8c20SJan Friedel }
695657a8c20SJan Friedel
696657a8c20SJan Friedel /*
697657a8c20SJan Friedel * auditd_plugin_open() may be called multiple times; on initial open or
698657a8c20SJan Friedel * `audit -s`, then kvlist != NULL; on `audit -n`, then kvlist == NULL.
699*bbf21555SRichard Lowe * For more information see audit(8).
700657a8c20SJan Friedel *
701657a8c20SJan Friedel * Note, that space on stack allocated for any error message returned along
702657a8c20SJan Friedel * with AUDITD_RETRY is subsequently freed by auditd.
703657a8c20SJan Friedel *
704657a8c20SJan Friedel */
705657a8c20SJan Friedel auditd_rc_t
auditd_plugin_open(const kva_t * kvlist,char ** ret_list,char ** error)706657a8c20SJan Friedel auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error)
707657a8c20SJan Friedel {
708657a8c20SJan Friedel kva_t *kv;
709657a8c20SJan Friedel char *val_str;
710657a8c20SJan Friedel int val;
711657a8c20SJan Friedel long val_l;
712657a8c20SJan Friedel int rc = 0;
713657a8c20SJan Friedel
714657a8c20SJan Friedel *error = NULL;
715657a8c20SJan Friedel *ret_list = NULL;
716657a8c20SJan Friedel kv = (kva_t *)kvlist;
717657a8c20SJan Friedel
718657a8c20SJan Friedel #if DEBUG
719657a8c20SJan Friedel dfile = __auditd_debug_file_open();
720657a8c20SJan Friedel #endif
721657a8c20SJan Friedel
722657a8c20SJan Friedel /* initial open or audit -s */
723657a8c20SJan Friedel if (kvlist != NULL) {
724657a8c20SJan Friedel DPRINT((dfile, "Action: initial open or `audit -s`\n"));
725657a8c20SJan Friedel val_str = kva_match(kv, "p_timeout");
726f8994074SJan Friedel if (val_str == NULL) {
727f8994074SJan Friedel *error = strdup(
728f8994074SJan Friedel gettext("p_timeout attribute not found"));
729f8994074SJan Friedel return (AUDITD_RETRY);
730f8994074SJan Friedel }
731657a8c20SJan Friedel DPRINT((dfile, "val_str=%s\n", val_str));
732657a8c20SJan Friedel errno = 0;
733657a8c20SJan Friedel val = atoi(val_str);
734657a8c20SJan Friedel if (errno == 0 && val >= 1) {
735657a8c20SJan Friedel timeout_p_timeout = val;
736657a8c20SJan Friedel timeout = val;
737f8994074SJan Friedel } else {
738f8994074SJan Friedel timeout_p_timeout = DEFAULT_TIMEOUT;
739f8994074SJan Friedel timeout = timeout_p_timeout;
740f8994074SJan Friedel DPRINT((dfile, "p_timeout set to default value: %d\n",
741f8994074SJan Friedel timeout));
742657a8c20SJan Friedel }
743657a8c20SJan Friedel
744657a8c20SJan Friedel val_str = kva_match(kv, "p_retries");
745f8994074SJan Friedel if (val_str == NULL) {
746f8994074SJan Friedel *error = strdup(
747f8994074SJan Friedel gettext("p_retries attribute not found"));
748f8994074SJan Friedel return (AUDITD_RETRY);
749f8994074SJan Friedel }
750657a8c20SJan Friedel DPRINT((dfile, "val_str=%s\n", val_str));
751657a8c20SJan Friedel errno = 0;
752657a8c20SJan Friedel val = atoi(val_str);
753657a8c20SJan Friedel if (errno == 0 && val >= 0) {
754657a8c20SJan Friedel retries = val;
755657a8c20SJan Friedel }
756657a8c20SJan Friedel
757657a8c20SJan Friedel val_str = kva_match(kv, "qsize");
758f8994074SJan Friedel if (val_str == NULL) {
759f8994074SJan Friedel *error = strdup(gettext("qsize attribute not found"));
760f8994074SJan Friedel return (AUDITD_RETRY);
761f8994074SJan Friedel }
762657a8c20SJan Friedel DPRINT((dfile, "qsize=%s\n", val_str));
763657a8c20SJan Friedel errno = 0;
764657a8c20SJan Friedel val_l = atol(val_str);
765f8994074SJan Friedel if (errno == 0 && val_l >= 0) {
766657a8c20SJan Friedel transq_count_max = val_l;
767657a8c20SJan Friedel }
768f8994074SJan Friedel if (transq_count_max == 0 &&
769f8994074SJan Friedel (rc = set_transq_count_max()) != AUDITD_SUCCESS) {
770657a8c20SJan Friedel *error = strdup(gettext("cannot get kernel "
771657a8c20SJan Friedel "auditd queue high water mark\n"));
772657a8c20SJan Friedel return (rc);
773657a8c20SJan Friedel }
774657a8c20SJan Friedel DPRINT((dfile, "timeout=%d, retries=%d, transq_count_max=%ld\n",
775657a8c20SJan Friedel timeout, retries, transq_count_max));
776657a8c20SJan Friedel
777657a8c20SJan Friedel val_str = kva_match(kv, "p_hosts");
778657a8c20SJan Friedel if (val_str == NULL) {
779657a8c20SJan Friedel *error = strdup(gettext("no hosts configured"));
780657a8c20SJan Friedel return (AUDITD_RETRY);
781657a8c20SJan Friedel }
782657a8c20SJan Friedel if ((rc = parsehosts(val_str, error)) != AUDITD_SUCCESS) {
783657a8c20SJan Friedel return (rc);
784657a8c20SJan Friedel }
785657a8c20SJan Friedel
786657a8c20SJan Friedel /* create the notification pipe towards the receiving thread */
787657a8c20SJan Friedel if (!notify_pipe_ready) {
788657a8c20SJan Friedel if (create_notify_pipe(notify_pipe, error)) {
789657a8c20SJan Friedel notify_pipe_ready = B_TRUE;
790657a8c20SJan Friedel } else {
791657a8c20SJan Friedel return (AUDITD_RETRY);
792657a8c20SJan Friedel }
793657a8c20SJan Friedel }
794657a8c20SJan Friedel
795657a8c20SJan Friedel #if DEBUG
796657a8c20SJan Friedel } else { /* audit -n */
797657a8c20SJan Friedel DPRINT((dfile, "Action: `audit -n`\n"));
798657a8c20SJan Friedel #endif
799657a8c20SJan Friedel }
800657a8c20SJan Friedel
801657a8c20SJan Friedel return (AUDITD_SUCCESS);
802657a8c20SJan Friedel }
803657a8c20SJan Friedel
804657a8c20SJan Friedel /*
805657a8c20SJan Friedel * auditd_plugin_close() performs shutdown operations. The return values are
806*bbf21555SRichard Lowe * used by auditd to output warnings via the audit_warn(8) script and the
807657a8c20SJan Friedel * string returned via "error_text", is passed to audit_warn.
808657a8c20SJan Friedel *
809657a8c20SJan Friedel * Note, that space on stack allocated for any error message returned along
810657a8c20SJan Friedel * with AUDITD_RETRY is subsequently freed by auditd.
811657a8c20SJan Friedel *
812657a8c20SJan Friedel */
813657a8c20SJan Friedel auditd_rc_t
auditd_plugin_close(char ** error)814657a8c20SJan Friedel auditd_plugin_close(char **error)
815657a8c20SJan Friedel {
816657a8c20SJan Friedel reset_transport(DO_EXIT, DO_SYNC);
817657a8c20SJan Friedel if (pthread_join(recv_tid, NULL) != 0) {
818657a8c20SJan Friedel *error = strdup(gettext("unable to close receiving thread"));
819657a8c20SJan Friedel return (AUDITD_RETRY);
820657a8c20SJan Friedel }
821657a8c20SJan Friedel
822f8994074SJan Friedel (void) pthread_mutex_lock(&plugin_mutex);
823f8994074SJan Friedel freehostlist(&hosts);
824f8994074SJan Friedel freehostlist(&hosts_prev);
825f8994074SJan Friedel (void) pthread_mutex_unlock(&plugin_mutex);
826f8994074SJan Friedel current_host = NULL;
827657a8c20SJan Friedel *error = NULL;
828657a8c20SJan Friedel return (AUDITD_SUCCESS);
829657a8c20SJan Friedel }
830