diff options
author | Ashlee Young <ashlee@wildernessvoice.com> | 2015-11-29 08:22:13 -0800 |
---|---|---|
committer | Ashlee Young <ashlee@wildernessvoice.com> | 2015-11-29 08:22:13 -0800 |
commit | df5afa4fcd9725380f94ca6476248d4cc24f889a (patch) | |
tree | 65456f62397305febf7f40778c5a413a35d094ef /framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c | |
parent | 76f6bf922552c00546e6e85ca471eab28f56986c (diff) |
v2.4.4 audit sources
Change-Id: I9315a7408817db51edf084fb4d27fbb492785084
Signed-off-by: Ashlee Young <ashlee@wildernessvoice.com>
Diffstat (limited to 'framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c')
-rw-r--r-- | framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c new file mode 100644 index 00000000..8234a273 --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c @@ -0,0 +1,580 @@ +/*************************************************************************** +* 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@br.ibm.com> * +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.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> +#ifdef HAVE_LIBCAP_NG +#include <cap-ng.h> +#endif +#include "auparse.h" +#include "zos-remote-log.h" +#include "zos-remote-ldap.h" +#include "zos-remote-config.h" +#include "zos-remote-queue.h" + +#define UNUSED(x) (void)(x) + +/* + * Global vars + */ +volatile int stop = 0; +volatile int hup = 0; +volatile ZOS_REMOTE zos_remote_inst; +static plugin_conf_t conf; +static const char *def_config_file = "/etc/audisp/zos-remote.conf"; +static pthread_t submission_thread; +pid_t mypid = 0; + +/* + * SIGTERM handler + */ +static void term_handler(int sig) +{ + UNUSED(sig); + log_info("Got Termination signal - shutting down plugin"); + stop = 1; + nudge_queue(); +} + +/* + * SIGHUP handler - re-read config, reconnect to ITDS + */ +static void hup_handler(int sig) +{ + UNUSED(sig); + log_info("Got Hangup signal - flushing plugin configuration"); + hup = 1; + nudge_queue(); +} + +/* + * SIGALRM handler - help force exit when terminating daemon + */ +static void alarm_handler(int sig) +{ + UNUSED(sig); + log_err("Timeout waiting for submission thread - Aborting (some events may have been dropped)"); + pthread_cancel(submission_thread); +} + +/* + * The submission thread + * It's job is to dequeue the events from the queue + * and sync submit them to ITDS + */ +static void *submission_thread_main(void *arg) +{ + int rc; + + UNUSED(arg); + log_debug("Starting event submission thread"); + + rc = zos_remote_init(&zos_remote_inst, conf.server, + conf.port, conf.user, + conf.password, + conf.timeout); + + if (rc != ICTX_SUCCESS) { + log_err("Error - Failed to initialize session to z/OS ITDS Server"); + 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(&zos_remote_inst, ber); + if (rc == ICTX_E_FATAL) { + log_err("Error - Fatal error in event submission. Aborting"); + stop = 1; + } else if (rc != ICTX_SUCCESS) { + log_warn("Warning - 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"); + zos_remote_destroy(&zos_remote_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[ZOS_REMOTE_LINK_VALUE_SIZE]; + char logString[ZOS_REMOTE_LOGSTRING_SIZE]; + unsigned long linkValue_tmp; + + UNUSED(user_data); + 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 zos-remote-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 *orig_type = auparse_find_field(au, "type"); + /* roll back event to get 'success' */ + auparse_first_record(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 = ZOS_REMOTE_QUALIF_FAIL; + else + qualifier = ZOS_REMOTE_QUALIF_SUCCESS; + } else if (res) { + if (strncmp(res, "0", 1) == 0 + || strncmp(res, "failed", 6) == 0) + qualifier = ZOS_REMOTE_QUALIF_FAIL; + else + qualifier = ZOS_REMOTE_QUALIF_SUCCESS; + } else + qualifier = ZOS_REMOTE_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, ZOS_REMOTE_LINK_VALUE_SIZE); + memcpy(&linkValue, &linkValue_tmp, sizeof(unsigned long)); + + /* + * Prepare the logString with some meaningful text + * We assume the first record type found is the + * 'originating' audit record + */ + sprintf(logString, "Linux (%s): type: %s", node, orig_type); + free((void *)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, + ZOS_REMOTE_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 + * SMF type 83, subtype 4 event here + */ + rc |= ber_printf(ber, "i", ZOS_REMOTE_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 ZOS_REMOTE_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 ZOS_REMOTE_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 + */ + 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", ZOS_REMOTE_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_interpret_field(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", ZOS_REMOTE_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("Error - Fatal error while encoding request. Aborting"); + stop = 1; +} + +int main(int argc, char *argv[]) +{ + int rc; + const char *cpath; + char buf[1024]; + struct sigaction sa; + sigset_t ss; + auparse_state_t *au; + ssize_t len; + + mypid = getpid(); + + log_info("starting with pid=%d", mypid); + + /* + * install signal handlers + */ + 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("Using configuration file: %s", cpath); + } else { + log_err("Error - invalid number of parameters passed. Aborting"); + return 1; + } + + /* initialize record counter */ + conf.counter = 1; + + /* initialize configuration with default values */ + plugin_clear_config(&conf); + + /* initialize the submission queue */ + if (init_queue(conf.q_depth) != 0) { + log_err("Error - Can't initialize event queue. Aborting"); + return -1; + } + +#ifdef HAVE_LIBCAP_NG + // Drop all capabilities + capng_clear(CAPNG_SELECT_BOTH); + capng_apply(CAPNG_SELECT_BOTH); +#endif + + /* set stdin to O_NONBLOCK */ + if (fcntl(0, F_SETFL, O_NONBLOCK) == -1) { + log_err("Error - Can't set input to Non-blocking mode: %s. Aborting", + strerror(errno)); + return -1; + } + + do { + + hup = 0; /* don't flush unless hup == 1 */ + + /* + * initialization is done in 4 steps: + */ + + /* + * load configuration and + * increase queue depth if needed + */ + rc = plugin_load_config(&conf, cpath); + if (rc != 0) { + log_err("Error - Can't load configuration. Aborting"); + return -1; + } + increase_queue_depth(conf.q_depth); /* 1 */ + + /* initialize auparse */ + au = auparse_init(AUSOURCE_FEED, 0); /* 2 */ + if (au == NULL) { + log_err("Error - exiting due to auparse init errors"); + return -1; + } + + /* + * Block signals for everyone, + * Initialize submission thread, and + * Unblock signals for this thread + */ + sigfillset(&ss); + pthread_sigmask(SIG_BLOCK, &ss, NULL); + pthread_create(&submission_thread, NULL, + submission_thread_main, NULL); + pthread_sigmask(SIG_UNBLOCK, &ss, NULL); /* 3 */ + + /* add our event consumer callback */ + auparse_add_callback(au, push_event, NULL, NULL); /* 4 */ + + /* main loop */ + while (hup == 0 && stop == 0) { + fd_set rfds; + struct timeval tv; + + FD_ZERO(&rfds); + FD_SET(0, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + rc = select(1, &rfds, NULL, NULL, &tv); + if (rc == -1) { + if (errno == EINTR) { + log_debug("Select call interrupted"); + continue; + } + else { + log_err("Error - Fatal error while monitoring input: %s. Aborting", + strerror(errno)); + stop = 1; + } + } + else if (rc) { + len = read(0, buf, 1024); + if (len > 0) + /* let our callback know of the new data */ + auparse_feed(au, buf, len); + else if (len == 0) { + log_debug("End of input - Exiting"); + stop = 1; + } + else { + /* ignore interrupted call or empty pipe */ + if (errno != EINTR && errno != EAGAIN) { + log_err("Error - Fatal error while reading input: %s. Aborting", + strerror(errno)); + stop = 1; + } + else { + log_debug("Ignoring read interruption: %s", + strerror(errno)); + } + } + } + } + /* flush everything, in order */ + auparse_flush_feed(au); /* 4 */ + alarm(10); /* 10 seconds to clear the queue */ + pthread_join(submission_thread, NULL); /* 3 */ + alarm(0); /* cancel any pending alarm */ + auparse_destroy(au); /* 2 */ + plugin_free_config(&conf); /* 1 */ + } + while (hup && stop == 0); + + /* destroy queue before leaving */ + destroy_queue(); + + log_info("Exiting"); + + return 0; +} |