audit_remote.c revision f89940742f5d14dde79b69b98a414dd7b7f585c7
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*
* send audit records to remote host
*
*/
/*
* auditd_plugin_open(), auditd_plugin() and auditd_plugin_close()
* implement a replaceable library for use by auditd; they are a
* project private interface and may change without notice.
*/
#include <assert.h>
#include <audit_plugin.h>
#include <bsm/audit_record.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <netdb.h>
#include <pthread.h>
#include <rpc/rpcsec_gss.h>
#include <secdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <unistd.h>
#include <poll.h>
#include "audit_remote.h"
static int nosuccess_cnt; /* unsuccessful delivery counter */
static int retries; /* connection retries */
int timeout; /* connection timeout */
static int timeout_p_timeout; /* p_timeout attr storage */
/* semi-exponential timeout back off; x .. attempts, y .. timeout */
/* general plugin lock */
static struct hostlist_s *current_host;
static struct hostlist_s *hosts;
static struct hostlist_s *hosts_prev;
extern struct transq_hdr_s transq_hdr;
static long transq_count_max;
extern pthread_mutex_t transq_lock;
extern boolean_t notify_pipe_ready;
extern int notify_pipe[2];
#if DEBUG
#endif
/*
* set_transq_count_max() - sets the transq_count_max value based on kernel
* audit queue high water mark. This is backup solution for a case, when the
* the default qsize zero value is (intentionally) set in the audit_remote(5)
* plugin configuration.
*/
static auditd_rc_t
{
return (AUDITD_SUCCESS);
}
return (AUDITD_RETRY);
}
/*
* get_port_default() - set the default port number; note, that "solaris-audit"
* used below in the code is the IANA assigned service name for the secure
* remote solaris audit logging.
*/
static auditd_rc_t
get_port_default(int *port_default)
{
char serventry_buf[1024];
#if DEBUG
}
#endif
return (AUDITD_INVALID);
}
return (AUDITD_SUCCESS);
}
/*
* trim_me() - trims the white space characters around the specified string.
* Inputs - pointer to the beginning of the string (str_ptr); returns - pointer
* to the trimmed string. Function returns NULL pointer in case of received
* empty string, NULL pointer or in case the pointed string consists of white
* space characters only.
*/
static char *
char *str_end;
return (NULL);
}
str_ptr++;
}
if (*str_ptr == '\0') {
return (NULL);
}
str_end--;
}
*str_end = '\0';
return (str_ptr);
}
/*
* Frees host list - should be called while keeping auditd_mutex.
*/
static void
{
hostlist_t *h, *n;
h = *hostlist_ptr;
while (h != NULL) {
n = h->next_host;
freehostent(h->host);
free(h);
h = n;
}
*hostlist_ptr = NULL;
}
/*
* parsehosts() end parses the host string (hosts_str)
*/
static auditd_rc_t
{
char *hostportmech, *hpm;
char *hostname;
char *port_str;
char *mech_str;
int port;
int port_default = -1;
char *lasts_hpm;
int error_num;
int rc;
#if DEBUG
char addr_buf[INET6_ADDRSTRLEN];
int num_of_hosts = 0;
#endif
port = port_default;
return (AUDITD_INVALID);
}
/* parse single host:port:mech target */
continue;
}
continue;
}
continue;
}
/* too many colons in the hostportmech string */
"specification"));
return (AUDITD_INVALID);
}
"specification"));
return (AUDITD_INVALID);
}
/* trim hostname */
"specification"));
return (AUDITD_INVALID);
}
if (!hostentry) {
&error_num);
}
if (!hostentry) {
"try later"));
return (AUDITD_RETRY);
} else {
return (AUDITD_INVALID);
}
}
INET6_ADDRSTRLEN)));
/* trim port */
if (port_default == -1 &&
!= AUDITD_SUCCESS) {
"unable to get default port number"));
return (rc);
}
port = port_default;
} else {
errno = 0;
return (AUDITD_INVALID);
}
}
/* trim mechanism */
return (AUDITD_INVALID);
}
#if DEBUG
} else {
#endif
}
/* add this host to host list */
return (AUDITD_NO_MEMORY);
}
} else {
}
#if DEBUG
num_of_hosts++;
#endif
}
(void) pthread_mutex_lock(&plugin_mutex);
if (hosts_prev == NULL) {
hosts_prev = hosts;
}
(void) pthread_mutex_unlock(&plugin_mutex);
return (AUDITD_SUCCESS);
}
#if DEBUG
static char *
char *rc_msg;
switch (msg_code) {
case AUDITD_SUCCESS:
break;
case AUDITD_RETRY:
break;
case AUDITD_NO_MEMORY:
break;
case AUDITD_INVALID:
break;
case AUDITD_COMM_FAIL:
break;
case AUDITD_FATAL:
break;
case AUDITD_FAIL:
break;
}
return (rc_msg);
}
#endif
/*
* rsn_to_msg() - translation of the reason of closure identifier to the more
* human readable/understandable form.
*/
static char *
{
char *rc_msg;
switch (reason) {
case RSN_UNDEFINED:
break;
case RSN_INIT_POLL:
break;
case RSN_TOK_RECV_FAILED:
break;
case RSN_TOK_TOO_BIG:
break;
case RSN_TOK_UNVERIFIABLE:
break;
case RSN_SOCKET_CLOSE:
break;
case RSN_SOCKET_CREATE:
break;
case RSN_CONNECTION_CREATE:
break;
case RSN_PROTOCOL_NEGOTIATE:
break;
case RSN_GSS_CTX_ESTABLISH:
break;
case RSN_GSS_CTX_EXP:
break;
case RSN_UNKNOWN_AF:
break;
case RSN_MEMORY_ALLOCATE:
break;
default: /* RSN_OTHER_ERR */
break;
}
return (rc_msg);
}
/*
* with fd file descriptor.
*/
static boolean_t
{
int flags;
/* power of two test - only single bit flags are allowed */
return (B_FALSE);
}
return (B_FALSE);
}
if (set_fl) { /* set the fl flag */
return (B_TRUE);
}
} else { /* unset the fl flag */
return (B_TRUE);
}
}
return (B_FALSE);
}
return (B_TRUE);
}
/*
* create_notify_pipe() - creates the notification pipe. Function returns
*/
static boolean_t
{
if (pipe(notify_pipe) < 0) {
return (B_FALSE);
} else {
notify_pipe[1]));
/* make (only) the pipe "in" end nonblocking */
(void) close(notify_pipe[0]);
"scheme on top of the notification pipe"));
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* auditd_plugin() sends a record via a tcp connection.
*
* Operation:
* - 1 tcp connection opened at a time, referenced by current_host->sockfd
* - tries to (open and) send a record to the current_host where its address
* is taken from the first hostent h_addr_list entry
* - if connection times out, tries second host
* - if all hosts where tried tries again for retries number of times
* - if everything fails, it bails out with AUDITD_RETRY
*
* Note, that space on stack allocated for any error message returned along
* with AUDITD_RETRY is subsequently freed by auditd.
*
*/
{
int rc = AUDITD_FAIL;
int send_record_rc = SEND_RECORD_FAIL;
int attempts = 0;
char *ext_error; /* extended error string */
char *rsn_msg;
#if DEBUG
char *rc_msg;
static uint64_t last_sequence = 0;
}
#endif
(void) pthread_mutex_lock(&transq_lock);
transq_hdr.count));
(void) pthread_mutex_unlock(&transq_lock);
return (AUDITD_RETRY);
}
(void) pthread_mutex_unlock(&transq_lock);
(void) pthread_mutex_lock(&plugin_mutex);
/* cycle over the hosts and possibly deliver the record */
while (rc != AUDITD_SUCCESS) {
switch (send_record_rc) {
case SEND_RECORD_SUCCESS:
nosuccess_cnt = 0;
rc = AUDITD_SUCCESS;
if (hosts_prev != NULL) {
}
break;
case SEND_RECORD_NEXT:
attempts++;
break;
case SEND_RECORD_RETRY:
break;
}
if (send_record_rc == SEND_RECORD_NEXT) {
/* warn about unsuccessful auditd record delivery */
break;
}
/* semi-exponential timeout back off */
} else {
/* get next host */
if (current_host == NULL) {
}
attempts = 0;
}
/* one cycle finished */
"as p_hosts were tried to deliver "
"the audit record to with no success "
"- sleeping for %d seconds",
break;
}
(void) sleep(NOSUCCESS_DELAY);
}
} /* if (send_record_rc == SEND_RECORD_NEXT) */
} /* while (rc != AUDITD_SUCCESS) */
(void) pthread_mutex_unlock(&plugin_mutex);
#if DEBUG
#endif
return (rc);
}
/*
* auditd_plugin_open() may be called multiple times; on initial open or
* `audit -s`, then kvlist != NULL; on `audit -n`, then kvlist == NULL.
* For more information see audit(1M).
*
* Note, that space on stack allocated for any error message returned along
* with AUDITD_RETRY is subsequently freed by auditd.
*
*/
{
char *val_str;
int val;
long val_l;
int rc = 0;
#if DEBUG
#endif
/* initial open or audit -s */
gettext("p_timeout attribute not found"));
return (AUDITD_RETRY);
}
errno = 0;
} else {
timeout));
}
gettext("p_retries attribute not found"));
return (AUDITD_RETRY);
}
errno = 0;
}
return (AUDITD_RETRY);
}
errno = 0;
}
if (transq_count_max == 0 &&
"auditd queue high water mark\n"));
return (rc);
}
return (AUDITD_RETRY);
}
return (rc);
}
/* create the notification pipe towards the receiving thread */
if (!notify_pipe_ready) {
} else {
return (AUDITD_RETRY);
}
}
#if DEBUG
} else { /* audit -n */
#endif
}
return (AUDITD_SUCCESS);
}
/*
* auditd_plugin_close() performs shutdown operations. The return values are
* used by auditd to output warnings via the audit_warn(1M) script and the
* string returned via "error_text", is passed to audit_warn.
*
* Note, that space on stack allocated for any error message returned along
* with AUDITD_RETRY is subsequently freed by auditd.
*
*/
auditd_plugin_close(char **error)
{
return (AUDITD_RETRY);
}
(void) pthread_mutex_lock(&plugin_mutex);
(void) pthread_mutex_unlock(&plugin_mutex);
current_host = NULL;
return (AUDITD_SUCCESS);
}