joshua_brindle avatar joshua_brindle committed 210a2e8

Add audit watch rule support and bugfixes

Add libaudit support for adding directory watch rules.
Add rule parsing support to auditd.
Rule format matches auditctl. Currently only supports -w and -e.
Retry on EAGAIN from recvfrom on the audit netlink socket.
Always enable audit syscall functionality on start of auditd.

Change-Id: I1f5652e595d4e6570c0af1ada51b748b19723543
Signed-off-by: Joshua Brindle <brindle@quarksecurity.com>;

Comments (0)

Files changed (6)

auditd/Android.mk

 LOCAL_SRC_FILES:= \
 	auditd.c \
 	libaudit.c \
-	audit_log.c
+	audit_log.c \
+	audit_rules.c
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \

auditd/audit_rules.c

+/*
+ * Copyright 2013, Quark Security Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Written by Joshua Brindle <brindle@quarksecurity.com>
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+#define LOG_TAG "audit_rules"
+#include <cutils/log.h>
+
+#include "libaudit.h"
+
+#define LINE_LEN 255
+
+static int audit_rules_parse_and_add(int audit_fd, char *line)
+{
+    char *argv[AUDIT_MAX_FIELDS];
+    int argc;
+    int rc = 0, added_rule = 0;
+    char p;
+    int o;
+    size_t len;
+    struct audit_rule_data *rule;
+
+    /* Strip crlf */
+    line[strlen(line) -1] = '\0';
+
+    argv[0] = "auditd";
+
+    for (argc=1; argc < AUDIT_MAX_FIELDS - 1; argc++) {
+        argv[argc] = strsep(&line, " \n\r");
+        if (argv[argc] == NULL)
+            break;
+    }
+
+    optind = 0;
+
+    while ((o = getopt(argc, argv, "w:e:p:")) != -1) {
+        switch(o) {
+        case 'w':
+            if (audit_add_dir(&rule, optarg)) {
+                SLOGE("Error adding rule");
+                return -1;
+            }
+            added_rule = 1;
+            break;
+        case 'e':
+            if (audit_set_enabled(audit_fd, strtoul(optarg, NULL, 10)))
+                return -1;
+            break;
+        case 'p':
+            if (added_rule == 0) {
+                SLOGE("Specify rule type before permissions");
+                return -1;
+            }
+            uint32_t perms = 0;
+            for (len=0; len < strlen(optarg); len++) {
+                 switch(optarg[len]) {
+                 case 'w':
+                     perms |= AUDIT_PERM_WRITE;
+                     break;
+                 case 'e':
+                     perms |= AUDIT_PERM_EXEC;
+                     break;
+                 case 'r':
+                     perms |= AUDIT_PERM_READ;
+                     break;
+                 case 'a':
+                     perms |= AUDIT_PERM_ATTR;
+                     break;
+                 default:
+                     SLOGE("Unknown permission %c", optarg[len]);
+                     break;
+                 }
+            }
+            if (audit_update_watch_perms(rule, perms)) {
+                SLOGE("Could not set perms on rule");
+                return -1;
+            }
+            break;
+        case '?':
+            SLOGE("Unsupported option: %c", optopt);
+            break;
+        }
+    }
+
+    if (added_rule) {
+        rc = audit_send(audit_fd, AUDIT_ADD_RULE, rule, sizeof(*rule) + rule->buflen);
+        free(rule);
+    }
+
+    return rc;
+}
+
+int audit_rules_read_and_add(int audit_fd, const char *rulefile)
+{
+    int rc;
+    struct stat s;
+    char line[LINE_LEN];
+    FILE *rules;
+
+    rc = stat(rulefile, &s);
+    if (rc < 0) {
+        SLOGE("Could not read audit rules %s: %s", rulefile, strerror(errno));
+        return 0;
+    }
+
+    rules = fopen(rulefile, "r");
+    if (rules == NULL) {
+        return -1;
+    }
+
+    while (fgets(line, sizeof(line), rules)) {
+        SLOGE(line);
+        if (line[0] != '-')
+            continue;
+        if (audit_rules_parse_and_add(audit_fd, line) < 0) {
+           SLOGE("Could not read audit rules");
+           return -1;
+        }
+    }
+
+    fclose(rules);
+    return 0;
+}

auditd/audit_rules.h

+/*
+ * Copyright 2013, Quark Security Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Written by Joshua Brindle <brindle@quarksecurity.com>
+ */
+
+#ifndef _AUDIT_RULES_H_
+#define _AUDIT_RULES_H_
+
+extern int audit_rules_read_and_add(int audit_fd, const char *rulefile);
+
+#endif
 
 #include "libaudit.h"
 #include "audit_log.h"
+#include "audit_rules.h"
 
 /*
  * TODO:
 #define AUDITD_LOG_FILE AUDITD_LOG_DIR "/audit.log"
 #define AUDITD_OLD_LOG_FILE AUDITD_LOG_DIR "/audit.old"
 
+#define AUDITD_RULES_FILE "/data/misc/audit/audit.rules"
+
 #define AUDITD_MAX_LOG_FILE_SIZE (1024 * AUDITD_MAX_LOG_FILE_SIZEKB)
 
 static volatile int quit = 0;
         goto err;
     }
 
+    if (audit_set_enabled(audit_fd, 1) < 0) {
+        rc = errno;
+        SLOGE("Failed on audit_set_enabled with error: %s", strerror(errno));
+        goto err;
+    }
+
+    if (audit_rules_read_and_add(audit_fd, AUDITD_RULES_FILE)) {
+        SLOGE("error reading audit rules: %s", strerror(errno));
+    }
+
     pfds.fd = audit_fd;
     pfds.events = POLLIN;
 

auditd/libaudit.c

  * @return
  *  This function returns a positive sequence number on success, else -errno.
  */
-static int audit_send(int fd, int type, const void *data, unsigned int size)
+int audit_send(int fd, int type, const void *data, unsigned int size)
 {
     int rc;
     static int16_t sequence = 0;
     return rc;
 }
 
+int audit_update_watch_perms(struct audit_rule_data *rule, int perms)
+{
+    uint32_t i;
+
+    if (rule == NULL)
+         return -1;
+
+    for (i = 0; i < rule->field_count; i++) {
+        if (rule->fields[i] == AUDIT_PERM) {
+            rule->values[i] = perms;
+            break;
+        }
+    }
+
+    if (rule->fields[i] == AUDIT_PERM)
+        return 0;
+
+    if (rule->field_count > AUDIT_MAX_FIELDS - 1)
+        return -2;
+
+    rule->fields[rule->field_count] = AUDIT_PERM;
+    rule->fieldflags[rule->field_count] = AUDIT_EQUAL;
+    rule->values[rule->field_count] = perms;
+    rule->field_count++;
+
+    return 0;
+}
+
+int audit_add_dir(struct audit_rule_data **rulep, const char *path)
+{
+    int len = strlen(path);
+    struct audit_rule_data *rule;
+    *rulep = calloc(1, sizeof(*rule) + len);
+    rule = *rulep;
+    if (!rule) {
+        SLOGE("Out of memory");
+        return -1;
+    }
+
+    rule->flags = AUDIT_FILTER_EXIT;
+    rule->action = AUDIT_ALWAYS;
+    rule->field_count = 2;
+
+    rule->mask[0] = ~0;
+    rule->fields[0] = AUDIT_DIR;
+    rule->fieldflags[0] = AUDIT_EQUAL;
+    rule->values[0] = len;
+
+    rule->mask[1] = ~0;
+    rule->fields[1] = AUDIT_PERM;
+    rule->fieldflags[1] = AUDIT_EQUAL;
+    rule->values[1] = AUDIT_PERM_READ | AUDIT_PERM_WRITE |
+                      AUDIT_PERM_EXEC | AUDIT_PERM_ATTR;
+
+    rule->buflen = len;
+    memcpy(&rule->buf[0], path, len);
+
+    return 0;
+}
+
+int audit_set_enabled(int fd, uint32_t state)
+{
+    if (state > AUDIT_LOCKED)
+        return -1;
+
+    struct audit_status s;
+    memset(&s, 0, sizeof(s));
+    s.mask = AUDIT_STATUS_ENABLED;
+    s.enabled = state;
+
+    return audit_send(fd, AUDIT_SET, &s, sizeof(s));
+}
+
 int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode)
 {
     int rc;
          * another error manifests.
          */
         if (len < 0 && errno != EINTR) {
-            if (block == GET_REPLY_NONBLOCKING && errno == EAGAIN) {
-                /* If the request is non blocking and the errno is EAGAIN, just return 0 */
-                return 0;
+            if (errno == EAGAIN) {
+	        if (block == GET_REPLY_NONBLOCKING) {
+                    /* If the request is non blocking and the errno is EAGAIN, just return 0 */
+                    return 0;
+	        }
+	    } else {
+                SLOGE("Error receiving from netlink socket, error: %s", strerror(errno));
+                return -errno;
             }
-            SLOGE("Error receiving from netlink socket, error: %s", strerror(errno));
-            return -errno;
         }
 
         /* 0 or greater indicates success */

auditd/libaudit.h

 
 #define MAX_AUDIT_MESSAGE_LENGTH    8970
 
+#define AUDIT_OFF       0
+#define AUDIT_ON        1
+#define AUDIT_LOCKED    2
+
 typedef enum {
     GET_REPLY_BLOCKING=0,
     GET_REPLY_NONBLOCKING
  */
 extern int  audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode);
 
+/**
+ * sends a command to the audit netlink socket
+ * @param fd
+ *  The fd returned by a call to audit_open()
+ * @param type
+ *  message type, see audit.h in the kernel
+ * @param data
+ *  opaque data pointer
+ * @param size
+ *  size of data in *data
+ * @return
+ *  This function returns 0 on success, -errno on error.
+ */
+extern int  audit_send(int fd, int type, const void *data, unsigned int size);
+
+/**
+ * allocates a rule and adds a directory to watch, defaults to all permissions
+ * @param rulep
+ *  pointer to pointer of an unallocated audit_rule_data, will be allocated, must be freed
+ * @param path
+ *  path to add to the rule
+ * @return
+ *  This function returns 0 on success, -errno on error.
+ */
+extern int  audit_add_dir(struct audit_rule_data **rulep, const char *path);
+
+/**
+ * sets enabled flag, 0 for audit off, 1 for audit on, 2 for audit locked
+ * @param fd
+ *  file descripter returned by audit_open()
+ * @param state
+ *  0 for audit off, 1 for audit on, 2 for audit locked
+ * @return
+ *  This function returns 0 on success, -errno on error, -1 if already locked
+ */
+extern int  audit_set_enabled(int fd, uint32_t state);
+
+/**
+ * Sets permissions for an already allocated watch rule
+ * @param rule
+ *  rule to set permissions on
+ * @param perms
+ *  permissions to set, AUDIT_PERM_{READ,WRITE,EXEC,ATTR}
+ * @return
+ *  This function returns 0 on success, -1 if rule is NULL and -2 if there are too many fields
+ */
+extern int  audit_update_watch_perms(struct audit_rule_data *rule, int perms);
+
 #endif
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.