/*
* 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"
/* semi-exponential timeout back off; x .. attempts, y .. timeout */
/* general plugin lock */
extern struct transq_hdr_s transq_hdr;
static long transq_count_max;
extern pthread_mutex_t transq_lock;
extern boolean_t recv_thread_up;
extern boolean_t notify_pipe_ready;
extern int notify_pipe[2];
#if DEBUG
#endif
/*
* set_transq_count_max() - sets the hiwater value according to the 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_RETRY);
}
return (AUDITD_SUCCESS);
}
/*
* 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
{
#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 if working with
* static hostlist_t.
*/
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;
}
#if DEBUG
void
{
while (hostlist_node != NULL) {
}
}
#endif
static boolean_t
{
/* compare the amount of hosts in the hostlists */
}
return (B_FALSE);
}
/* reduce the head end and compare the hostlists nodes */
while (hosts_a_ptr != NULL) {
break;
}
}
while (hosts_a_ptr != NULL) {
while (hosts_b_ptr != NULL) {
break;
}
}
if (hosts_b_ptr == NULL) {
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* parsehosts() end parses the host string (hosts_str)
*/
static auditd_rc_t
{
char *hostname;
char *port_str;
char *mech_str;
int port;
char *lasts_hpm;
int error_num;
int rc;
#if DEBUG
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);
}
&error_num);
}
"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
}
#if DEBUG
#endif
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.
*
*/
/* ARGSUSED */
{
int send_record_rc;
char *rsn_msg;
#if DEBUG
char *rc_msg;
}
#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(&remote_plugin_mutex);
switch (send_record_rc) {
case SEND_RECORD_SUCCESS:
nosuccess_cnt = 0;
attempts = 0;
failed_host = NULL;
rc = AUDITD_SUCCESS;
break;
case SEND_RECORD_NEXT:
err_rsn));
if (failed_host == NULL) {
}
attempts++;
rc = AUDITD_RETRY;
break;
case SEND_RECORD_RETRY:
err_rsn));
rc = AUDITD_RETRY;
goto retry;
default:
break;
}
if (send_record_rc == SEND_RECORD_NEXT) {
/* warn about unsuccessful audit record delivery */
goto out;
}
attempts + 1);
/* semi-exponential timeout back off */
} else {
/* switch to next host */
if (current_host == NULL) {
}
attempts = 0;
}
/* one cycle finished */
"the audit record to all hosts defined as p_hosts "
"without success - sleeping for %d seconds",
goto out;
}
}
} /* if (send_record_rc == SEND_RECORD_NEXT) */
out:
(void) pthread_mutex_unlock(&remote_plugin_mutex);
if (delay_past_cycle) {
(void) sleep(NOSUCCESS_DELAY);
}
#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.
*
*/
/* ARGSUSED */
char **error)
{
char *val_str;
int val;
long val_l;
int reason;
int timeout_p_timeout_new = 0;
int timeout_new = 0;
int retries_new = 0;
long transq_count_max_new = 0;
#if DEBUG
#endif
if (audit_remote_opened) {
reason = 1;
} else {
reason = 2;
}
} else {
reason = 0;
}
gettext("p_timeout attribute not found"));
return (AUDITD_RETRY);
}
errno = 0;
timeout_new = val;
} else {
timeout));
}
gettext("p_retries attribute not found"));
return (AUDITD_RETRY);
}
errno = 0;
retries_new = val;
}
return (AUDITD_RETRY);
}
errno = 0;
}
if (transq_count_max_new == 0) {
if (rc != AUDITD_SUCCESS) {
"auditd queue high water mark\n"));
return (rc);
}
}
return (AUDITD_RETRY);
}
if (rc != AUDITD_SUCCESS) {
return (rc);
}
/* create the notification pipe towards the receiving thread */
if (!notify_pipe_ready) {
} else {
return (AUDITD_RETRY);
}
}
/* reconfiguration */
(void) pthread_mutex_lock(&transq_lock);
(void) pthread_mutex_unlock(&transq_lock);
(void) pthread_mutex_lock(&remote_plugin_mutex);
if (reason == 1) {
"reset the server connection\n"));
attempts = 0;
nosuccess_cnt = 0;
}
} else {
}
(void) pthread_mutex_unlock(&remote_plugin_mutex);
} else {
/* audit -n (USR1) */
}
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.
*
*/
/* ARGSUSED */
{
return (AUDITD_RETRY);
}
(void) pthread_mutex_lock(&remote_plugin_mutex);
current_host = NULL;
attempts = 0;
nosuccess_cnt = 0;
(void) pthread_mutex_unlock(&remote_plugin_mutex);
return (AUDITD_SUCCESS);
}