aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/audit/src/auditd-listen.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/audit/src/auditd-listen.c')
-rw-r--r--framework/src/audit/src/auditd-listen.c1065
1 files changed, 0 insertions, 1065 deletions
diff --git a/framework/src/audit/src/auditd-listen.c b/framework/src/audit/src/auditd-listen.c
deleted file mode 100644
index d1977c63..00000000
--- a/framework/src/audit/src/auditd-listen.c
+++ /dev/null
@@ -1,1065 +0,0 @@
-/* auditd-listen.c --
- * Copyright 2008,2009,2011 Red Hat Inc., Durham, North Carolina.
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Authors:
- * DJ Delorie <dj@redhat.com>
- *
- */
-
-#include "config.h"
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <string.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <netdb.h>
-#include <fcntl.h> /* O_NOFOLLOW needs gnu defined */
-#include <libgen.h>
-#include <arpa/inet.h>
-#include <limits.h> /* INT_MAX */
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#ifdef HAVE_LIBWRAP
-#include <tcpd.h>
-#endif
-#ifdef USE_GSSAPI
-#include <gssapi/gssapi.h>
-#include <gssapi/gssapi_generic.h>
-#include <krb5.h>
-#endif
-#include "libaudit.h"
-#include "auditd-event.h"
-#include "auditd-config.h"
-#include "private.h"
-
-#include "ev.h"
-
-extern volatile int stop;
-extern int send_audit_event(int type, const char *str);
-#define DEFAULT_BUF_SZ 192
-
-typedef struct ev_tcp {
- struct ev_io io;
- struct sockaddr_in addr;
- struct ev_tcp *next, *prev;
- unsigned int bufptr;
- int client_active;
-#ifdef USE_GSSAPI
- /* This holds the negotiated security context for this client. */
- gss_ctx_id_t gss_context;
- char *remote_name;
- int remote_name_len;
-#endif
- unsigned char buffer [MAX_AUDIT_MESSAGE_LENGTH + 17];
-} ev_tcp;
-
-static int listen_socket;
-static struct ev_io tcp_listen_watcher;
-static struct ev_periodic periodic_watcher;
-static int min_port, max_port, max_per_addr;
-static int use_libwrap = 1;
-#ifdef USE_GSSAPI
-/* This is used to hold our own private key. */
-static gss_cred_id_t server_creds;
-static char *my_service_name, *my_gss_realm;
-static int use_gss = 0;
-static char msgbuf[MAX_AUDIT_MESSAGE_LENGTH + 1];
-#endif
-
-static struct ev_tcp *client_chain = NULL;
-
-static char *sockaddr_to_ipv4(struct sockaddr_in *addr)
-{
- unsigned char *uaddr = (unsigned char *)&(addr->sin_addr);
- static char buf[40];
-
- snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
- uaddr[0], uaddr[1], uaddr[2], uaddr[3]);
- return buf;
-}
-
-static char *sockaddr_to_addr4(struct sockaddr_in *addr)
-{
- unsigned char *uaddr = (unsigned char *)&(addr->sin_addr);
- static char buf[40];
-
- snprintf(buf, sizeof(buf), "%u.%u.%u.%u:%u",
- uaddr[0], uaddr[1], uaddr[2], uaddr[3],
- ntohs (addr->sin_port));
- return buf;
-}
-
-static void set_close_on_exec (int fd)
-{
- int flags = fcntl (fd, F_GETFD);
- if (flags == -1)
- flags = 0;
- flags |= FD_CLOEXEC;
- fcntl (fd, F_SETFD, flags);
-}
-
-static void release_client(struct ev_tcp *client)
-{
- char emsg[DEFAULT_BUF_SZ];
-
- snprintf(emsg, sizeof(emsg), "addr=%s port=%d res=success",
- sockaddr_to_ipv4(&client->addr), ntohs (client->addr.sin_port));
- send_audit_event(AUDIT_DAEMON_CLOSE, emsg);
-#ifdef USE_GSSAPI
- if (client->remote_name)
- free (client->remote_name);
-#endif
- shutdown(client->io.fd, SHUT_RDWR);
- close(client->io.fd);
- if (client_chain == client)
- client_chain = client->next;
- if (client->next)
- client->next->prev = client->prev;
- if (client->prev)
- client->prev->next = client->next;
-}
-
-static void close_client(struct ev_tcp *client)
-{
- release_client (client);
- free (client);
-}
-
-static int ar_write (int sock, const void *buf, int len)
-{
- int rc = 0, w;
- while (len > 0) {
- do {
- w = write(sock, buf, len);
- } while (w < 0 && errno == EINTR);
- if (w < 0)
- return w;
- if (w == 0)
- break;
- rc += w;
- len -= w;
- buf = (const void *)((const char *)buf + w);
- }
- return rc;
-}
-
-#ifdef USE_GSSAPI
-static int ar_read (int sock, void *buf, int len)
-{
- int rc = 0, r;
- while (len > 0) {
- do {
- r = read(sock, buf, len);
- } while (r < 0 && errno == EINTR);
- if (r < 0)
- return r;
- if (r == 0)
- break;
- rc += r;
- len -= r;
- buf = (void *)((char *)buf + r);
- }
- return rc;
-}
-
-
-/* Communications under GSS is done by token exchanges. Each "token"
- may contain a message, perhaps signed, perhaps encrypted. The
- messages within are what we're interested in, but the network sees
- the tokens. The protocol we use for transferring tokens is to send
- the length first, four bytes MSB first, then the token data. We
- return nonzero on error. */
-static int recv_token (int s, gss_buffer_t tok)
-{
- int ret;
- unsigned char lenbuf[4];
- unsigned int len;
-
- ret = ar_read(s, (char *) lenbuf, 4);
- if (ret < 0) {
- audit_msg(LOG_ERR, "GSS-API error reading token length");
- return -1;
- } else if (!ret) {
- return 0;
- } else if (ret != 4) {
- audit_msg(LOG_ERR, "GSS-API error reading token length");
- return -1;
- }
-
- len = ((lenbuf[0] << 24)
- | (lenbuf[1] << 16)
- | (lenbuf[2] << 8)
- | lenbuf[3]);
- if (len > MAX_AUDIT_MESSAGE_LENGTH) {
- audit_msg(LOG_ERR,
- "GSS-API error: event length excedes MAX_AUDIT_LENGTH");
- return -1;
- }
- tok->length = len;
-
- tok->value = (char *) malloc(tok->length ? tok->length : 1);
- if (tok->length && tok->value == NULL) {
- audit_msg(LOG_ERR, "Out of memory allocating token data");
- return -1;
- }
-
- ret = ar_read(s, (char *) tok->value, tok->length);
- if (ret < 0) {
- audit_msg(LOG_ERR, "GSS-API error reading token data");
- free(tok->value);
- return -1;
- } else if (ret != (int) tok->length) {
- audit_msg(LOG_ERR, "GSS-API error reading token data");
- free(tok->value);
- return -1;
- }
-
- return 1;
-}
-
-/* Same here. */
-int send_token(int s, gss_buffer_t tok)
-{
- int ret;
- unsigned char lenbuf[4];
- unsigned int len;
-
- if (tok->length > 0xffffffffUL)
- return -1;
- len = tok->length;
- lenbuf[0] = (len >> 24) & 0xff;
- lenbuf[1] = (len >> 16) & 0xff;
- lenbuf[2] = (len >> 8) & 0xff;
- lenbuf[3] = len & 0xff;
-
- ret = ar_write(s, (char *) lenbuf, 4);
- if (ret < 0) {
- audit_msg(LOG_ERR, "GSS-API error sending token length");
- return -1;
- } else if (ret != 4) {
- audit_msg(LOG_ERR, "GSS-API error sending token length");
- return -1;
- }
-
- ret = ar_write(s, tok->value, tok->length);
- if (ret < 0) {
- audit_msg(LOG_ERR, "GSS-API error sending token data");
- return -1;
- } else if (ret != (int) tok->length) {
- audit_msg(LOG_ERR, "GSS-API error sending token data");
- return -1;
- }
-
- return 0;
-}
-
-
-static void gss_failure_2 (const char *msg, int status, int type)
-{
- OM_uint32 message_context = 0;
- OM_uint32 min_status = 0;
- gss_buffer_desc status_string;
-
- do {
- gss_display_status (&min_status,
- status,
- type,
- GSS_C_NO_OID,
- &message_context,
- &status_string);
-
- audit_msg (LOG_ERR, "GSS error: %s: %s",
- msg, (char *)status_string.value);
-
- gss_release_buffer(&min_status, &status_string);
- } while (message_context != 0);
-}
-
-static void gss_failure (const char *msg, int major_status, int minor_status)
-{
- gss_failure_2 (msg, major_status, GSS_C_GSS_CODE);
- if (minor_status)
- gss_failure_2 (msg, minor_status, GSS_C_MECH_CODE);
-}
-
-#define KCHECK(x,f) if (x) { \
- const char *kstr = krb5_get_error_message(kcontext, x); \
- audit_msg(LOG_ERR, "krb5 error: %s in %s\n", kstr, f); \
- krb5_free_error_message(kcontext, kstr); \
- return -1; }
-
-/* These are our private credentials, which come from a key file on
- our server. They are aquired once, at program start. */
-static int server_acquire_creds(const char *service_name,
- gss_cred_id_t *server_creds)
-{
- gss_buffer_desc name_buf;
- gss_name_t server_name;
- OM_uint32 major_status, minor_status;
-
- krb5_context kcontext = NULL;
- int krberr;
-
- my_service_name = strdup (service_name);
- name_buf.value = (char *)service_name;
- name_buf.length = strlen(name_buf.value) + 1;
- major_status = gss_import_name(&minor_status, &name_buf,
- (gss_OID) gss_nt_service_name,
- &server_name);
- if (major_status != GSS_S_COMPLETE) {
- gss_failure("importing name", major_status, minor_status);
- return -1;
- }
-
- major_status = gss_acquire_cred(&minor_status,
- server_name, GSS_C_INDEFINITE,
- GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
- server_creds, NULL, NULL);
- if (major_status != GSS_S_COMPLETE) {
- gss_failure("acquiring credentials",
- major_status, minor_status);
- return -1;
- }
-
- (void) gss_release_name(&minor_status, &server_name);
-
- krberr = krb5_init_context (&kcontext);
- KCHECK (krberr, "krb5_init_context");
- krberr = krb5_get_default_realm (kcontext, &my_gss_realm);
- KCHECK (krberr, "krb5_get_default_realm");
-
- audit_msg(LOG_DEBUG, "GSS creds for %s acquired", service_name);
-
- return 0;
-}
-
-/* This is where we negotiate a security context with the client. In
- the case of Kerberos, this is where the key exchange happens.
- FIXME: While everything else is strictly nonblocking, this
- negotiation blocks. */
-static int negotiate_credentials (ev_tcp *io)
-{
- gss_buffer_desc send_tok, recv_tok;
- gss_name_t client;
- OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
- gss_ctx_id_t *context;
- OM_uint32 sess_flags;
- char *slashptr, *atptr;
-
- context = & io->gss_context;
- *context = GSS_C_NO_CONTEXT;
-
- maj_stat = GSS_S_CONTINUE_NEEDED;
- do {
- /* STEP 1 - get a token from the client. */
-
- if (recv_token(io->io.fd, &recv_tok) <= 0) {
- audit_msg(LOG_ERR,
- "TCP session from %s will be closed, error ignored",
- sockaddr_to_addr4(&io->addr));
- return -1;
- }
- if (recv_tok.length == 0)
- continue;
-
- /* STEP 2 - let GSS process that token. */
-
- maj_stat = gss_accept_sec_context(&acc_sec_min_stat,
- context, server_creds,
- &recv_tok,
- GSS_C_NO_CHANNEL_BINDINGS, &client,
- NULL, &send_tok, &sess_flags,
- NULL, NULL);
- if (recv_tok.value) {
- free(recv_tok.value);
- recv_tok.value = NULL;
- }
- if (maj_stat != GSS_S_COMPLETE
- && maj_stat != GSS_S_CONTINUE_NEEDED) {
- gss_release_buffer(&min_stat, &send_tok);
- if (*context != GSS_C_NO_CONTEXT)
- gss_delete_sec_context(&min_stat, context,
- GSS_C_NO_BUFFER);
- gss_failure("accepting context", maj_stat,
- acc_sec_min_stat);
- return -1;
- }
-
- /* STEP 3 - send any tokens to the client that GSS may
- ask us to send. */
-
- if (send_tok.length != 0) {
- if (send_token(io->io.fd, &send_tok) < 0) {
- gss_release_buffer(&min_stat, &send_tok);
- audit_msg(LOG_ERR,
- "TCP session from %s will be closed, error ignored",
- sockaddr_to_addr4(&io->addr));
- if (*context != GSS_C_NO_CONTEXT)
- gss_delete_sec_context(&min_stat,
- context, GSS_C_NO_BUFFER);
- return -1;
- }
- gss_release_buffer(&min_stat, &send_tok);
- }
- } while (maj_stat == GSS_S_CONTINUE_NEEDED);
-
- maj_stat = gss_display_name(&min_stat, client, &recv_tok, NULL);
- gss_release_name(&min_stat, &client);
-
- if (maj_stat != GSS_S_COMPLETE) {
- gss_failure("displaying name", maj_stat, min_stat);
- return -1;
- }
-
- audit_msg(LOG_INFO, "GSS-API Accepted connection from: %s",
- (char *)recv_tok.value);
- io->remote_name = strdup (recv_tok.value);
- io->remote_name_len = strlen (recv_tok.value);
- gss_release_buffer(&min_stat, &recv_tok);
-
- slashptr = strchr (io->remote_name, '/');
- atptr = strchr (io->remote_name, '@');
-
- if (!slashptr || !atptr) {
- audit_msg(LOG_ERR, "Invalid GSS name from remote client: %s",
- io->remote_name);
- return -1;
- }
-
- *slashptr = 0;
- if (strcmp (io->remote_name, my_service_name)) {
- audit_msg(LOG_ERR, "Unauthorized GSS client name: %s (not %s)",
- io->remote_name, my_service_name);
- return -1;
- }
- *slashptr = '/';
-
- if (strcmp (atptr+1, my_gss_realm)) {
- audit_msg(LOG_ERR, "Unauthorized GSS client realm: %s (not %s)",
- atptr+1, my_gss_realm);
- return -1;
- }
-
- return 0;
-}
-#endif /* USE_GSSAPI */
-
-/* This is called from auditd-event after the message has been logged.
- The header is already filled in. */
-static void client_ack (void *ack_data, const unsigned char *header,
- const char *msg)
-{
- ev_tcp *io = (ev_tcp *)ack_data;
-#ifdef USE_GSSAPI
- if (use_gss) {
- OM_uint32 major_status, minor_status;
- gss_buffer_desc utok, etok;
- int rc, mlen;
-
- mlen = strlen (msg);
- utok.length = AUDIT_RMW_HEADER_SIZE + mlen;
- utok.value = malloc (utok.length + 1);
-
- memcpy (utok.value, header, AUDIT_RMW_HEADER_SIZE);
- memcpy (utok.value+AUDIT_RMW_HEADER_SIZE, msg, mlen);
-
- /* Wrapping the message creates a token for the
- client. Then we just have to worry about sending
- the token. */
-
- major_status = gss_wrap (&minor_status,
- io->gss_context,
- 1,
- GSS_C_QOP_DEFAULT,
- &utok,
- NULL,
- &etok);
- if (major_status != GSS_S_COMPLETE) {
- gss_failure("encrypting message", major_status,
- minor_status);
- free (utok.value);
- return;
- }
- // FIXME: What were we going to do with rc?
- rc = send_token (io->io.fd, &etok);
- free (utok.value);
- (void) gss_release_buffer(&minor_status, &etok);
-
- return;
- }
-#endif
- // Send the header and a text error message if it exists
- ar_write (io->io.fd, header, AUDIT_RMW_HEADER_SIZE);
- if (msg[0])
- ar_write (io->io.fd, msg, strlen(msg));
-}
-
-static void client_message (struct ev_tcp *io, unsigned int length,
- unsigned char *header)
-{
- unsigned char ch;
- uint32_t type, mlen, seq;
- int hver, mver;
-
- if (AUDIT_RMW_IS_MAGIC (header, length)) {
- AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, mlen, seq)
-
- ch = header[length];
- header[length] = 0;
- if (length > 1 && header[length-1] == '\n')
- header[length-1] = 0;
- if (type == AUDIT_RMW_TYPE_HEARTBEAT) {
- unsigned char ack[AUDIT_RMW_HEADER_SIZE];
- AUDIT_RMW_PACK_HEADER (ack, 0, AUDIT_RMW_TYPE_ACK,
- 0, seq);
- client_ack (io, ack, "");
- } else
- enqueue_formatted_event(header+AUDIT_RMW_HEADER_SIZE,
- client_ack, io, seq);
- header[length] = ch;
- } else {
- header[length] = 0;
- if (length > 1 && header[length-1] == '\n')
- header[length-1] = 0;
- enqueue_formatted_event (header, NULL, NULL, 0);
- }
-}
-
-static void auditd_tcp_client_handler( struct ev_loop *loop,
- struct ev_io *_io, int revents )
-{
- struct ev_tcp *io = (struct ev_tcp *) _io;
- int i, r;
- int total_this_call = 0;
-
- io->client_active = 1;
-
- /* The socket is non-blocking, but we have a limited buffer
- size. In the event that we get a packet that's bigger than
- our buffer, we need to read it in multiple parts. Thus, we
- keep reading/parsing/processing until we run out of ready
- data. */
-read_more:
- r = read (io->io.fd,
- io->buffer + io->bufptr,
- MAX_AUDIT_MESSAGE_LENGTH - io->bufptr);
-
- if (r < 0 && errno == EAGAIN)
- r = 0;
-
- /* We need to keep track of the difference between "no data
- * because it's closed" and "no data because we've read it
- * all". */
- if (r == 0 && total_this_call > 0) {
- return;
- }
-
- /* If the connection is gracefully closed, the first read we
- try will return zero. If the connection times out or
- otherwise fails, the read will return -1. */
- if (r <= 0) {
- if (r < 0)
- audit_msg (LOG_WARNING,
- "client %s socket closed unexpectedly",
- sockaddr_to_addr4(&io->addr));
-
- /* There may have been a final message without a LF. */
- if (io->bufptr) {
- client_message (io, io->bufptr, io->buffer);
-
- }
-
- ev_io_stop (loop, _io);
- close_client (io);
- return;
- }
-
- total_this_call += r;
-
-more_messages:
-#ifdef USE_GSSAPI
- /* If we're using GSS at all, everything will be encrypted,
- one record per token. */
- if (use_gss) {
- gss_buffer_desc utok, etok;
- io->bufptr += r;
- uint32_t len;
- OM_uint32 major_status, minor_status;
-
- /* We need at least four bytes to test the length. If
- we have more than four bytes, we can tell if we
- have a whole token (or more). */
-
- if (io->bufptr < 4)
- return;
-
- len = ( ((uint32_t)(io->buffer[0] & 0xFF) << 24)
- | ((uint32_t)(io->buffer[1] & 0xFF) << 16)
- | ((uint32_t)(io->buffer[2] & 0xFF) << 8)
- | (uint32_t)(io->buffer[3] & 0xFF));
-
- /* Make sure we got something big enough and not too big */
- if (io->bufptr < 4 + len || len > MAX_AUDIT_MESSAGE_LENGTH)
- return;
- i = len + 4;
-
- etok.length = len;
- etok.value = io->buffer + 4;
-
- /* Unwrapping the token gives us the original message,
- which we know is already a single record. */
- major_status = gss_unwrap (&minor_status, io->gss_context,
- &etok, &utok, NULL, NULL);
-
- if (major_status != GSS_S_COMPLETE) {
- gss_failure("decrypting message", major_status,
- minor_status);
- } else {
- /* client_message() wants to NUL terminate it,
- so copy it to a bigger buffer. Plus, we
- want to add our own tag. */
- memcpy (msgbuf, utok.value, utok.length);
- while (utok.length > 0 && msgbuf[utok.length-1] == '\n')
- utok.length --;
- snprintf (msgbuf + utok.length,
- MAX_AUDIT_MESSAGE_LENGTH - utok.length,
- " krb5=%s", io->remote_name);
- utok.length += 6 + io->remote_name_len;
- client_message (io, utok.length, msgbuf);
- gss_release_buffer(&minor_status, &utok);
- }
- } else
-#endif
- if (AUDIT_RMW_IS_MAGIC (io->buffer, (io->bufptr+r))) {
- uint32_t type, len, seq;
- int hver, mver;
- unsigned char *header = (unsigned char *)io->buffer;
-
- io->bufptr += r;
-
- if (io->bufptr < AUDIT_RMW_HEADER_SIZE)
- return;
-
- AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, len, seq);
-
- /* Make sure len is not too big */
- if (len > MAX_AUDIT_MESSAGE_LENGTH)
- return;
-
- i = len;
- i += AUDIT_RMW_HEADER_SIZE;
-
- /* See if we have enough bytes to extract the whole message. */
- if (io->bufptr < i)
- return;
-
- /* We have an I-byte message in buffer. Send ACK */
- client_message (io, i, io->buffer);
-
- } else {
- /* At this point, the buffer has IO->BUFPTR+R bytes in it.
- The first IO->BUFPTR bytes do not have a LF in them (we've
- already checked), we must check the R new bytes. */
-
- for (i = io->bufptr; i < io->bufptr + r; i ++)
- if (io->buffer [i] == '\n')
- break;
-
- io->bufptr += r;
-
- /* Check for a partial message, with no LF yet. */
- if (i == io->bufptr)
- return;
-
- i++;
-
- /* We have an I-byte message in buffer. Send ACK */
- client_message (io, i, io->buffer);
- }
-
- /* Now copy any remaining bytes to the beginning of the
- buffer. */
- memmove(io->buffer, io->buffer + i, io->bufptr - i);
- io->bufptr -= i;
-
- /* See if this packet had more than one message in it. */
- if (io->bufptr > 0) {
- r = io->bufptr;
- io->bufptr = 0;
- goto more_messages;
- }
-
- /* Go back and see if there's more data to read. */
- goto read_more;
-}
-
-#ifndef HAVE_LIBWRAP
-#define auditd_tcpd_check(s) ({ 0; })
-#else
-int allow_severity = LOG_INFO, deny_severity = LOG_NOTICE;
-static int auditd_tcpd_check(int sock)
-{
- struct request_info request;
-
- request_init(&request, RQ_DAEMON, "auditd", RQ_FILE, sock, 0);
- fromhost(&request);
- if (! hosts_access(&request))
- return 1;
- return 0;
-}
-#endif
-
-/*
- * This function counts the number of concurrent connections and returns
- * a 1 if there are too many and a 0 otherwise. It assumes the incoming
- * connection has not been added to the linked list yet.
- */
-static int check_num_connections(struct sockaddr_in *aaddr)
-{
- int num = 0;
- struct ev_tcp *client = client_chain;
-
- while (client) {
- if (memcmp(&aaddr->sin_addr, &client->addr.sin_addr,
- sizeof(struct in_addr)) == 0) {
- num++;
- if (num >= max_per_addr)
- return 1;
- }
- client = client->next;
- }
- return 0;
-}
-
-static void auditd_tcp_listen_handler( struct ev_loop *loop,
- struct ev_io *_io, int revents )
-{
- int one=1;
- int afd;
- socklen_t aaddrlen;
- struct sockaddr_in aaddr;
- struct ev_tcp *client;
- char emsg[DEFAULT_BUF_SZ];
-
- /* Accept the connection and see where it's coming from. */
- aaddrlen = sizeof(aaddr);
- afd = accept (listen_socket, (struct sockaddr *)&aaddr, &aaddrlen);
- if (afd == -1) {
- audit_msg(LOG_ERR, "Unable to accept TCP connection");
- return;
- }
-
- if (use_libwrap) {
- if (auditd_tcpd_check(afd)) {
- shutdown(afd, SHUT_RDWR);
- close(afd);
- audit_msg(LOG_ERR, "TCP connection from %s rejected",
- sockaddr_to_addr4(&aaddr));
- snprintf(emsg, sizeof(emsg),
- "op=wrap addr=%s port=%d res=no",
- sockaddr_to_ipv4(&aaddr),
- ntohs (aaddr.sin_port));
- send_audit_event(AUDIT_DAEMON_ACCEPT, emsg);
- return;
- }
- }
-
- /* Verify it's coming from an authorized port. We assume the firewall
- * will block attempts from unauthorized machines. */
- if (min_port > ntohs (aaddr.sin_port) ||
- ntohs (aaddr.sin_port) > max_port) {
- audit_msg(LOG_ERR, "TCP connection from %s rejected",
- sockaddr_to_addr4(&aaddr));
- snprintf(emsg, sizeof(emsg),
- "op=port addr=%s port=%d res=no",
- sockaddr_to_ipv4(&aaddr),
- ntohs (aaddr.sin_port));
- send_audit_event(AUDIT_DAEMON_ACCEPT, emsg);
- shutdown(afd, SHUT_RDWR);
- close(afd);
- return;
- }
-
- /* Make sure we don't have too many connections */
- if (check_num_connections(&aaddr)) {
- audit_msg(LOG_ERR, "Too many connections from %s - rejected",
- sockaddr_to_addr4(&aaddr));
- snprintf(emsg, sizeof(emsg),
- "op=dup addr=%s port=%d res=no",
- sockaddr_to_ipv4(&aaddr),
- ntohs (aaddr.sin_port));
- send_audit_event(AUDIT_DAEMON_ACCEPT, emsg);
- shutdown(afd, SHUT_RDWR);
- close(afd);
- return;
- }
-
- /* Connection is accepted...start setting it up */
- setsockopt(afd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof (int));
- setsockopt(afd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof (int));
- setsockopt(afd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof (int));
- set_close_on_exec (afd);
-
- /* Make the client data structure */
- client = (struct ev_tcp *) malloc (sizeof (struct ev_tcp));
- if (client == NULL) {
- audit_msg(LOG_CRIT, "Unable to allocate TCP client data");
- snprintf(emsg, sizeof(emsg),
- "op=alloc addr=%s port=%d res=no",
- sockaddr_to_ipv4(&aaddr),
- ntohs (aaddr.sin_port));
- send_audit_event(AUDIT_DAEMON_ACCEPT, emsg);
- shutdown(afd, SHUT_RDWR);
- close(afd);
- return;
- }
-
- memset (client, 0, sizeof (struct ev_tcp));
- client->client_active = 1;
-
- // Was watching for EV_ERROR, but libev 3.48 took it away
- ev_io_init (&(client->io), auditd_tcp_client_handler, afd, EV_READ);
-
- memcpy (&client->addr, &aaddr, sizeof (struct sockaddr_in));
-
-#ifdef USE_GSSAPI
- if (use_gss && negotiate_credentials (client)) {
- shutdown(afd, SHUT_RDWR);
- close(afd);
- free(client);
- return;
- }
-#endif
-
- fcntl(afd, F_SETFL, O_NONBLOCK | O_NDELAY);
- ev_io_start (loop, &(client->io));
-
- /* Add the new connection to a linked list of active clients. */
- client->next = client_chain;
- if (client->next)
- client->next->prev = client;
- client_chain = client;
-
- /* And finally log that we accepted the connection */
- snprintf(emsg, sizeof(emsg),
- "addr=%s port=%d res=success", sockaddr_to_ipv4(&aaddr),
- ntohs (aaddr.sin_port));
- send_audit_event(AUDIT_DAEMON_ACCEPT, emsg);
-}
-
-static void auditd_set_ports(int minp, int maxp, int max_p_addr)
-{
- min_port = minp;
- max_port = maxp;
- max_per_addr = max_p_addr;
-}
-
-static void periodic_handler(struct ev_loop *loop, struct ev_periodic *per,
- int revents )
-{
- struct daemon_conf *config = (struct daemon_conf *) per->data;
- struct ev_tcp *ev, *next = NULL;
- int active;
-
- if (!config->tcp_client_max_idle)
- return;
-
- for (ev = client_chain; ev; ev = next) {
- active = ev->client_active;
- ev->client_active = 0;
- if (active)
- continue;
-
- audit_msg(LOG_NOTICE,
- "client %s idle too long - closing connection\n",
- sockaddr_to_addr4(&(ev->addr)));
- ev_io_stop (loop, &ev->io);
- release_client(ev);
- next = ev->next;
- free(ev);
- }
-}
-
-int auditd_tcp_listen_init ( struct ev_loop *loop, struct daemon_conf *config )
-{
- struct sockaddr_in address;
- int one = 1;
-
- ev_periodic_init (&periodic_watcher, periodic_handler,
- 0, config->tcp_client_max_idle, NULL);
- periodic_watcher.data = config;
- if (config->tcp_client_max_idle)
- ev_periodic_start (loop, &periodic_watcher);
-
- /* If the port is not set, that means we aren't going to
- listen for connections. */
- if (config->tcp_listen_port == 0)
- return 0;
-
- listen_socket = socket (AF_INET, SOCK_STREAM, 0);
- if (listen_socket < 0) {
- audit_msg(LOG_ERR, "Cannot create tcp listener socket");
- return 1;
- }
-
- set_close_on_exec (listen_socket);
- setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR,
- (char *)&one, sizeof (int));
-
- memset (&address, 0, sizeof(address));
- address.sin_family = AF_INET;
- address.sin_port = htons(config->tcp_listen_port);
- address.sin_addr.s_addr = htonl(INADDR_ANY);
-
- /* This avoids problems if auditd needs to be restarted. */
- setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR,
- (char *)&one, sizeof (int));
-
- if (bind(listen_socket, (struct sockaddr *)&address, sizeof(address))){
- audit_msg(LOG_ERR,
- "Cannot bind tcp listener socket to port %ld",
- config->tcp_listen_port);
- close(listen_socket);
- return 1;
- }
-
- listen(listen_socket, config->tcp_listen_queue);
-
- audit_msg(LOG_DEBUG, "Listening on TCP port %ld",
- config->tcp_listen_port);
-
- ev_io_init (&tcp_listen_watcher, auditd_tcp_listen_handler,
- listen_socket, EV_READ);
- ev_io_start (loop, &tcp_listen_watcher);
-
- use_libwrap = config->use_libwrap;
- auditd_set_ports(config->tcp_client_min_port,
- config->tcp_client_max_port,
- config->tcp_max_per_addr);
-
-#ifdef USE_GSSAPI
- if (config->enable_krb5) {
- const char *princ = config->krb5_principal;
- const char *key_file;
- struct stat st;
-
- if (!princ)
- princ = "auditd";
- use_gss = 1;
- /* This may fail, but we don't care. */
- unsetenv ("KRB5_KTNAME");
- if (config->krb5_key_file)
- key_file = config->krb5_key_file;
- else
- key_file = "/etc/audit/audit.key";
- setenv ("KRB5_KTNAME", key_file, 1);
-
- if (stat (key_file, &st) == 0) {
- if ((st.st_mode & 07777) != 0400) {
- audit_msg (LOG_ERR,
- "%s is not mode 0400 (it's %#o) - compromised key?",
- key_file, st.st_mode & 07777);
- return -1;
- }
- if (st.st_uid != 0) {
- audit_msg (LOG_ERR,
- "%s is not owned by root (it's %d) - compromised key?",
- key_file, st.st_uid);
- return -1;
- }
- }
-
- server_acquire_creds(princ, &server_creds);
- }
-#endif
-
- return 0;
-}
-
-void auditd_tcp_listen_uninit ( struct ev_loop *loop,
- struct daemon_conf *config )
-{
-#ifdef USE_GSSAPI
- OM_uint32 status;
-#endif
-
- ev_io_stop ( loop, &tcp_listen_watcher );
- close ( listen_socket );
-
-#ifdef USE_GSSAPI
- if (use_gss) {
- use_gss = 0;
- gss_release_cred(&status, &server_creds);
- }
-#endif
-
- while (client_chain) {
- unsigned char ack[AUDIT_RMW_HEADER_SIZE];
-
- AUDIT_RMW_PACK_HEADER (ack, 0, AUDIT_RMW_TYPE_ENDING, 0, 0);
- client_ack (client_chain, ack, "");
- ev_io_stop (loop, &client_chain->io);
- close_client (client_chain);
- }
-
- if (config->tcp_client_max_idle)
- ev_periodic_stop (loop, &periodic_watcher);
-}
-
-static void periodic_reconfigure(struct daemon_conf *config)
-{
- struct ev_loop *loop = ev_default_loop (EVFLAG_AUTO);
- if (config->tcp_client_max_idle) {
- ev_periodic_set (&periodic_watcher, ev_now (loop),
- config->tcp_client_max_idle, NULL);
- ev_periodic_start (loop, &periodic_watcher);
- } else {
- ev_periodic_stop (loop, &periodic_watcher);
- }
-}
-
-void auditd_tcp_listen_reconfigure ( struct daemon_conf *nconf,
- struct daemon_conf *oconf )
-{
- /* Look at network things that do not need restarting */
- if (oconf->tcp_client_min_port != nconf->tcp_client_min_port ||
- oconf->tcp_client_max_port != nconf->tcp_client_max_port ||
- oconf->tcp_max_per_addr != nconf->tcp_max_per_addr) {
- oconf->tcp_client_min_port = nconf->tcp_client_min_port;
- oconf->tcp_client_max_port = nconf->tcp_client_max_port;
- oconf->tcp_max_per_addr = nconf->tcp_max_per_addr;
- auditd_set_ports(oconf->tcp_client_min_port,
- oconf->tcp_client_max_port,
- oconf->tcp_max_per_addr);
- }
- if (oconf->tcp_client_max_idle != nconf->tcp_client_max_idle) {
- oconf->tcp_client_max_idle = nconf->tcp_client_max_idle;
- periodic_reconfigure(oconf);
- }
- if (oconf->tcp_listen_port != nconf->tcp_listen_port ||
- oconf->tcp_listen_queue != nconf->tcp_listen_queue) {
- oconf->tcp_listen_port = nconf->tcp_listen_port;
- oconf->tcp_listen_queue = nconf->tcp_listen_queue;
- // FIXME: need to restart the network stuff
- }
-}