diff options
author | Stephen Smalley <sds@tycho.nsa.gov> | 2013-04-05 10:28:30 -0400 |
---|---|---|
committer | Ricardo Cerqueira <cyanogenmod@cerqueira.org> | 2013-07-18 20:38:39 +0100 |
commit | 01996abeda1240e04a86a0393e0b553dd6310412 (patch) | |
tree | 01e9bce924c7008e12e5ef2cfdcfb828bc6d9e00 | |
parent | 911cbea54fe75341069c66d300e35ad9bde0e591 (diff) | |
download | system_core-01996abeda1240e04a86a0393e0b553dd6310412.zip system_core-01996abeda1240e04a86a0393e0b553dd6310412.tar.gz system_core-01996abeda1240e04a86a0393e0b553dd6310412.tar.bz2 |
Sync with master auditd.
-rw-r--r-- | auditd/Android.mk | 12 | ||||
-rw-r--r-- | auditd/README | 44 | ||||
-rw-r--r-- | auditd/audit_log.c | 526 | ||||
-rw-r--r-- | auditd/audit_log.h | 39 | ||||
-rw-r--r-- | auditd/auditd.c | 311 | ||||
-rw-r--r-- | auditd/auditd.h | 44 | ||||
-rw-r--r-- | auditd/libaudit.c | 351 | ||||
-rw-r--r-- | auditd/libaudit.h | 75 | ||||
-rw-r--r-- | auditd/netlink.c | 367 |
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; -} - |