[PATCH] Updated audit failure query functionality

Lisa Smith lisa.m.smith at hp.com
Mon Jun 26 19:23:08 UTC 2006


This is an updated patch for the audit failure query 
functionality.  The patch has been completely rewritten
to match the programming style and algorithms used in
auditd-config.c.

This patch introduces a new query function,
get_auditfail_action(enum auditfail_t *failmode), which 
can be called to determine what action should be taken by 
a service after audit_open() fails. The function will 
read the tunable "failure_action" from the new 
/etc/libaudit.conf file to determine what action should be
taken.

This patch provides easy expansion of the /etc/libaudit.conf
file to introduce new libaudit tunables if the need arises 
in the future.

The audit_open() man page will be updated to reference this
query function, and a new man page will also be created for
get_auditfail_action().

Lisa

-------------------

 libaudit.c |  246 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 libaudit.h |   11 ++
 2 files changed, 257 insertions(+)

diff -burN orig/libaudit.c src/libaudit.c
--- orig/libaudit.c     2006-05-25 17:39:52.000000000 -0400
+++ src/libaudit.c      2006-06-26 15:00:56.000000000 -0400
@@ -42,13 +42,56 @@
 #include "libaudit.h"
 #include "private.h"

+/* Local prototypes */
+struct nv_pair
+{
+        const char *name;
+        const char *value;
+};
+
+struct kw_pair
+{
+        const char *name;
+        int (*parser)(const char *, int);
+};
+
+struct nv_list
+{
+        const char *name;
+        int option;
+};
+
+struct libaudit_conf
+{
+        auditfail_t failure_action;
+};

 int audit_archadded = 0;
 int audit_syscalladded = 0;
 unsigned int audit_elf = 0U;
+static struct libaudit_conf config;
+static int load_libaudit_config();
+static char *get_line(FILE *f, char *buf);
+static int nv_split(char *buf, struct nv_pair *nv);
+static const struct kw_pair *kw_lookup(const char *val);
+static int audit_failure_parser(const char *val, int line);
 static int name_to_uid(const char *name, uid_t *uid);
 static int name_to_gid(const char *name, gid_t *gid);

+static const struct kw_pair keywords[] =
+{
+  {"failure_action",           audit_failure_parser },
+  { NULL,                      NULL }
+};
+
+static const struct nv_list failure_actions[] =
+{
+  {"ignore",                   FA_IGNORE },
+  {"log",                      FA_LOG },
+  {"terminate",                FA_TERMINATE },
+  { NULL,                      0 }
+};
+

 int audit_request_status(int fd)
 {
@@ -68,6 +111,209 @@
        return rc;
 }

+/*
+ * Set everything to its default value
+*/
+static void clear_config()
+{
+        config.failure_action = FA_IGNORE;
+}
+
+static int load_libaudit_config()
+{
+        int fd, rc, lineno = 1;
+        struct stat st;
+        FILE *f;
+        char buf[128];
+
+        clear_config();
+
+        /* open the file */
+        rc = open(CONFIG_FILE, O_NOFOLLOW|O_RDONLY);
+        if (rc < 0) {
+                if (errno != ENOENT) {
+                        audit_msg(LOG_ERR, "Error opening config file (%s)",
+                                strerror(errno));
+                        return 1;
+                }
+                audit_msg(LOG_WARNING,
+                        "Config file %s doesn't exist, skipping", CONFIG_FILE);
+                return 0;
+        }
+        fd = rc;
+
+        /* check the file's permissions: owned by root, not world writable,
+         * not symlink.
+         */
+        audit_msg(LOG_DEBUG, "Config file %s opened for parsing",
+                        CONFIG_FILE);
+        if (fstat(fd, &st) < 0) {
+                audit_msg(LOG_ERR, "Error fstat'ing config file (%s)",
+                        strerror(errno));
+                return 1;
+        }
+        if (st.st_uid != 0) {
+                audit_msg(LOG_ERR, "Error - %s isn't owned by root",
+                        CONFIG_FILE);
+                return 1;
+        }
+        if ((st.st_mode & S_IWOTH) == S_IWOTH) {
+                audit_msg(LOG_ERR, "Error - %s is world writable",
+                        CONFIG_FILE);
+                return 1;
+        }
+        if (!S_ISREG(st.st_mode)) {
+                audit_msg(LOG_ERR, "Error - %s is not a regular file",
+                        CONFIG_FILE);
+                return 1;
+        }
+
+        /* it's ok, read line by line */
+        f = fdopen(fd, "r");
+        if (f == NULL) {
+                audit_msg(LOG_ERR, "Error - fdopen failed (%s)",
+                        strerror(errno));
+                return 1;
+        }
+
+        while (get_line(f, buf)) {
+                // convert line into name-value pair
+                const struct kw_pair *kw;
+                struct nv_pair nv;
+                rc = nv_split(buf, &nv);
+                switch (rc) {
+                        case 0: // fine
+                                break;
+                        case 1: // not the right number of tokens.
+                                audit_msg(LOG_ERR,
+                                "Wrong number of arguments for line %d in %s",
+                                        lineno, CONFIG_FILE);
+                                break;
+                        case 2: // no '=' sign
+                                audit_msg(LOG_ERR,
+                                        "Missing equal sign for line %d in %s",
+                                        lineno, CONFIG_FILE);
+                                break;
+                        default: // something else went wrong...
+                                audit_msg(LOG_ERR,
+                                        "Unknown error for line %d in %s",
+                                        lineno, CONFIG_FILE);
+                                break;
+                }
+                if (nv.name == NULL) {
+                        lineno++;
+                        continue;
+                }
+
+                /* identify keyword or error */
+                kw = kw_lookup(nv.name);
+                if (kw->name == NULL) {
+                        audit_msg(LOG_ERR,
+                                "Unknown keyword \"%s\" in line %d of %s",
+                                nv.name, lineno, CONFIG_FILE);
+                        fclose(f);
+                        return 1;
+                }
+
+                /* dispatch to keyword's local parser */
+                rc = kw->parser(nv.value, lineno);
+                if (rc != 0) {
+                        fclose(f);
+                        return 1; // local parser puts message out
+                }
+
+                lineno++;
+        }
+
+        fclose(f);
+        return 0;
+}
+
+int get_auditfail_action(auditfail_t *failmode)
+{
+       if (load_libaudit_config())
+               *failmode = config.failure_action;
+               return 1;
+
+       *failmode = config.failure_action;
+       return 0;
+}
+
+
+static char *get_line(FILE *f, char *buf)
+{
+        if (fgets_unlocked(buf, 128, f)) {
+                /* remove newline */
+                char *ptr = strchr(buf, 0x0a);
+                if (ptr)
+                        *ptr = 0;
+                return buf;
+        }
+        return NULL;
+}
+
+static int nv_split(char *buf, struct nv_pair *nv)
+{
+        /* Get the name part */
+        char *ptr;
+
+        nv->name = NULL;
+        nv->value = NULL;
+        ptr = strtok(buf, " ");
+        if (ptr == NULL)
+                return 0; /* If there's nothing, go to next line */
+        if (ptr[0] == '#')
+                return 0; /* If there's a comment, go to next line */
+        nv->name = ptr;
+
+        /* Check for a '=' */
+        ptr = strtok(NULL, " ");
+        if (ptr == NULL)
+                return 1;
+        if (strcmp(ptr, "=") != 0)
+                return 2;
+
+        /* get the value */
+        ptr = strtok(NULL, " ");
+        if (ptr == NULL)
+                return 1;
+        nv->value = ptr;
+
+        /* Make sure there's nothing else */
+        ptr = strtok(NULL, " ");
+        if (ptr)
+                return 1;
+
+        /* Everything is OK */
+        return 0;
+}
+
+static const struct kw_pair *kw_lookup(const char *val)
+{
+        int i = 0;
+        while (keywords[i].name != NULL) {
+                if (strcasecmp(keywords[i].name, val) == 0)
+                        break;
+                i++;
+        }
+        return &keywords[i];
+}
+
+static int audit_failure_parser(const char *val, int line)
+{
+        int i;
+
+        audit_msg(LOG_DEBUG, "audit_failure_parser called with: %s", val);
+        for (i=0; failure_actions[i].name != NULL; i++) {
+                if (strcasecmp(val, failure_actions[i].name) == 0) {
+                        config.failure_action = failure_actions[i].option;
+                        return 0;
+                }
+        }
+        audit_msg(LOG_ERR, "Option %s not found - line %d", val, line);
+        return 1;
+}
+
 int audit_set_enabled(int fd, uint32_t enabled)
 {
        int rc;
diff -burN orig/libaudit.h src/libaudit.h
--- orig/libaudit.h     2006-05-25 17:38:21.000000000 -0400
+++ src/libaudit.h      2006-06-26 14:58:53.000000000 -0400
@@ -248,6 +248,16 @@
         MACH_ALPHA
 } machine_t;

+/* These are the valid audit failure tunable enum values */
+typedef enum {
+       FA_IGNORE=0,
+       FA_LOG,
+       FA_TERMINATE
+} auditfail_t;
+
+/* #defines for the audit failure query  */
+#define CONFIG_FILE "/etc/libaudit.conf"
+
 /*
  * audit_rule_data supports filter rules with both integer and string
  * fields. It corresponds with AUDIT_ADD_RULE, AUDIT_DEL_RULE and
@@ -362,6 +372,7 @@
 /* AUDIT_GET */
 extern int audit_request_status(int fd);
 extern int audit_is_enabled(int fd);
+extern int get_auditfail_action(auditfail_t *failmode);

 /* AUDIT_SET */
 typedef enum { WAIT_NO, WAIT_YES } rep_wait_t;







More information about the Linux-audit mailing list