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 70*f8994074SJan Friedel static int retries; /* connection retries */ 71*f8994074SJan Friedel int timeout; /* connection timeout */ 72*f8994074SJan 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; 82*f8994074SJan 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*f8994074SJan Friedel * the default qsize zero value is (intentionally) set in the audit_remote(5) 101*f8994074SJan Friedel * plugin configuration. 102657a8c20SJan Friedel */ 103657a8c20SJan Friedel static auditd_rc_t 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 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 * 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 180*f8994074SJan Friedel /* 181*f8994074SJan Friedel * Frees host list - should be called while keeping auditd_mutex. 182*f8994074SJan Friedel */ 183*f8994074SJan Friedel static void 184*f8994074SJan Friedel freehostlist(hostlist_t **hostlist_ptr) 185*f8994074SJan Friedel { 186*f8994074SJan Friedel hostlist_t *h, *n; 187*f8994074SJan Friedel 188*f8994074SJan Friedel h = *hostlist_ptr; 189*f8994074SJan Friedel 190*f8994074SJan Friedel while (h != NULL) { 191*f8994074SJan Friedel n = h->next_host; 192*f8994074SJan Friedel freehostent(h->host); 193*f8994074SJan Friedel free(h); 194*f8994074SJan Friedel h = n; 195*f8994074SJan Friedel } 196*f8994074SJan Friedel *hostlist_ptr = NULL; 197*f8994074SJan Friedel } 198657a8c20SJan Friedel 199657a8c20SJan Friedel /* 200657a8c20SJan Friedel * parsehosts() end parses the host string (hosts_str) 201657a8c20SJan Friedel */ 202657a8c20SJan Friedel static auditd_rc_t 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; 214*f8994074SJan 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; 351*f8994074SJan Friedel hosts_new = newhost; 352657a8c20SJan Friedel } 353657a8c20SJan Friedel #if DEBUG 354657a8c20SJan Friedel num_of_hosts++; 355657a8c20SJan Friedel #endif 356657a8c20SJan Friedel } 357657a8c20SJan Friedel 358*f8994074SJan Friedel (void) pthread_mutex_lock(&plugin_mutex); 359*f8994074SJan Friedel if (hosts_prev == NULL) { 360*f8994074SJan Friedel hosts_prev = hosts; 361*f8994074SJan Friedel } 362*f8994074SJan Friedel hosts = hosts_new; 363657a8c20SJan Friedel current_host = hosts; 364*f8994074SJan Friedel (void) pthread_mutex_unlock(&plugin_mutex); 365*f8994074SJan 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 * 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 * 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 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 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 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; 610*f8994074SJan Friedel if (hosts_prev != NULL) { 611*f8994074SJan Friedel freehostlist(&hosts_prev); 612*f8994074SJan Friedel DPRINT((dfile, "stale host list freed\n")); 613*f8994074SJan Friedel } 614657a8c20SJan Friedel break; 615657a8c20SJan Friedel case SEND_RECORD_NEXT: 616*f8994074SJan Friedel DPRINT((dfile, "retry the same host: %s (penalty) " 617*f8994074SJan Friedel "rsn:%d\n", current_host->host->h_name, err_rsn)); 618657a8c20SJan Friedel attempts++; 619657a8c20SJan Friedel break; 620657a8c20SJan Friedel case SEND_RECORD_RETRY: 621*f8994074SJan Friedel DPRINT((dfile, "retry the same host: %s (no penalty) " 622*f8994074SJan 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 } 655*f8994074SJan 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. 699657a8c20SJan Friedel * For more information see audit(1M). 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 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"); 726*f8994074SJan Friedel if (val_str == NULL) { 727*f8994074SJan Friedel *error = strdup( 728*f8994074SJan Friedel gettext("p_timeout attribute not found")); 729*f8994074SJan Friedel return (AUDITD_RETRY); 730*f8994074SJan 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; 737*f8994074SJan Friedel } else { 738*f8994074SJan Friedel timeout_p_timeout = DEFAULT_TIMEOUT; 739*f8994074SJan Friedel timeout = timeout_p_timeout; 740*f8994074SJan Friedel DPRINT((dfile, "p_timeout set to default value: %d\n", 741*f8994074SJan Friedel timeout)); 742657a8c20SJan Friedel } 743657a8c20SJan Friedel 744657a8c20SJan Friedel val_str = kva_match(kv, "p_retries"); 745*f8994074SJan Friedel if (val_str == NULL) { 746*f8994074SJan Friedel *error = strdup( 747*f8994074SJan Friedel gettext("p_retries attribute not found")); 748*f8994074SJan Friedel return (AUDITD_RETRY); 749*f8994074SJan 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"); 758*f8994074SJan Friedel if (val_str == NULL) { 759*f8994074SJan Friedel *error = strdup(gettext("qsize attribute not found")); 760*f8994074SJan Friedel return (AUDITD_RETRY); 761*f8994074SJan Friedel } 762657a8c20SJan Friedel DPRINT((dfile, "qsize=%s\n", val_str)); 763657a8c20SJan Friedel errno = 0; 764657a8c20SJan Friedel val_l = atol(val_str); 765*f8994074SJan Friedel if (errno == 0 && val_l >= 0) { 766657a8c20SJan Friedel transq_count_max = val_l; 767657a8c20SJan Friedel } 768*f8994074SJan Friedel if (transq_count_max == 0 && 769*f8994074SJan 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 806657a8c20SJan Friedel * used by auditd to output warnings via the audit_warn(1M) 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 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 822*f8994074SJan Friedel (void) pthread_mutex_lock(&plugin_mutex); 823*f8994074SJan Friedel freehostlist(&hosts); 824*f8994074SJan Friedel freehostlist(&hosts_prev); 825*f8994074SJan Friedel (void) pthread_mutex_unlock(&plugin_mutex); 826*f8994074SJan Friedel current_host = NULL; 827657a8c20SJan Friedel *error = NULL; 828657a8c20SJan Friedel return (AUDITD_SUCCESS); 829657a8c20SJan Friedel } 830