[PATCH 06/07][RFC] RACF audit plugin - plugin main code

Klaus Heinrich Kiwi klausk at br.ibm.com
Fri Sep 28 13:28:35 UTC 2007


This patch implements the main body for the racf plugin. It uses the
auparse_feed() interface to add a callback interface that's called
whenever a complete event is read from stdin.
The push_event() callback does the BER encoding and enqueues the encoded
event.
The 'submission_thread' then dequeues it and synchronously submits it to
the RACF server (using the ldap interface).

SIGHUP is supposed to trigger a configuration file re-read and flush
queues and network connections, but seems like there's still a bit of a
problem when the submission thread is blocked on dequeueing events and a
SIGHUP is caught.

Signed-off-by: Klaus Heinrich Kiwi <klausk at br.ibm.com>

diff -purN audit-1.6.2/audisp/plugins/racf/racf-plugin.c audit-1.6.2_racf/audisp/plugins/racf/racf-plugin.c
--- audit-1.6.2/audisp/plugins/racf/racf-plugin.c	1969-12-31 21:00:00.000000000 -0300
+++ audit-1.6.2_racf/audisp/plugins/racf/racf-plugin.c	2007-09-28 09:18:08.000000000 -0300
@@ -0,0 +1,483 @@
+/***************************************************************************
+*   Copyright (C) 2007 International Business Machines  Corp.             *
+*   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:                                                                *
+*   Klaus Heinrich Kiwi <klausk at br.ibm.com>                               *
+***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <limits.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include <lber.h>
+#include <netinet/in.h>
+#include "auparse.h"
+#include "racf-log.h"
+#include "racf-ldap.h"
+#include "racf-config.h"
+#include "racf-queue.h"
+
+/*
+ * Global vars 
+ */
+volatile int stop = 0;
+volatile int hup = 0;
+volatile RACF racf_inst;
+static racf_conf_t conf;
+static const char *def_config_file = "/etc/audisp/racf.conf";
+static pthread_t submission_thread;
+pid_t mypid = 0;
+
+/*
+ * SIGTERM handler 
+ */
+static void term_handler(int sig)
+{
+        log_info("Got SIGTERM - Exiting");
+        stop = 1;
+        nudge_queue();
+}
+
+/*
+ * SIGHUP handler - re-read config, reconnect to RACF
+ */
+static void hup_handler(int sig)
+{
+        log_info("Got SIGHUP - flushing configuration");
+        hup = 1;
+        nudge_queue();
+}
+
+/*
+ * SIGALRM handler - help force exit when terminating daemon
+ */
+static void alarm_handler(int sig)
+{
+        log_err("Aborting submission thread");
+        pthread_cancel(submission_thread);
+        abort();
+}
+
+/*
+ * The submission thread
+ * It's job is to dequeue the events from the queue
+ * and sync submit them to RACF
+ */
+static void *submission_thread_main(void *arg)
+{
+        int rc;
+        
+        rc = racf_init(&racf_inst, conf.server, 
+                          conf.port, conf.user,
+                          conf.password,
+                          conf.timeout);
+                          
+        if (rc != ICTX_SUCCESS) {
+                log_err("Error - RACF instance initialization failed");
+                stop = 1;
+                return 0;
+        }
+        
+        while (stop == 0) {
+                /* block until we have an event */
+                BerElement *ber = dequeue();
+
+                if (ber == NULL) {
+                        if (hup) {
+                                break;
+                        }
+                        continue;
+                }
+                debug_ber(ber);
+                rc = submit_request_s(&racf_inst, ber);
+                if (rc == ICTX_E_FATAL) {
+                        log_err("Error - Fatal error in event submission");
+                        stop = 1;
+                } else if (rc != ICTX_SUCCESS) {
+                        log_err("Event submission failure - event dropped");
+                }
+                else {
+                        log_debug("Event submission success");
+                }
+                ber_free(ber, 1);        /* also free BER buffer */
+        }
+        log_debug("Stopping event submission thread");
+        racf_destroy(&racf_inst);
+        
+        return 0;
+}
+            
+
+/* 
+ * auparse library callback that's called when an event is ready
+ */
+void
+push_event(auparse_state_t * au, auparse_cb_event_t cb_event_type,
+           void *user_data)
+{
+        int rc;
+        BerElement *ber;
+        int qualifier;
+        char timestamp[26];
+        char linkValue[RACF_LINK_VALUE_SIZE];
+        char logString[RACF_LOGSTRING_SIZE];
+        unsigned long linkValue_tmp;
+
+        if (cb_event_type != AUPARSE_CB_EVENT_READY)
+                return;
+                
+        const au_event_t *e = auparse_get_timestamp(au);
+        if (e == NULL)
+                return;
+        /*
+         * we have an event. Each record will result in a different 'Item'
+         * (refer ASN.1 definition in racf-ldap.h) 
+         */
+        
+        /*
+         * Create a new BER element to encode the request
+         */
+        ber = ber_alloc_t(LBER_USE_DER);
+        if (ber == NULL) {
+            log_err("Error allocating memory for BER element");
+            goto fatal;
+        }
+
+        /*
+         * Collect some information to fill in every item
+         */
+        const char *node = auparse_get_node(au);
+        const char *success = auparse_find_field(au, "success");
+        /* roll back event to get 'res' */
+        auparse_first_record(au);
+        const char *res = auparse_find_field(au, "res");
+        
+        /* check if this event is a success or failure one */
+        if (success) {
+                if (strncmp(success, "0", 1) == 0 ||
+                    strncmp(success, "no", 2) == 0)
+                        qualifier = RACF_QUALIF_FAIL;
+                else
+                        qualifier = RACF_QUALIF_SUCCESS;
+        } else if (res) {
+                if (strncmp(res, "0", 1) == 0
+                    || strncmp(res, "failed", 6) == 0)
+                        qualifier = RACF_QUALIF_FAIL;
+                else
+                        qualifier = RACF_QUALIF_SUCCESS;
+        } else
+                qualifier = RACF_QUALIF_INFO;
+                
+        /* get timestamp text */
+        ctime_r(&e->sec, timestamp);
+        timestamp[24] = '\0';    /* strip \n' */
+        
+        /* prepare linkValue which will be used for every item */
+        linkValue_tmp = htonl(e->serial);        /* padronize to use network
+                                                  * byte order 
+                                                  */
+        memset(&linkValue, 0, RACF_LINK_VALUE_SIZE);
+        memcpy(&linkValue, &linkValue_tmp, sizeof(unsigned long));        
+
+        /* 
+         * Prepare the logString with some meaningful text
+         */
+        sprintf(logString, "Linux (%s):", node);
+
+        /* 
+         * Start writing to BER element.
+         * There's only one field (version) out of the item sequence.
+         * Also open item sequence
+         */
+        rc = ber_printf(ber, "{i{", ICTX_REQUESTVER);
+        if (rc < 0)
+                goto skip_event;
+
+        /* 
+         * Roll back to first record and iterate through all records
+         */
+        auparse_first_record(au);
+        do {
+                const char *type = auparse_find_field(au, "type");
+                if (type == NULL)
+                        goto skip_event;
+                
+                log_debug("got record: %s", auparse_get_record_text(au));
+
+                /* 
+                 * First field is item Version, same as global version
+                 */
+                rc = ber_printf(ber, "{i", ICTX_REQUESTVER);
+
+                /*
+                 * Second field is the itemTag
+                 * use our internal event counter, increasing it
+                 */
+                rc |= ber_printf(ber, "i", conf.counter++);
+
+                /*
+                 * Third field is the linkValue
+                 * using ber_put_ostring since it is not null-terminated
+                 */
+                rc |= ber_put_ostring(ber, linkValue,
+                                      RACF_LINK_VALUE_SIZE,
+                                      LBER_OCTETSTRING);
+                /*
+                 * Fourth field is the violation
+                 * Don't have anything better yet to put here
+                 */
+                rc |= ber_printf(ber, "b", 0);
+                
+                /* 
+                 * Fifth field is the event.
+                 * FIXME: this might be the place to switch on the
+                 * audit record type and map to a more meaningful
+                 * RACF event here
+                 */
+                rc |= ber_printf(ber, "i", RACF_EVENT_AUTHORIZATION);
+                
+                /*
+                 * Sixth field is the qualifier. We map 'success' or
+                 * 'res' to this field
+                 */
+                rc |= ber_printf(ber, "i", qualifier);
+                
+                /* 
+                 * Seventh field is the Class
+                 * always use '@LINUX' for this version
+                 * max size RACF_CLASS_SIZE
+                 */
+                rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG);
+                rc |= ber_printf(ber, "s", "@LINUX");
+                
+                /* 
+                 * Eighth field is the resource
+                 * use the record type (name) as the resource
+                 * max size RACF_RESOURCE_SIZE
+                 */
+                rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG);
+                rc |= ber_printf(ber, "s", type);
+                
+                /* 
+                 * Nineth field is the LogString
+                 * we try to put something meaningful here
+                 * we also start the relocations sequence
+                 */
+                strcat(logString, type);    /* concatenate the event type */
+                rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG);
+                rc |= ber_printf(ber, "s{", logString);
+
+                /*
+                 * Now we start adding the relocations.
+                 * Let's add the timestamp as the first one
+                 * so it's out of the field loop
+                 */
+                rc |= ber_printf(ber, "{i", RACF_RELOC_TIMESTAMP);
+                rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG);
+                rc |= ber_printf(ber, "s}", timestamp);
+
+                /* 
+                 * Check that encoding is going OK until now
+                 */
+                if (rc < 0)
+                        goto skip_event;
+
+                /* 
+                 * Now go to first field,
+                 * and iterate through all fields
+                 */
+                auparse_first_field(au);
+                do {
+                        /* 
+                         * we set a maximum of 1024 chars for
+                         * relocation data (field=value pairs)
+                         * Hopefuly this wont overflow too often
+                         */
+                        char data[1024];
+                        const char *name = auparse_get_field_name(au);
+                        const char *value = auparse_get_field_str(au);
+                        if (name == NULL || value == NULL)
+                                goto skip_event;
+                        
+                        /*
+                         * First reloc field is the Relocation type
+                         * We use 'OTHER' here since we don't have
+                         * anything better
+                         */
+                        rc |= ber_printf(ber, "{i", RACF_RELOC_OTHER);
+                        
+                        /*
+                         * Second field is the relocation data
+                         * We use a 'name=value' pair here
+                         * Use up to 1023 chars (one char left for '\0')
+                         */
+                        snprintf(data, 1023, "%s=%s", name, value);
+                        rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG);
+                        rc |= ber_printf(ber, "s}", data);
+                        
+                        /*
+                         * Check encoding status
+                         */
+                        if (rc < 0)
+                                goto skip_event;
+                } while (auparse_next_field(au) > 0);
+                
+                /* 
+                 * After adding all relocations we are done with
+                 * this item - finalize relocs and item 
+                 */
+                rc |= ber_printf(ber, "}}");
+                
+                /*
+                 * Check if we are doing well with encoding
+                 */
+                if (rc < 0)
+                        goto skip_event;            
+
+        } while (auparse_next_record(au) > 0);
+        
+        /*
+         * We have all items in - finalize item sequence & request
+         */
+        rc |= ber_printf(ber, "}}");
+
+        /*
+         * Check if everything went alright with encoding
+         */
+        if (rc < 0)
+                goto skip_event;
+
+        /* 
+         * finally, enqueue request and let the other
+         * thread process it
+         */
+        log_debug("Encoding done, enqueuing event");
+        enqueue(ber);
+
+        return;
+        
+skip_event:
+        log_warn("Warning - error encoding request, skipping event");
+        ber_free(ber, 1);        /* free it since we're not enqueuing it */
+        return;
+
+fatal:
+        log_err("Fatal error while encoding request - aborting");
+        stop = 1;
+}
+
+int main(int argc, char *argv[])
+{
+        int rc;
+        char *cpath;
+        char buf[1024];
+        struct sigaction sa;
+        auparse_state_t *au;
+
+        mypid = getpid();
+
+        log_info("starting");
+
+        /*
+         * sighandlers 
+         */
+        sa.sa_flags = 0;
+        sigemptyset(&sa.sa_mask);
+        sa.sa_handler = term_handler;
+        sigaction(SIGTERM, &sa, NULL);
+        sa.sa_handler = hup_handler;
+        sigaction(SIGHUP, &sa, NULL);
+        sa.sa_handler = alarm_handler;
+        sigaction(SIGALRM, &sa, NULL);
+
+        /* 
+         * the main program accepts a single (optional) argument:
+         * it's configuration file (this is NOT the plugin configuration
+         * usually located at /etc/audisp/plugin.d)
+         * We use the default (def_config_file) if no arguments are given
+         */
+        if (argc == 1) {
+                cpath = def_config_file;
+                log_warn("No configuration file specified - using default (%s)", cpath);
+        } else if (argc == 2) {
+                cpath = argv[1];
+                log_info("Configuration file: %s", cpath);
+        } else {
+                log_err("Error - invalid number of parameters");
+                return 1;
+        }
+
+        /* initialize record counter */
+        conf.counter = 1;
+
+        do {
+                hup = 0;        /* don't flush unless hup==1 */
+                
+                /* initialization is done in 5 steps: */
+                rc = load_config(&conf, cpath);                    /* 1 */
+                if (rc != 0) {
+                        log_err("Error - Can't load configuration");
+                        return -1;
+                }
+
+                /* initialize auparse */
+                au = auparse_init(AUSOURCE_FEED, 0);               /* 2 */
+                
+                /* initialize the submission queue */              /* 3 */
+                if (init_queue(conf.q_depth) != 0) {
+                        log_err("Error - Can't initialize event queue");
+                        return -1;
+                }
+                
+                /* Initialize submission thread */
+                 pthread_create(&submission_thread, NULL, 
+                                submission_thread_main, NULL);     /* 4 */
+
+                /* add our event consumer callback */
+                auparse_add_callback(au, push_event, NULL, NULL);  /* 5 */
+
+                /* loop reading stdin */
+                while (fgets(buf, 1024, stdin) && hup == 0 && stop == 0) {
+                        /* let our callback know of the new data */
+                        auparse_feed(au, buf, strlen(buf));
+                }
+                /* flush everything, in order */
+                auparse_flush_feed(au);                            /* 5 */
+                nudge_queue();
+                alarm(5);               /* 5 seconds to clear the queue */
+                pthread_join(submission_thread, NULL);             /* 4 */
+                destroy_queue();                                   /* 3 */
+                auparse_destroy(au);                               /* 2 */
+                free_config(&conf);                                /* 1 */
+        }
+        while (hup && stop == 0);
+
+        
+        return 0;
+}





More information about the Linux-audit mailing list