xref: /illumos-gate/usr/src/lib/auditd_plugins/remote/audit_remote.c (revision bbf215553c7233fbab8a0afdf1fac74c44781867)
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