diff options
Diffstat (limited to 'framework/src/audit/src/auditd-listen.c')
-rw-r--r-- | framework/src/audit/src/auditd-listen.c | 1065 |
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 - } -} |