summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Smalley <sds@tycho.nsa.gov>2013-04-05 10:28:30 -0400
committerRicardo Cerqueira <cyanogenmod@cerqueira.org>2013-07-18 20:38:39 +0100
commit01996abeda1240e04a86a0393e0b553dd6310412 (patch)
tree01e9bce924c7008e12e5ef2cfdcfb828bc6d9e00
parent911cbea54fe75341069c66d300e35ad9bde0e591 (diff)
downloadsystem_core-01996abeda1240e04a86a0393e0b553dd6310412.zip
system_core-01996abeda1240e04a86a0393e0b553dd6310412.tar.gz
system_core-01996abeda1240e04a86a0393e0b553dd6310412.tar.bz2
Sync with master auditd.
-rw-r--r--auditd/Android.mk12
-rw-r--r--auditd/README44
-rw-r--r--auditd/audit_log.c526
-rw-r--r--auditd/audit_log.h39
-rw-r--r--auditd/auditd.c311
-rw-r--r--auditd/auditd.h44
-rw-r--r--auditd/libaudit.c351
-rw-r--r--auditd/libaudit.h75
-rw-r--r--auditd/netlink.c367
9 files changed, 911 insertions, 858 deletions
diff --git a/auditd/Android.mk b/auditd/Android.mk
index 582511b..c29319a 100644
--- a/auditd/Android.mk
+++ b/auditd/Android.mk
@@ -3,9 +3,13 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+# Override this in the BoardConfig.mk
+# to change the default size
+# Note: The value is in Kilobytes
+AUDITD_MAX_LOG_FILE_SIZEKB ?= 100
+
LOCAL_SRC_FILES:= \
auditd.c \
- netlink.c \
libaudit.c \
audit_log.c
@@ -13,7 +17,7 @@ LOCAL_SHARED_LIBRARIES := \
libcutils \
libc
-LOCAL_MODULE_TAGS:=optional
-LOCAL_MODULE:=auditd
-
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := auditd
+LOCAL_CFLAGS := -DAUDITD_MAX_LOG_FILE_SIZEKB=$(AUDITD_MAX_LOG_FILE_SIZEKB)
include $(BUILD_EXECUTABLE)
diff --git a/auditd/README b/auditd/README
new file mode 100644
index 0000000..9d14c0d
--- /dev/null
+++ b/auditd/README
@@ -0,0 +1,44 @@
+Auditd Daemon
+
+The audit daemon is a simplified version of its desktop
+counterpart designed to gather the audit logs from the
+audit kernel subsystem. The audit subsystem of the kernel
+includes Linux Security Modules (LSM) messages as well.
+
+To enable the audit subsystem, you must add this to your
+kernel config:
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+
+To enable a LSM, you must consult that LSM's documentation, the
+example below is for SELinux:
+CONFIG_SECURITY_SELINUX=y
+
+This does not include possible dependencies that may need to be
+satisfied for that particular LSM.
+
+The daemon maintains two log files audit.log and audit.old
+at /data/misc/audit/. On boot, if audit.log exists, and
+the size is greater than 0, audit.log is renamed to
+audit.old. The log file is also renamed, or rotated, when
+a threshold is hit. This threshold is hard-coded to 100KB
+but can be adjusted through the AUDITD_MAX_LOG_FILE_SIZEKB
+Makefile file variable that can be overridden in the device.mk
+
+The daemon is not included by default, and must explicitly be
+added to PRODUCT_PACKAGES. This could be set in the device.mk
+
+The daemon also has no external interfaces, but one could
+use inotify to start and build a system from this. The log
+files are owned by UID audit and readable by system. A
+system UID application could conceivably be used to consume
+these logs.
+
+Example configuration in device.mk:
+
+# 1MB Log file threshold
+AUDITD_MAX_LOG_FILE_SIZEKB := 1000
+
+PRODUCT_PACKAGES += auditd
+
+
diff --git a/auditd/audit_log.c b/auditd/audit_log.c
index 5a9ffd3..ef77a3f 100644
--- a/auditd/audit_log.c
+++ b/auditd/audit_log.c
@@ -1,251 +1,353 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ *
+ * 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 William Roberts <w.roberts@sta.samsung.com>
+ */
+
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
-
+#include <stdarg.h>
+#include <ctype.h>
+#include <alloca.h>
#include <sys/klog.h>
#include <sys/types.h>
#include <sys/stat.h>
+#define LOG_TAG "audit_log"
+#include <cutils/log.h>
+
#include "libaudit.h"
#include "audit_log.h"
-#include "auditd.h"
-#define AUDIT_LOG_MODE (S_IRUSR | S_IWUSR | S_IRGRP)
+/*
+ * Note that the flags passed to fcntl and the flags used
+ * by fopen must be compatible. For instance, specifying
+ * write only on one and read only on the other will yield
+ * strange behavior.
+ */
+/* Mode for fopen */
+#define AUDIT_LOG_FMODE "w+"
+/* mode for fchmod*/
+#define AUDIT_LOG_MODE (S_IRUSR | S_IWUSR | S_IRGRP)
+/* flags for fcntl */
#define AUDIT_LOG_FLAGS (O_RDWR | O_CREAT | O_SYNC)
-struct audit_log {
- int fd;
- size_t total_bytes;
- size_t threshold;
- char *rotatefile;
- char *logfile;
+#define AUDIT_TYPE "type="
+#define AUDIT_MSG "msg="
+#define AUDIT_KEYWORD "audit("
+
+struct audit_log
+{
+ FILE *file;
+ size_t total_bytes;
+ size_t threshold;
+ char *rotatefile;
+ char *logfile;
};
/**
- * Writes data pointed by buf to audit log, appends a trailing newline.
- * @param l
- * The log to write
- * @param buf
- * The data to write
- * @param len
- * The length of the data
+ * Wraps open with a fchmod to prevent umask issues from arising in
+ * permission setting as well as a fcntl to set the underlying
+ * fds mode. However, the rest of the library relies on stdio file
+ * access, so a FILE pointer is returned.
+ *
+ * You must make sure your mode and fmode are compatible
+ *
+ * @param file
+ * File stream output
+ * @param path
+ * The path of the log file
+ * @param flags
+ * The flags passed to fcntl
+ * @param fmode
+ * The mode passed to fopen
+ * @param mode
+ * The mode passed to open and fchmod
* @return
+ * 0 on success with *file set, or -errno on error
*/
-static int write_log(audit_log *l, const void *buf, size_t len) {
-
- int rc = 0;
- ssize_t bytes = 0;
-
- /*
- * Ensure that the pointer offset and
- * number of bytes written are the same
- * size. Avoid char *, as on esoteric
- * systems that are not byte addressable
- * it could be defined as something else.
- */
- const uint8_t *b = (uint8_t *)buf;
-
- if(!l) {
- rc = EINVAL;
- goto err;
- }
-
- do {
- bytes = write(l->fd, b, len);
- if(bytes < 0 && errno != EINTR) {
- rc = errno;
- goto err;
- }
- b += bytes;
- len -= bytes;
- l->total_bytes += bytes;
- } while (len > 0);
-
-
-out:
- /*
- * Always attempt to write a newline, but ignore
- * any errors as it could be a cascading effect
- * from above.
- */
- bytes = write(l->fd, "\n", 1);
- l->total_bytes += (bytes > 0) ? bytes : 0;
-
- /*
- * Always attempt to rotate, even in the
- * face of errors above
- */
- if(l->total_bytes > l->threshold) {
- rc = audit_log_rotate(l);
- }
-
- return rc;
+static int open_log(FILE **file, const char *path, int flags, const char *fmode, mode_t mode)
+{
+ int fd;
+ int rc;
+
+ if(!file) {
+ return -EINVAL;
+ }
+
+ *file = fopen(path, fmode);
+ if(!*file) {
+ rc = -errno;
+ SLOGE("Could not open audit log file %s : %s", path, strerror(errno));
+ return rc;
+ }
+
+ rc = setvbuf(*file, NULL, _IONBF, 0);
+ if (rc != 0) {
+ rc = -errno;
+ SLOGE("Could not setvbuf the log file");
+ goto err;
+ }
+
+ fd = fileno(*file);
+ rc = fchmod(fd, mode);
+ if (rc < 0) {
+ rc = -errno;
+ SLOGE("Could not fchmod the log file");
+ goto err;
+ }
+
+ rc = fcntl(fd, F_SETFD, flags);
+ if (rc < 0) {
+ rc = -errno;
+ SLOGE("Could not fcntl the log file");
+ goto err;
+ }
+
+ return 0;
err:
- ERROR("Error in function: %s, line: %d, error: %s\n",
- __FUNCTION__, __LINE__, strerror(rc));
- goto out;
+ fclose(*file);
+ return rc;
}
-audit_log *audit_log_open(const char *logfile, const char *rotatefile, size_t threshold) {
-
- int fd = -1;
- audit_log *l = NULL;
- struct stat log_file_stats;
-
- if (stat(logfile, &log_file_stats) < 0) {
- if (errno != ENOENT) {
- ERROR("Could not stat %s: %s\n",
- logfile, strerror(errno));
- goto err;
- }
- }
- /* The existing log had data */
- else if (log_file_stats.st_size != 0){
- if (rename(logfile, rotatefile) < 0) {
- ERROR("Could not rename %s to %s: %s\n",
- logfile, rotatefile, strerror(errno));
- goto err;
- }
- }
-
- /* Open the output logfile */
- fd = open(logfile, AUDIT_LOG_FLAGS, AUDIT_LOG_MODE);
- if (fd < 0) {
- ERROR("Could not open %s: %s\n",
- logfile, strerror(errno));
- goto err;
- }
- fchmod(fd, AUDIT_LOG_MODE);
-
- l = calloc(sizeof(struct audit_log), 1);
- if (!l) {
- goto err;
- }
- l->rotatefile = strdup(rotatefile);
- if (!l->rotatefile) {
- goto err;
- }
-
- l->logfile = strdup(logfile);
- if (!l->logfile) {
- goto err;
- }
- l->fd = fd;
- l->threshold = threshold;
-
-out:
- return l;
+audit_log *audit_log_open(const char *logfile, const char *rotatefile, size_t threshold)
+{
+ int rc;
+ audit_log *l = NULL;
+ struct stat log_file_stats;
+
+ rc = stat(logfile, &log_file_stats);
+ if (rc < 0) {
+ if(errno != ENOENT) {
+ SLOGE("Could not stat audit logfile %s: %s", logfile, strerror(errno));
+ return NULL;
+ }
+ else {
+ SLOGI("Previous audit logfile not detected");
+ }
+ }
+
+ /* The existing log had data */
+ if (rc == 0 && log_file_stats.st_size >= 0) {
+ rc = rename(logfile, rotatefile);
+ if (rc < 0) {
+ SLOGE("Could not rename %s to %s: %s", logfile, rotatefile, strerror(errno));
+ return NULL;
+ }
+ SLOGI("Previous audit logfile detected, rotating\n");
+ }
+
+ l = calloc(sizeof(struct audit_log), 1);
+ if (!l) {
+ SLOGE("Out of memory while allocating audit log");
+ return NULL;
+ }
+
+ /* Open the output logfile */
+ rc = open_log(&(l->file), logfile, AUDIT_LOG_FLAGS, AUDIT_LOG_FMODE, AUDIT_LOG_MODE);
+ if (rc < 0) {
+ /* Error message handled by open_log() */
+ return NULL;
+ }
+
+ l->rotatefile = strdup(rotatefile);
+ if (!l->rotatefile) {
+ SLOGE("Out of memory while duplicating rotatefile string");
+ goto err;
+ }
+
+ l->logfile = strdup(logfile);
+ if (!l->logfile) {
+ SLOGE("Out of memory while duplicating logfile string");
+ goto err;
+ }
+
+ l->threshold = threshold;
+
+ return l;
err:
- audit_log_close(l);
- l = NULL;
- goto out;
+ audit_log_close(l);
+ return NULL;
}
-int audit_log_write_str(audit_log *l, const char *str) {
- return write_log(l, str, strlen(str));
-}
+int audit_log_write(audit_log *l, const char *fmt, ...)
+{
+ int rc;
+ va_list args;
-int audit_log_write(audit_log *l, const struct audit_reply *reply) {
- return write_log(l, reply->msg.data, reply->len);
-}
+ if (l == NULL || fmt == NULL) {
+ return -EINVAL;
+ }
+
+ va_start(args, fmt);
+ rc = vfprintf(l->file, fmt, args);
+ va_end(args);
-int audit_log_rotate(audit_log *l) {
- int rc = 0;
- if(!l) {
- rc = EINVAL;
- goto err;
- }
-
- rc = rename(l->logfile, l->rotatefile);
- if (rc < 0) {
- rc = errno;
- goto err;
- }
-
- close(l->fd);
- l->total_bytes = 0;
-
- l->fd = open(l->logfile, AUDIT_LOG_FLAGS, AUDIT_LOG_MODE);
- if(l->fd < 0) {
- rc = errno;
- goto err;
- }
- fchmod(l->fd, AUDIT_LOG_MODE);
+ if(rc < 0) {
+ SLOGE("Error writing to log file");
+ clearerr(l->file);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ l->total_bytes += rc;
out:
- return rc;
-err:
- goto out;
+ if(l->total_bytes > l->threshold) {
+ /* audit_log_rotate() handles error message */
+ rc = audit_log_rotate(l);
+ }
+
+ return rc;
+}
+
+int audit_log_rotate(audit_log *l)
+{
+ FILE *file;
+ int rc = 0;
+
+ if (!l) {
+ return -EINVAL;
+ }
+
+ rc = rename(l->logfile, l->rotatefile);
+ if (rc < 0) {
+ rc = -errno;
+ SLOGE("Could not rename audit log file \"%s\" to \"%s\", error: %s",
+ l->logfile, l->rotatefile, strerror(errno));
+ return rc;
+ }
+
+ rc = open_log(&file, l->logfile, AUDIT_LOG_FLAGS, AUDIT_LOG_FMODE, AUDIT_LOG_MODE);
+ if (rc < 0) {
+ /* Error message handled by open_log() */
+ return rc;
+ }
+
+ fclose(l->file);
+ l->total_bytes = 0;
+ l->file = file;
+
+ return 0;
}
-void audit_log_close(audit_log *l) {
- if(l) {
- if(l->logfile) {
- free(l->logfile);
- }
- if(l->rotatefile) {
- free(l->rotatefile);
- }
- if(l->fd >= 0) {
- close(l->fd);
- }
- free(l);
- l = NULL;
- }
- return;
+void audit_log_close(audit_log *l)
+{
+ if (!l) {
+ return;
+ }
+
+ free(l->logfile);
+ free(l->rotatefile);
+ if (l->file) {
+ fclose(l->file);
+ }
+ free(l);
+ return;
}
-int audit_log_put_kmsg(audit_log *l) {
-
- char *tok;
-
- int rc = 0;
- char *buf = NULL;
- int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
-
- if (len > 0) {
- len++;
- buf = malloc(len * sizeof(*buf));
- if (!buf) {
- ERROR("Out of memory\n");
- rc = ENOMEM;
- goto err;
- }
- }
- else if(len < 0) {
- ERROR("Could not read logs: %s\n",
- strerror(errno));
- rc = errno;
- goto err;
- }
-
- rc = klogctl(KLOG_READ_ALL, buf, len);
- if(rc < 0) {
- ERROR("Could not read logs: %s\n",
- strerror(errno));
- rc = errno;
- goto err;
- }
-
- buf[len-1] = '\0';
- tok = buf;
-
- while((tok = strtok(tok, "\r\n"))) {
- if(strstr(tok, " audit(")) {
- audit_log_write_str(l, tok);
- }
- tok = NULL;
- }
+int audit_log_put_kmsg(audit_log *l)
+{
+ char *tok;
+ char *audit;
+ char *type;
+ int rc = 0;
+ char *buf = NULL;
+ int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
+
+ /* No data to read */
+ if (len == 0) {
+ SLOGI("Empty kmsg");
+ return 0;
+ }
+
+ /* Error */
+ if (len < 0) {
+ rc = -errno;
+ SLOGE("Could not read kernel log length: %s", strerror(errno));
+ return rc;
+ }
+
+ /* Data to read */
+ len++;
+ buf = malloc(len * sizeof(*buf));
+ if (!buf) {
+ SLOGE("Out of memory wile allocating kmsg buffer");
+ return -ENOMEM;
+ }
+
+ rc = klogctl(KLOG_READ_ALL, buf, len);
+ if (rc < 0) {
+ rc = -errno;
+ SLOGE("Could not read kernel log data: %s", strerror(errno));
+ goto err;
+ }
+
+ buf[len - 1] = '\0';
+ tok = buf;
+
+ while ((tok = strtok(tok, "\r\n"))) {
+
+ /* Only print audit messages The SPACE is important!! as we want the
+ * audit pointer pointing to a space and not the beginning of the message.
+ * This helps ensure that we don't erroneously going down the wrong path when
+ * parsing this data.
+ * XXX Should we include the space in the AUDIT_KEYWORD macro?
+ */
+ audit = strstr(tok, " "AUDIT_KEYWORD);
+ if (audit) {
+
+ /* Place a null terminator at the space, and advance the pointer past it */
+ *audit++ = '\0';
+
+ /* If it has type field, print that than msg=<rest> */
+ type = strstr(tok, AUDIT_TYPE);
+ if (type) {
+
+ /*
+ * The type should be the the left of the space we replaced with a
+ * null terminator
+ *
+ * type is pointing to type=1400\0 and audit is pointing to audit(....\0
+ */
+ rc = audit_log_write(l, "%s msg=%s\n", type, audit);
+ if(rc < 0) {
+ /* audit_log_write handles error message */
+ goto err;
+ }
+ }
+ /* It contined the AUDIT_KEWORD but was not formatted as expected, just dump it */
+ else {
+ SLOGW("Improperly formatted kernel audit message, dumping as is");
+ rc = audit_log_write(l, "%s\n", audit);
+ if(rc < 0) {
+ /* audit_log_write handles error message */
+ goto err;
+ }
+ }
+ }
+ tok = NULL;
+ }
err:
- if (buf) {
- free(buf);
- }
-
- return 0;
+ free(buf);
+ return rc;
}
diff --git a/auditd/audit_log.h b/auditd/audit_log.h
index 454782d..3bb98e8 100644
--- a/auditd/audit_log.h
+++ b/auditd/audit_log.h
@@ -34,58 +34,45 @@ typedef struct audit_log audit_log;
* The threshold, in bytes, the log file should grow to
* until rotation.
* @return
- * A valid handle to the audit_log.
+ * A valid handle to the audit_log or NULL on failure.
*/
extern audit_log *audit_log_open(const char *logfile, const char *rotatefile, size_t threshold);
/**
- * Appends an audit reposnse to the audit log, and rotates the log if threshold is
- * passed. Note, it always finishes a message even if it is past threshold. Also
- * always appends a newline to the end of the message.
+ * Writes a formatted message to the audit log
* @param l
- * The audit log to use
- * @param reply
- * The response to write
+ * The log to write too
+ * @param fmt
+ * The fmt specifier as passed to fprintf/printf family of functions
* @return
- * 0 on success
- */
-extern int audit_log_write(audit_log *l, const struct audit_reply *reply);
-
-/**
- * Appends a string to the audit log, appending a newline, and rotating
- * the logs if needed.
- * @param l
- * The audit log to append too.
- * @param str
- * The string to append to the log.
- * @return
- * 0 on success
+ * 0 on success or -errno on error
+ *
*/
-extern int audit_log_write_str(audit_log *l, const char *str);
+extern int audit_log_write(audit_log *l, const char *fmt, ...);
/**
* Forces a rotation of the audit log.
* @param l
* The log file to use
* @return
- * 0 on success
+ * 0 on success, -errno on failure.
*/
extern int audit_log_rotate(audit_log *l);
/**
* Closes the audit log file.
* @param l
- * The log file to close, NULL's the pointer.
+ * The log file to close.
*/
extern void audit_log_close(audit_log *l);
/**
- * Searches once through kmesg for type=1400
+ * Searches once through kmsg for type=1400
* kernel messages and logs them to the audit log
* @param l
- * The log to append too
+ * The log to append too
* @return
- * 0 on success
+ * 0 on success, -errno on failure.
*/
extern int audit_log_put_kmsg(audit_log *l);
diff --git a/auditd/auditd.c b/auditd/auditd.c
index f56f659..7291d34 100644
--- a/auditd/auditd.c
+++ b/auditd/auditd.c
@@ -16,6 +16,8 @@
* Written by William Roberts <w.roberts@sta.samsung.com>
*/
+#define LOG_TAG "auditd"
+
#include <stdio.h>
#include <stdlib.h>
@@ -28,17 +30,20 @@
#include <syslog.h>
#include <unistd.h>
+#include <sys/capability.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <cutils/log.h>
#include <cutils/klog.h>
+
+#include <cutils/log.h>
#include <private/android_filesystem_config.h>
#include <linux/capability.h>
#include <linux/prctl.h>
-#include "auditd.h"
#include "libaudit.h"
#include "audit_log.h"
@@ -48,168 +53,172 @@
* 1. Add a socket interface for sending events
*/
-#define LOG_DIR "/data/misc/audit"
-#define LOG_FILE LOG_DIR "/audit.log"
-#define OLD_LOG_FILE LOG_DIR "/audit.old"
+#ifndef AUDITD_MAX_LOG_FILE_SIZEKB
+#error "AUDITD_MAX_LOG_FILE_SIZEKB not defined by makefile!"
+#endif
+
+#define AUDITD_LOG_DIR "/data/misc/audit"
+#define AUDITD_LOG_FILE AUDITD_LOG_DIR "/audit.log"
+#define AUDITD_OLD_LOG_FILE AUDITD_LOG_DIR "/audit.old"
-#define MAX_LOG_FILE_SIZE (1024 * 100)
+#define AUDITD_MAX_LOG_FILE_SIZE (1024 * AUDITD_MAX_LOG_FILE_SIZEKB)
static volatile int quit = 0;
-static void signal_handler(int sig) {
- switch (sig) {
- case SIGINT:
- quit = 1;
- break;
- }
- return;
+static void signal_handler(int sig)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ quit = 1;
+ break;
+ }
+ return;
}
-static void usage(char *cmd) {
- printf("%s - log audit events from the kernel\n"
- "OPTIONS\n"
- "-k - search dmesg on startup for audit events\n"
- "\n",
- cmd);
+static void usage(char *cmd)
+{
+ printf("%s - log audit events from the kernel\n"
+ "OPTIONS\n"
+ "-k - search dmesg on startup for audit events\n"
+ "\n", cmd);
}
#define RAISE(ary, c) ary[CAP_TO_INDEX(c)].permitted |= CAP_TO_MASK(c);
-static void drop_privileges_or_die(void) {
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
-
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- ERROR("Failed on prctl KEEPCAPS: %s\n", strerror(errno));
- exit(1);
- }
-
- if (setgid(AID_AUDIT) < 0) {
- ERROR("Failed on setgid: %s\n", strerror(errno));
- exit(1);
- }
-
- if (setuid(AID_AUDIT) < 0) {
- ERROR("Failed on setuid: %s\n", strerror(errno));
- exit(1);
- }
-
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
-
- RAISE(capdata, CAP_AUDIT_CONTROL);
- RAISE(capdata, CAP_SYSLOG);
-
- capdata[0].effective = capdata[0].permitted;
- capdata[1].effective = capdata[1].permitted;
- capdata[0].inheritable = 0;
- capdata[1].inheritable = 0;
-
- if (capset(&capheader, &capdata[0]) < 0) {
- ERROR("Failed on capset: %s\n", strerror(errno));
- exit(1);
- }
+static void drop_privileges_or_die(void)
+{
+
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ SLOGE("Failed on prctl KEEPCAPS: %s", strerror(errno));
+ exit(1);
+ }
+
+ if (setgid(AID_AUDIT) < 0) {
+ SLOGE("Failed on setgid: %s", strerror(errno));
+ exit(1);
+ }
+
+ if (setuid(AID_AUDIT) < 0) {
+ SLOGE("Failed on setuid: %s", strerror(errno));
+ exit(1);
+ }
+
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ RAISE(capdata, CAP_AUDIT_CONTROL);
+ RAISE(capdata, CAP_SYSLOG);
+
+ capdata[0].effective = capdata[0].permitted;
+ capdata[1].effective = capdata[1].permitted;
+ capdata[0].inheritable = 0;
+ capdata[1].inheritable = 0;
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ SLOGE("Failed on capset: %s", strerror(errno));
+ exit(1);
+ }
}
-int main(int argc, char *argv[]) {
-
- int c;
-
- struct audit_reply rep;
- struct sigaction action;
-
- int rc;
- int audit_fd = -1;
- int check_kernel_log = 0;
-
- audit_log *l = NULL;
- ssize_t total_bytes = 0;
- struct pollfd pfds;
-
- INFO("auditd: starting up\n");
-
- drop_privileges_or_die();
-
- /* register the signal handler */
- action.sa_handler = signal_handler;
- sigemptyset (&action.sa_mask);
- action.sa_flags = 0;
- rc = sigaction (SIGINT, &action, NULL);
- if (rc < 0) {
- rc = errno;
- ERROR("Failed on set signal handler: %s\n", strerror(errno));
- goto err;
- }
-
- while((c = getopt(argc, argv, "k")) != -1) {
- switch(c) {
- case 'k':
- check_kernel_log = 1;
- break;
- default:
- usage(argv[0]);
- goto err;
- }
- }
-
- /* Open the netlink socket for audit events */
- audit_fd = audit_open();
- if (audit_fd < 0) {
- rc = errno;
- ERROR("Failed on audit_set_pid with error: %s\n", strerror(errno));
- goto err;
- }
-
- l = audit_log_open(LOG_FILE, OLD_LOG_FILE, MAX_LOG_FILE_SIZE);
- if(!l) {
- ERROR("Failed on audit_log_open\n");
- goto err;
- }
-
- if (audit_set_pid(audit_fd, getpid(), WAIT_YES) < 0) {
- rc = errno;
- ERROR("Failed on audit_set_pid with error: %s\n", strerror(errno));
- goto err;
- }
-
- pfds.fd = audit_fd;
- pfds.events = POLLIN;
-
- if(check_kernel_log) {
- audit_log_put_kmsg(l);
- }
-
- while (!quit) {
-
- /* Start reading for events */
- rc = poll(&pfds, 1, -1);
- if (rc == 0) {
- continue;
- }
- else if(rc < 0) {
- if (errno != EINTR) {
- ERROR("Failed to poll audit log socket: %d : %s\n", errno, strerror(errno));
- }
- continue;
- }
-
- if (audit_get_reply(audit_fd, &rep, GET_REPLY_BLOCKING, 0) < 0) {
- ERROR("Failed on audit_get_reply with error: %s\n", strerror(errno));
- continue;
- }
-
- audit_log_write(l, &rep);
- /* Keep reading for events */
- }
+int main(int argc, char *argv[])
+{
+ int c;
+ int rc;
+ int audit_fd = -1;
+ int check_kernel_log = 0;
+
+ struct pollfd pfds;
+ struct audit_reply rep;
+ struct sigaction action;
+ audit_log *l = NULL;
+
+ SLOGI("Starting up");
+
+ drop_privileges_or_die();
+
+ /* register the signal handler */
+ action.sa_handler = signal_handler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ rc = sigaction(SIGINT, &action, NULL);
+ if (rc < 0) {
+ rc = errno;
+ SLOGE("Failed on set signal handler: %s", strerror(errno));
+ goto err;
+ }
+
+ while ((c = getopt(argc, argv, "k")) != -1) {
+ switch (c) {
+ case 'k':
+ check_kernel_log = 1;
+ break;
+ default:
+ usage(argv[0]);
+ goto err;
+ }
+ }
+
+ /* Open the netlink socket for audit events */
+ audit_fd = audit_open();
+ if (audit_fd < 0) {
+ rc = errno;
+ SLOGE("Failed on audit_set_pid with error: %s", strerror(errno));
+ goto err;
+ }
+
+ l = audit_log_open(AUDITD_LOG_FILE, AUDITD_OLD_LOG_FILE, AUDITD_MAX_LOG_FILE_SIZE);
+ if (!l) {
+ SLOGE("Failed on audit_log_open");
+ goto err;
+ }
+
+ if (audit_set_pid(audit_fd, getpid(), WAIT_YES) < 0) {
+ rc = errno;
+ SLOGE("Failed on audit_set_pid with error: %s", strerror(errno));
+ goto err;
+ }
+
+ pfds.fd = audit_fd;
+ pfds.events = POLLIN;
+
+ if (check_kernel_log) {
+ audit_log_put_kmsg(l);
+ }
+
+ while (!quit) {
+
+ /* Start reading for events */
+ rc = poll(&pfds, 1, -1);
+ if (rc == 0) {
+ continue;
+ } else if (rc < 0) {
+ if (errno != EINTR) {
+ SLOGE("Failed to poll audit log socket: %d : %s", errno, strerror(errno));
+ }
+ continue;
+ }
+
+ if (audit_get_reply(audit_fd, &rep, GET_REPLY_BLOCKING, 0) < 0) {
+ SLOGE("Failed on audit_get_reply with error: %s", strerror(errno));
+ continue;
+ }
+
+ audit_log_write(l, "type=%d msg=%.*s\n", rep.type, rep.len, rep.msg.data);
+ /* Keep reading for events */
+ }
err:
- INFO("auditd: exiting\n");
- if (audit_fd >= 0) {
- audit_set_pid(audit_fd, 0, WAIT_NO);
- audit_close(audit_fd);
- }
- audit_log_close(l);
- return rc;
+ SLOGI("Exiting");
+ if (audit_fd >= 0) {
+ audit_set_pid(audit_fd, 0, WAIT_NO);
+ audit_close(audit_fd);
+ }
+ audit_log_close(l);
+ return rc;
}
diff --git a/auditd/auditd.h b/auditd/auditd.h
deleted file mode 100644
index 594b670..0000000
--- a/auditd/auditd.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2012, Samsung Telecommunications of America
- *
- * 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 William Roberts <w.roberts@sta.samsung.com>
- */
-
-#ifndef _AUDITD_H_
-#define _AUDITD_H_
-
-#define LOG_TAG "auditd"
-
-#include <cutils/log.h>
-
-/* Debugging statements that go to the system log
- * aka logcat.
- * The naming convention follows the severity of the
- * message to print.
- */
-#define INFO(...) SLOGI(__VA_ARGS__)
-#define ERROR(...) SLOGE(__VA_ARGS__)
-#define WARNING(...) SLOGW(__VA_ARGS__)
-
-/*
- * Given an errno variable, returns the abs value of it
- * Some of the existing interfaces return a -errno instead of -1
- * with errno set. This is just a way of ensuring that the errno
- * you are using, is a positive value.
- */
-#define GETERRNO(x) ((x < 0) ? -x : x)
-
-#endif
-
diff --git a/auditd/libaudit.c b/auditd/libaudit.c
index 905e8af..06e5557 100644
--- a/auditd/libaudit.c
+++ b/auditd/libaudit.c
@@ -16,63 +16,316 @@
* Written by William Roberts <w.roberts@sta.samsung.com>
*
*/
+
#include <errno.h>
+#include <fcntl.h>
#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#define LOG_TAG "libaudit"
+#include <cutils/log.h>
#include <cutils/klog.h>
-#include "auditd.h"
#include "libaudit.h"
-/* Only should be used internally */
-extern int audit_send(int fd, int type, const void *data, unsigned int size);
-
-/* Depending on static to initialize this to 0 */
-static const struct audit_status _new_audit_status;
-
-int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode) {
-
- int rc;
- int line;
- struct audit_reply rep;
- struct audit_status status = _new_audit_status;
-
- /*
- * In order to set the auditd PID we send an audit message over the netlink socket
- * with the pid field of the status struct set to our current pid, and the
- * the mask set to AUDIT_STATUS_PID
- */
- status.pid = pid;
- status.mask = AUDIT_STATUS_PID;
-
- /* Let the kernel know this pid will be registering for audit events */
- rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
- if (rc < 0) {
- line = __LINE__;
- goto err;
- }
-
- /*
- * In a request where we need to wait for a response, wait for the message
- * and discard it. This message confirms and sync's us with the kernel.
- * This daemon is now registered as the audit logger. Only wait if the
- * wmode is != WAIT_NO
- */
- if (wmode != WAIT_NO) {
- /* TODO
- * If the daemon dies and restarts the message didn't come back,
- * so I went to non-blocking and it seemed to fix the bug.
- * Need to investigate further.
- */
- (void)audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
- }
+/**
+ * Copies the netlink message data to the reply structure.
+ *
+ * When the kernel sends a response back, we must adjust the response from the
+ * netlink message header.
+ * All the data is in rep->msg but belongs in the type enforced fields in the struct.
+ *
+ * @param rep
+ * The response
+ * @param len
+ * The length of the message, len must never be less than 0!
+ * @return
+ * This function returns 0 on success, else -error.
+ */
+static int set_internal_fields(struct audit_reply *rep, ssize_t len)
+{
+ int rc;
+
+ /*
+ * We end up setting a specific field in the union, but since it
+ * is a union and they are all of type pointer, we can just clear
+ * one.
+ */
+ rep->status = NULL;
+
+ /* Set the response from the netlink message */
+ rep->nlh = &rep->msg.nlh;
+ rep->len = rep->msg.nlh.nlmsg_len;
+ rep->type = rep->msg.nlh.nlmsg_type;
+
+ /* Check if the reply from the kernel was ok */
+ if (!NLMSG_OK(rep->nlh, (size_t)len)) {
+ rc = (len == sizeof(rep->msg)) ? -EFBIG : -EBADE;
+ SLOGE("Bad kernel response %s", strerror(-rc));
+ return rc;
+ }
+
+ /* Next we'll set the data structure to point to msg.data. This is
+ * to avoid having to use casts later. */
+ if (rep->type == NLMSG_ERROR) {
+ rep->error = NLMSG_DATA(rep->nlh);
+ } else if (rep->type == AUDIT_GET) {
+ rep->status = NLMSG_DATA(rep->nlh);
+ } else if (rep->type == AUDIT_LIST_RULES) {
+ rep->ruledata = NLMSG_DATA(rep->nlh);
+ } else if (rep->type == AUDIT_SIGNAL_INFO) {
+ rep->signal_info = NLMSG_DATA(rep->nlh);
+ }
+ /* If it is not any of the above specific events, it must be a generic message */
+ else {
+ rep->message = NLMSG_DATA(rep->nlh);
+ }
+
+ return 0;
+}
+
+/**
+ * Waits for an ack from the kernel
+ * @param fd
+ * The netlink socket fd
+ * @param seq
+ * The current sequence number were acking on
+ * @return
+ * This function returns 0 on success, else -errno.
+ */
+static int get_ack(int fd, int16_t seq)
+{
+ int rc;
+ struct audit_reply rep;
+
+ /* Sanity check the input, this is an internal interface this shouldn't happen */
+ if (fd < 0) {
+ return -EINVAL;
+ }
+
+ rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
+ if (rc < 0) {
+ return rc;
+ }
+
+ if (rep.type == NLMSG_ERROR) {
+ audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
+ if (rep.error->error) {
+ return -rep.error->error;
+ }
+ }
+
+ if ((int16_t)rep.nlh->nlmsg_seq != seq) {
+ SLOGW("Expected sequence number between user space and kernel space is out of skew, "
+ "expected %u got %u", seq, rep.nlh->nlmsg_seq);
+ }
+
+ return 0;
+}
+
+/**
+ *
+ * @param fd
+ * The netlink socket fd
+ * @param type
+ * The type of netlink message
+ * @param data
+ * The data to send
+ * @param size
+ * The length of the data in bytes
+ * @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 rc;
+ static int16_t sequence = 0;
+ struct audit_message req;
+ struct sockaddr_nl addr;
+
+ memset(&req, 0, sizeof(req));
+ memset(&addr, 0, sizeof(addr));
+
+ /* We always send netlink messaged */
+ addr.nl_family = AF_NETLINK;
+
+ /* Set up the netlink headers */
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_len = NLMSG_SPACE(size);
+ req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+
+ /*
+ * Check for a valid fd, even though sendto would catch this, its easier to always
+ * blindly increment the sequence number
+ */
+ if (fd < 0) {
+ return -EBADF;
+ }
+
+ /* Ensure the message is not too big */
+ if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) {
+ SLOGE("netlink message is too large");
+ return -EINVAL;
+ }
+
+ /* Only memcpy in the data if it was specified */
+ if (size && data)
+ memcpy(NLMSG_DATA(&req.nlh), data, size);
+
+ /*
+ * Only increment the sequence number on a guarantee
+ * you will send it to the kernel.
+ *
+ * Also, the sequence is defined as a u32 in the kernel
+ * struct. Using an int here might not work on 32/64 bit splits. A
+ * signed 64 bit value can overflow a u32..but a u32
+ * might not fit in the response, so we need to use s32.
+ * Which is still kind of hackish since int could be 16 bits
+ * in size. The only safe type to use here is a signed 16
+ * bit value.
+ */
+ req.nlh.nlmsg_seq = ++sequence;
+
+ /* While failing and its due to interrupts */
+ do {
+ /* Try and send the netlink message */
+ rc = sendto(fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr*) &addr, sizeof(addr));
+
+ } while (rc < 0 && errno == EINTR);
+
+ /* Not all the bytes were sent */
+ if ((uint32_t) rc != req.nlh.nlmsg_len) {
+ rc = -EPROTO;
+ goto out;
+ } else if (rc < 0) {
+ rc = -errno;
+ SLOGE("Error sending data over the netlink socket: %s", strerror(-errno));
+ goto out;
+ }
+
+ /* We sent all the bytes, get the ack */
+ rc = get_ack(fd, sequence);
+
+ /* If the ack failed, return the error, else return the sequence number */
+ rc = (rc == 0) ? (int) sequence : rc;
out:
- return rc;
+ /* Don't let sequence roll to negative */
+ if (sequence < 0) {
+ SLOGW("Auditd to Kernel sequence number has rolled over");
+ sequence = 0;
+ }
+
+ return rc;
+}
+
+int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode)
+{
+ int rc;
+ struct audit_reply rep;
+ struct audit_status status;
+
+ memset(&status, 0, sizeof(status));
+
+ /*
+ * In order to set the auditd PID we send an audit message over the netlink socket
+ * with the pid field of the status struct set to our current pid, and the
+ * the mask set to AUDIT_STATUS_PID
+ */
+ status.pid = pid;
+ status.mask = AUDIT_STATUS_PID;
+
+ /* Let the kernel know this pid will be registering for audit events */
+ rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+ if (rc < 0) {
+ SLOGE("Could net set pid for audit events, error: %s", strerror(-rc));
+ return rc;
+ }
+
+ /*
+ * In a request where we need to wait for a response, wait for the message
+ * and discard it. This message confirms and sync's us with the kernel.
+ * This daemon is now registered as the audit logger. Only wait if the
+ * wmode is != WAIT_NO
+ */
+ if (wmode != WAIT_NO) {
+ /* TODO
+ * If the daemon dies and restarts the message didn't come back,
+ * so I went to non-blocking and it seemed to fix the bug.
+ * Need to investigate further.
+ */
+ audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
+ }
+
+ return 0;
+}
+
+int audit_open()
+{
+ return socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
+}
+
+int audit_get_reply(int fd, struct audit_reply *rep, reply_t block, int peek)
+{
+ ssize_t len;
+ int flags;
+
+ struct sockaddr_nl nladdr;
+ socklen_t nladdrlen = sizeof(nladdr);
+
+ if (fd < 0) {
+ return -EBADF;
+ }
+
+ /* Set up the flags for recv from */
+ flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
+ flags |= peek;
+
+ /*
+ * Get the data from the netlink socket but on error we need to be carefull,
+ * the interface shows that EINTR can never be returned, other errors, however,
+ * can be returned.
+ */
+ do {
+ len = recvfrom(fd, &rep->msg, sizeof(rep->msg), flags, (struct sockaddr*) &nladdr,
+ &nladdrlen);
+
+ /*
+ * EAGAIN and EINTR should be re-tried until success or
+ * 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;
+ }
+ SLOGE("Error receiving from netlink socket, error: %s", strerror(errno));
+ return -errno;
+ }
+
+ /* 0 or greater indicates success */
+ } while (len < 0);
+
+ if (nladdrlen != sizeof(nladdr)) {
+ SLOGE("Protocol fault, error: %s", strerror(EPROTO));
+ return -EPROTO;
+ }
+
+ /* Make sure the netlink message was not spoof'd */
+ if (nladdr.nl_pid) {
+ SLOGE("Invalid netlink pid received, expected 0 got: %d", nladdr.nl_pid);
+ return -EINVAL;
+ }
+
+ return set_internal_fields(rep, len);
+}
-err:
- errno = GETERRNO(rc);
- ERROR("%s Failed in %s around line %d with error: %s\n", __FILE__, __FUNCTION__, line, strerror(errno));
- rc = -1;
- goto out;
+void audit_close(int fd)
+{
+ int rc = close(fd);
+ if (rc < 0) {
+ SLOGE("Attempting to close invalid fd %d, error: %s", fd, strerror(errno));
+ }
+ return;
}
diff --git a/auditd/libaudit.h b/auditd/libaudit.h
index 4d96059..fbaa7b9 100644
--- a/auditd/libaudit.h
+++ b/auditd/libaudit.h
@@ -1,5 +1,24 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ *
+ * 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 William Roberts <w.roberts@sta.samsung.com>
+ */
+
#ifndef _LIBAUDIT_H_
#define _LIBAUDIT_H_
+
#include <stdint.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -8,13 +27,20 @@
#define MAX_AUDIT_MESSAGE_LENGTH 8970
-typedef enum { GET_REPLY_BLOCKING=0, GET_REPLY_NONBLOCKING } reply_t;
-typedef enum { WAIT_NO, WAIT_YES } rep_wait_t;
+typedef enum {
+ GET_REPLY_BLOCKING=0,
+ GET_REPLY_NONBLOCKING
+} reply_t;
+
+typedef enum {
+ WAIT_NO,
+ WAIT_YES
+} rep_wait_t;
struct audit_sig_info {
- uid_t uid;
- pid_t pid;
- char ctx[0];
+ uid_t uid;
+ pid_t pid;
+ char ctx[0];
};
struct audit_message {
@@ -37,10 +63,49 @@ struct audit_reply {
};
};
+/**
+ * Opens a connection to the Audit netlink socket
+ * @return
+ * A valid fd on success or < 0 on error with errno set.
+ * Returns the same errors as man 2 socket.
+ */
extern int audit_open(void);
+
+/**
+ * Closes the fd returned from audit_open()
+ * @param fd
+ * The fd to close
+ */
extern void audit_close(int fd);
+
+/**
+ *
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param rep
+ * The response struct to store the response in.
+ * @param block
+ * Whether or not to block on IO
+ * @param peek
+ * Whether or not we are to remove the message from
+ * the queue when we do a read on the netlink socket.
+ * @return
+ * This function returns 0 on success, else -errno.
+ */
extern int audit_get_reply(int fd, struct audit_reply *rep, reply_t block,
int peek);
+
+/**
+ * Sets a pid to recieve audit netlink events from the kernel
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param pid
+ * The pid whom to set as the reciever of audit messages
+ * @param wmode
+ * Whether or not to block on the underlying socket io calls.
+ * @return
+ * This function returns 0 on success, -errno on error.
+ */
extern int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode);
#endif
diff --git a/auditd/netlink.c b/auditd/netlink.c
deleted file mode 100644
index 27cbfb9..0000000
--- a/auditd/netlink.c
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright 2012, Samsung Telecommunications of America
- *
- * 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 William Roberts <w.roberts@sta.samsung.com>
- *
- */
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdint.h>
-
-#include <cutils/klog.h>
-
-#include "auditd.h"
-#include "libaudit.h"
-
-/* Relying on static to initialize to 0, these are used to initialize structs to 0 */
-static const struct audit_message _new_req;
-static const struct sockaddr_nl _new_addr;
-
-/**
- * Copies the netlink message data to the reply structure.
- *
- * When the kernel sends a response back, we must adjust the response from the nlmsghdr.
- * All the data is is rep->msg but belongs in type enforced fields in the struct.
- *
- * @param rep
- * The response
- * @param len
- * The length of ther message, len must never be less than 0!
- * @return
- * This function returns 0 on success.
- */
-static int set_internal_fields(struct audit_reply *rep, ssize_t len) {
-
- int rc = 0;
- int line = 0;
-
- /*
- * We end up setting a specific field in the union, but since it
- * is a union and they are all of type pointer, we can just clear
- * one.
- */
- rep->status = NULL;
-
- /* Set the response from the netlink message */
- rep->nlh = &rep->msg.nlh;
- rep->len = rep->msg.nlh.nlmsg_len;
- rep->type = rep->msg.nlh.nlmsg_type;
-
- /* Check if the reply from the kernel was ok */
- if (!NLMSG_OK(rep->nlh, (size_t)len)) {
- rc = (len == sizeof(rep->msg)) ? EFBIG : EBADE;
- line = __LINE__;
- goto err;
- }
-
- /* Next we'll set the data structure to point to msg.data. This is
- * to avoid having to use casts later. */
- if (rep->type == NLMSG_ERROR) {
- rep->error = NLMSG_DATA(rep->nlh);
- }
- else if (rep->type == AUDIT_GET) {
- rep->status = NLMSG_DATA(rep->nlh);
- }
- else if (rep -> type == AUDIT_LIST_RULES) {
- rep->ruledata = NLMSG_DATA(rep->nlh);
- }
- else if (rep->type == AUDIT_SIGNAL_INFO) {
- rep->signal_info = NLMSG_DATA(rep->nlh);
- }
- /* If it is not any of the above specific events, it must be a generic message */
- else {
- rep->message = NLMSG_DATA(rep->nlh);
- }
-out:
- return rc;
-err:
- errno = GETERRNO(rc);
- ERROR("%s Failed in %s around line %d with error: %s\n", __FILE__, __FUNCTION__, line, strerror(errno));
- rc = -1;
- goto out;
-}
-
-/**
- * Waits for an ack from the kernel
- * @param fd
- * The netlink socket fd
- * @param seq
- * The current sequence number were acking on
- * @return
- * 0 on success
- */
-static int get_ack(int fd, int16_t seq) {
-
- int rc = 0;
- int line = 0;
-
- struct audit_reply rep;
-
- /* Sanity check the input, this is an internal interface this shouldn't happen */
- if (fd < 0) {
- rc = EINVAL;
- line = __LINE__;
- goto err;
- }
-
- rc = -audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
- if(rc < 0) {
- line = __LINE__;
- goto err;
- }
- else if (rep.type == NLMSG_ERROR) {
- (void)audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
- if (rep.error->error) {
- line = __LINE__;
- rc = rep.error->error;
- goto err;
- }
- }
-out:
- return rc;
-err:
- errno = GETERRNO(rc);
- ERROR("%s Failed in %s around line %d with error: %s\n", __FILE__, __FUNCTION__, line, strerror(errno));
- rc = -1;
- goto out;
-}
-
-/**
- * Opens a connection to the Audit netlink socket
- * @return
- * A valid fd on success or < 0 on error with errno set.
- * Returns the same errors as man 2 socket.
- */
-int audit_open() {
- return socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
-}
-
-/**
- *
- * @param fd
- * The fd returned by a call to audit_open()
- * @param rep
- * The response struct to store the response in.
- * @param block
- * Whether or not to block on IO
- * @param peek
- * Whether or not we are to remove the message from
- * the queue when we do a read on the netlink socket.
- * @return
- * 0 on success, errno on error
- */
-int audit_get_reply(int fd, struct audit_reply *rep, reply_t block, int peek) {
-
- int rc;
- ssize_t len;
- int flags;
- int line = 0;
-
- struct sockaddr_nl nladdr;
- socklen_t nladdrlen = sizeof(nladdr);
-
- if (fd < 0) {
- rc = EBADF;
- line = __LINE__;
- goto err;
- }
-
- /* Set up the flags for recv from */
- flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
- flags |= peek;
-
- /*
- * Get the data from the netlink socket but on error we need to be carefull,
- * the interface shows that EINTR can never be returned, other errors, however,
- * can be returned.
- */
- do {
-
- len = recvfrom(fd, &rep->msg, sizeof(rep->msg), flags,
- (struct sockaddr*)&nladdr, &nladdrlen);
-
- /*
- * EAGAIN and EINTR should be re-tried until success or
- * another error manifests.
- */
- if (len < 0 && errno != EINTR) {
- if (block == GET_REPLY_NONBLOCKING && errno == EAGAIN) {
- rc = 0;
- goto out;
- }
- rc = errno;
- line = __LINE__;
- goto err;
- }
-
- /* 0 or greater indicates success */
- } while (len < 0);
-
- if (nladdrlen != sizeof(nladdr)) {
- rc = EPROTO;
- line == __LINE__;
- goto err;
- }
-
- /* Make sure the netlink message was not spoof'd */
- if (nladdr.nl_pid) {
- rc = EINVAL;
- line == __LINE__;
- goto err;
- }
-
- rc = set_internal_fields(rep, len);
- if (rc < 0) {
- rc = errno;
- line == __LINE__;
- goto err;
- }
-
-out:
- return rc;
-err:
- errno = GETERRNO(rc);
- ERROR("%s Failed in %s around line %d with error: %s\n", __FILE__, __FUNCTION__, line, strerror(errno));
- rc = -1;
- goto out;
-}
-
-/**
- * Closes the fd returned from audit_open()
- * @param fd
- * The fd to close
- *
- * @note
- * This function was left void to be complaint
- * to an existing interface, however it clears
- * errno on entry, so errno can be used to check
- * if the fd you gave close was wrong.
- *
- */
-void audit_close(int fd) {
-
- int line = 0;
- int rc = close(fd);
- if(rc) {
- line == __LINE__;
- rc = errno;
- goto err;
- }
-out:
- return;
-
-err:
- errno = GETERRNO(rc);
- ERROR("%s Failed in %s around line %d with error: %s\n", __FILE__, __FUNCTION__, line, strerror(errno));
- goto out;
-}
-
-/**
- *
- * @param fd
- * The netlink socket fd
- * @param type
- * The type of netlink message
- * @param data
- * The data to send
- * @param size
- * The length of the data in bytes
- * @return
- * Sequence number on success or -errno on error.
- */
-int audit_send(int fd, int type, const void *data, unsigned int size) {
-
- int rc = 0;
- static int16_t sequence = 0;
- struct audit_message req = _new_req;
- struct sockaddr_nl addr = _new_addr;
-
- /* We always send netlink messaged */
- addr.nl_family = AF_NETLINK;
-
- /* Set up the netlink headers */
- req.nlh.nlmsg_type = type;
- req.nlh.nlmsg_len = NLMSG_SPACE(size);
- req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
-
- /*
- * Check for a valid fd, even though sendto would catch this, its easier to always
- * blindly increment the sequence number
- */
- if (fd < 0) {
- rc = EBADF;
- goto err;
- }
-
- /* Ensure the message is not too big */
- if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) {
- rc = EINVAL;
- goto err;
- }
-
- /* Only memcpy in the data if it was specified */
- if (size && data)
- memcpy(NLMSG_DATA(&req.nlh), data, size);
-
- /*
- * Only increment the sequence number on a guarantee
- * you will send it to the kernel.
- *
- * Also, the sequence is defined as a u32 in the kernel
- * struct. Using an int here might not work on 32/64 bit splits. A
- * signed 64 bit value can overflow a u32..but a u32
- * might not fit in the response, so we need to use s32.
- * Which is still kind of hackish since int could be 16 bits
- * in size. The only safe type to use here is a signed 16
- * bit value.
- */
- req.nlh.nlmsg_seq = ++sequence;
-
- /* While failing and its due to interrupts */
- do {
- /* Try and send the netlink message */
- rc = sendto(fd, &req, req.nlh.nlmsg_len, 0,
- (struct sockaddr*)&addr, sizeof(addr));
-
- } while (rc < 0 && errno == EINTR);
-
- /* Not all the bytes were sent */
- if ((uint32_t)rc != req.nlh.nlmsg_len) {
- rc = EPROTO;
- goto err;
- }
- else if (rc < 0) {
- rc = errno;
- goto out;
- }
-
- /* We sent all the bytes, get the ack */
- rc = (get_ack(fd, sequence) == 0) ? (int)sequence : rc;
-
-out:
-
- /* Don't let sequence roll to negative */
- sequence = (sequence < 0) ? 0 : sequence;
-
- return rc;
-
-err:
- errno = rc;
- rc = -rc;
- goto out;
-}
-