[TOMOYO #16 11/25] TOMOYO: Add auditing support.

From: Tetsuo Handa
Date: Sun Oct 04 2009 - 08:55:50 EST


This patch adds auditing code used by TOMOYO.

TOMOYO generates audit logs ( /sys/kernel/security/tomoyo/grant_log and
/sys/kernel/security/tomoyo/reject_log ) in the form of domain policy
( /sys/kernel/security/domain_policy ).

Users can configure whether to generate audit logs or not via profile
( /sys/kernel/security/tomoyo/profile ). By default all information usable for
"if" clause is audited. Users can suppress unneeded information.
Memory quota for audit logs is configurable via profile and meminfo interface
( /sys/kernel/security/tomoyo/meminfo ).

tomoyo_profile()->audit is guaranteed to point to either default configuration
( tomoyo_default_profile ) or per profile configuration.

Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
---
security/tomoyo/audit.c | 561 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 561 insertions(+)

--- /dev/null
+++ security-testing-2.6/security/tomoyo/audit.c
@@ -0,0 +1,561 @@
+/*
+ * security/tomoyo/audit.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/**
+ * tomoyo_print_bprm - Print "struct linux_binprm" for auditing.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @dump: Pointer to "struct tomoyo_page_dump".
+ *
+ * Returns the contents of @bprm on success, NULL otherwise.
+ */
+static char *tomoyo_print_bprm(struct linux_binprm *bprm,
+ struct tomoyo_page_dump *dump)
+{
+ static const int tomoyo_buffer_len = 4096 * 2;
+ char *buffer = kzalloc(tomoyo_buffer_len, GFP_KERNEL);
+ char *cp;
+ char *last_start;
+ int len;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ int argv_count = bprm->argc;
+ int envp_count = bprm->envc;
+ bool truncated = false;
+ if (!buffer)
+ return NULL;
+ len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ ");
+ cp = buffer + len;
+ if (!argv_count) {
+ memmove(cp, "} envp[]={ ", 11);
+ cp += 11;
+ }
+ last_start = cp;
+ while (argv_count || envp_count) {
+ if (!tomoyo_dump_page(bprm, pos, dump))
+ goto out;
+ pos += PAGE_SIZE - offset;
+ /* Read. */
+ while (offset < PAGE_SIZE) {
+ const char *kaddr = dump->data;
+ const unsigned char c = kaddr[offset++];
+ if (cp == last_start)
+ *cp++ = '"';
+ if (cp >= buffer + tomoyo_buffer_len - 32) {
+ /* Reserve some room for "..." string. */
+ truncated = true;
+ } else if (c == '\\') {
+ *cp++ = '\\';
+ *cp++ = '\\';
+ } else if (c > ' ' && c < 127) {
+ *cp++ = c;
+ } else if (!c) {
+ *cp++ = '"';
+ *cp++ = ' ';
+ last_start = cp;
+ } else {
+ *cp++ = '\\';
+ *cp++ = (c >> 6) + '0';
+ *cp++ = ((c >> 3) & 7) + '0';
+ *cp++ = (c & 7) + '0';
+ }
+ if (c)
+ continue;
+ if (argv_count) {
+ if (--argv_count == 0) {
+ if (truncated) {
+ cp = last_start;
+ memmove(cp, "... ", 4);
+ cp += 4;
+ }
+ memmove(cp, "} envp[]={ ", 11);
+ cp += 11;
+ last_start = cp;
+ truncated = false;
+ }
+ } else if (envp_count) {
+ if (--envp_count == 0) {
+ if (truncated) {
+ cp = last_start;
+ memmove(cp, "... ", 4);
+ cp += 4;
+ }
+ }
+ }
+ if (!argv_count && !envp_count)
+ break;
+ }
+ offset = 0;
+ }
+ *cp++ = '}';
+ *cp = '\0';
+ return buffer;
+ out:
+ snprintf(buffer, tomoyo_buffer_len - 1,
+ "argv[]={ ... } envp[]= { ... }");
+ return buffer;
+}
+
+/**
+ * tomoyo_filetype - Get string representation of file type.
+ *
+ * @mode: Mode value for stat().
+ *
+ * Returns header line on success, NULL otherwise.
+ */
+static const char *tomoyo_filetype(const mode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ case 0:
+ return "file";
+ case S_IFDIR:
+ return "directory";
+ case S_IFLNK:
+ return "symlink";
+ case S_IFIFO:
+ return "fifo";
+ case S_IFSOCK:
+ return "socket";
+ case S_IFBLK:
+ return "block";
+ case S_IFCHR:
+ return "char";
+ }
+ return "unknown"; /* This should not happen. */
+}
+
+/**
+ * tomoyo_print_header - Get header line of audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns string representation.
+ */
+static char *tomoyo_print_header(struct tomoyo_request_info *r)
+{
+ static const char *tomoyo_mode_4[4] = {
+ "disabled", "learning", "permissive", "enforcing"
+ };
+ struct timeval tv;
+ unsigned int dev;
+ mode_t mode;
+ struct tomoyo_obj_info *obj = r->obj;
+ const u32 tomoyo_flags = current->tomoyo_flags;
+ static const int tomoyo_buffer_len = 4096;
+ char *buffer = kmalloc(tomoyo_buffer_len, GFP_KERNEL);
+ int pos;
+ if (!buffer)
+ return NULL;
+ do_gettimeofday(&tv);
+ pos = snprintf(buffer, tomoyo_buffer_len - 1,
+ "#timestamp=%lu profile=%u mode=%s "
+ "(global-pid=%u)", tv.tv_sec, r->profile,
+ tomoyo_mode_4[r->mode], task_pid_nr(current));
+ if (tomoyo_profile(r->profile)->audit->audit_task_info) {
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u"
+ " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u"
+ " state[0]=%u state[1]=%u state[2]=%u"
+ " type%s=execute_handler }",
+ (pid_t) sys_getpid(), (pid_t) sys_getppid(),
+ current_uid(), current_gid(), current_euid(),
+ current_egid(), current_suid(), current_sgid(),
+ current_fsuid(), current_fsgid(),
+ (u8) (tomoyo_flags >> 24),
+ (u8) (tomoyo_flags >> 16),
+ (u8) (tomoyo_flags >> 8), tomoyo_flags &
+ TOMOYO_TASK_IS_EXECUTE_HANDLER ? "" : "!");
+ }
+ if (!obj || !tomoyo_profile(r->profile)->audit->audit_path_info)
+ goto no_obj_info;
+ if (!obj->validate_done) {
+ tomoyo_get_attributes(obj);
+ obj->validate_done = true;
+ }
+ if (obj->path1_valid) {
+ dev = obj->path1_stat.dev;
+ mode = obj->path1_stat.mode;
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " path1={ uid=%u gid=%u ino=%lu major=%u"
+ " minor=%u perm=0%o type=%s",
+ obj->path1_stat.uid, obj->path1_stat.gid,
+ (unsigned long) obj->path1_stat.ino,
+ MAJOR(dev), MINOR(dev), mode & S_IALLUGO,
+ tomoyo_filetype(mode & S_IFMT));
+ if (S_ISCHR(mode) || S_ISBLK(mode)) {
+ dev = obj->path1_stat.rdev;
+ pos += snprintf(buffer + pos,
+ tomoyo_buffer_len - 1 - pos,
+ " dev_major=%u dev_minor=%u",
+ MAJOR(dev), MINOR(dev));
+ }
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " }");
+ }
+ if (obj->path1_parent_valid) {
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " path1.parent={ uid=%u gid=%u ino=%lu"
+ " perm=0%o }", obj->path1_parent_stat.uid,
+ obj->path1_parent_stat.gid,
+ obj->path1_parent_stat.ino,
+ obj->path1_parent_stat.mode & S_IALLUGO);
+ }
+ if (obj->path2_valid) {
+ dev = obj->path2_stat.dev;
+ mode = obj->path2_stat.mode;
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " path2={ uid=%u gid=%u ino=%lu major=%u"
+ " minor=%u perm=0%o type=%s",
+ obj->path2_stat.uid, obj->path2_stat.gid,
+ (unsigned long) obj->path2_stat.ino,
+ MAJOR(dev), MINOR(dev), mode & S_IALLUGO,
+ tomoyo_filetype(mode & S_IFMT));
+ if (S_ISCHR(mode) || S_ISBLK(mode)) {
+ dev = obj->path2_stat.rdev;
+ pos += snprintf(buffer + pos,
+ tomoyo_buffer_len - 1 - pos,
+ " dev_major=%u dev_minor=%u",
+ MAJOR(dev), MINOR(dev));
+ }
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " }");
+ }
+ if (obj->path2_parent_valid) {
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " path2.parent={ uid=%u gid=%u ino=%lu"
+ " perm=0%o }", obj->path2_parent_stat.uid,
+ obj->path2_parent_stat.gid,
+ obj->path2_parent_stat.ino,
+ obj->path2_parent_stat.mode & S_IALLUGO);
+ }
+ no_obj_info:
+ if (pos < tomoyo_buffer_len - 1)
+ return buffer;
+ kfree(buffer);
+ return NULL;
+}
+
+/**
+ * tomoyo_init_audit_log - Allocate buffer for audit logs.
+ *
+ * @len: Required size.
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns pointer to allocated memory.
+ *
+ * The @len is updated to add the header lines' size on success.
+ */
+char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r)
+{
+ char *buf = NULL;
+ char *bprm_info = NULL;
+ char *realpath = NULL;
+ const char *symlink = NULL;
+ const char *header = NULL;
+ int pos;
+ const char *domainname;
+ if (!r->domain)
+ r->domain = tomoyo_current_domain();
+ domainname = r->domain->domainname->name;
+ header = tomoyo_print_header(r);
+ if (!header)
+ return NULL;
+ *len += strlen(domainname) + strlen(header) + 10;
+ if (r->ee) {
+ struct file *file = r->ee->bprm->file;
+ realpath = tomoyo_realpath_from_path(&file->f_path);
+ bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump);
+ if (!realpath || !bprm_info)
+ goto out;
+ *len += strlen(realpath) + 64 + strlen(bprm_info);
+ } else if (r->obj && r->obj->symlink_target) {
+ symlink = r->obj->symlink_target->name;
+ *len += 18 + strlen(symlink);
+ }
+ buf = kzalloc(*len, GFP_KERNEL);
+ if (!buf)
+ goto out;
+ pos = snprintf(buf, (*len) - 1, "%s", header);
+ if (realpath) {
+ struct linux_binprm *bprm = r->ee->bprm;
+ pos += snprintf(buf + pos, (*len) - 1 - pos,
+ " exec={ realpath=\"%s\" argc=%d envc=%d %s }",
+ realpath, bprm->argc, bprm->envc, bprm_info);
+ } else if (symlink)
+ pos += snprintf(buf + pos, (*len) - 1 - pos,
+ " symlink.target=\"%s\"", symlink);
+ snprintf(buf + pos, (*len) - 1 - pos, "\n%s\n", domainname);
+ out:
+ kfree(realpath);
+ kfree(bprm_info);
+ kfree(header);
+ return buf;
+}
+
+/**
+ * tomoyo_update_task_state - Update task's state.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ */
+static void tomoyo_update_task_state(struct tomoyo_request_info *r)
+{
+ /*
+ * Don't change the lowest byte because it is reserved for
+ * TOMOYO_TASK_IS_IN_EXECVE /
+ * TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR /
+ * TOMOYO_TASK_IS_EXECUTE_HANDLER / TOMOYO_TASK_IS_POLICY_MANAGER .
+ */
+ const struct tomoyo_condition *ptr = r->cond;
+ if (ptr) {
+ struct task_struct *task = current;
+ const u8 flags = ptr->post_state[3];
+ u32 tomoyo_flags = task->tomoyo_flags;
+ if (flags & 1) {
+ tomoyo_flags &= ~0xFF000000;
+ tomoyo_flags |= ptr->post_state[0] << 24;
+ }
+ if (flags & 2) {
+ tomoyo_flags &= ~0x00FF0000;
+ tomoyo_flags |= ptr->post_state[1] << 16;
+ }
+ if (flags & 4) {
+ tomoyo_flags &= ~0x0000FF00;
+ tomoyo_flags |= ptr->post_state[2] << 8;
+ }
+ task->tomoyo_flags = tomoyo_flags;
+ r->cond = NULL;
+ }
+}
+
+#ifndef CONFIG_SECURITY_TOMOYO_AUDIT
+
+/**
+ * tomoyo_write_audit_log - Write audit log.
+ *
+ * @is_granted: True if this is a granted log.
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns 0 on success, -ENOMEM otherwise.
+ */
+int tomoyo_write_audit_log(const bool is_granted,
+ struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ tomoyo_update_task_state(r);
+ return 0;
+}
+
+#else
+
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_grant_log_wait);
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_reject_log_wait);
+
+static DEFINE_SPINLOCK(tomoyo_audit_log_lock);
+
+/* Structure for audit log. */
+struct tomoyo_log_entry {
+ struct list_head list;
+ char *log;
+ int size;
+};
+
+/* The list for "struct tomoyo_log_entry". */
+static LIST_HEAD(tomoyo_grant_log);
+
+/* The list for "struct tomoyo_log_entry". */
+static LIST_HEAD(tomoyo_reject_log);
+
+static unsigned int tomoyo_grant_log_count;
+static unsigned int tomoyo_reject_log_count;
+
+/**
+ * tomoyo_write_audit_log - Write audit log.
+ *
+ * @is_granted: True if this is a granted log.
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns 0 on success, -ENOMEM otherwise.
+ */
+int tomoyo_write_audit_log(const bool is_granted,
+ struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ va_list args;
+ int error = -ENOMEM;
+ int pos;
+ int len;
+ char *buf;
+ struct tomoyo_log_entry *new_entry;
+ bool quota_exceeded = false;
+ if (!r->domain)
+ r->domain = tomoyo_current_domain();
+ if (is_granted) {
+ if (tomoyo_grant_log_count >=
+ tomoyo_profile(r->domain->profile)->
+ audit->audit_max_grant_log
+ || !tomoyo_get_audit(r->profile, r->type, true))
+ goto out;
+ } else {
+ if (tomoyo_reject_log_count >=
+ tomoyo_profile(r->domain->profile)->
+ audit->audit_max_reject_log
+ || !tomoyo_get_audit(r->profile, r->type, false))
+ goto out;
+ }
+ va_start(args, fmt);
+ len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+ va_end(args);
+ buf = tomoyo_init_audit_log(&len, r);
+ if (!buf)
+ goto out;
+ pos = strlen(buf);
+ va_start(args, fmt);
+ vsnprintf(buf + pos, len - pos - 1, fmt, args);
+ va_end(args);
+ new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
+ if (!new_entry) {
+ kfree(buf);
+ goto out;
+ }
+ new_entry->log = buf;
+ /*
+ * The new_entry->size is used for memory quota checks.
+ * Don't go beyond strlen(new_entry->log).
+ */
+ new_entry->size = tomoyo_round2(len) +
+ tomoyo_round2(sizeof(*new_entry));
+ spin_lock(&tomoyo_audit_log_lock);
+ if (tomoyo_quota_for_audit_log && tomoyo_audit_log_memory_size
+ + new_entry->size >= tomoyo_quota_for_audit_log) {
+ quota_exceeded = true;
+ } else {
+ tomoyo_audit_log_memory_size += new_entry->size;
+ if (is_granted) {
+ list_add_tail(&new_entry->list, &tomoyo_grant_log);
+ tomoyo_grant_log_count++;
+ } else {
+ list_add_tail(&new_entry->list, &tomoyo_reject_log);
+ tomoyo_reject_log_count++;
+ }
+ }
+ spin_unlock(&tomoyo_audit_log_lock);
+ if (quota_exceeded) {
+ kfree(buf);
+ kfree(new_entry);
+ goto out;
+ }
+ if (is_granted)
+ wake_up(&tomoyo_grant_log_wait);
+ else
+ wake_up(&tomoyo_reject_log_wait);
+ error = 0;
+ out:
+ tomoyo_update_task_state(r);
+ return error;
+}
+
+/**
+ * tomoyo_read_grant_log - Read a grant log.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+void tomoyo_read_grant_log(struct tomoyo_io_buffer *head)
+{
+ struct tomoyo_log_entry *ptr = NULL;
+ if (head->read_avail)
+ return;
+ if (head->read_buf) {
+ kfree(head->read_buf);
+ head->read_buf = NULL;
+ head->readbuf_size = 0;
+ }
+ spin_lock(&tomoyo_audit_log_lock);
+ if (!list_empty(&tomoyo_grant_log)) {
+ ptr = list_entry(tomoyo_grant_log.next,
+ struct tomoyo_log_entry, list);
+ list_del(&ptr->list);
+ tomoyo_grant_log_count--;
+ tomoyo_audit_log_memory_size -= ptr->size;
+ }
+ spin_unlock(&tomoyo_audit_log_lock);
+ if (ptr) {
+ head->read_buf = ptr->log;
+ head->read_avail = strlen(ptr->log) + 1;
+ head->readbuf_size = head->read_avail;
+ kfree(ptr);
+ }
+}
+
+/**
+ * tomoyo_poll_grant_log - Wait for a grant log.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read a grant log.
+ */
+int tomoyo_poll_grant_log(struct file *file, poll_table *wait)
+{
+ if (tomoyo_grant_log_count)
+ return POLLIN | POLLRDNORM;
+ poll_wait(file, &tomoyo_grant_log_wait, wait);
+ if (tomoyo_grant_log_count)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+/**
+ * tomoyo_read_reject_log - Read a reject log.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+void tomoyo_read_reject_log(struct tomoyo_io_buffer *head)
+{
+ struct tomoyo_log_entry *ptr = NULL;
+ if (head->read_avail)
+ return;
+ if (head->read_buf) {
+ kfree(head->read_buf);
+ head->read_buf = NULL;
+ head->readbuf_size = 0;
+ }
+ spin_lock(&tomoyo_audit_log_lock);
+ if (!list_empty(&tomoyo_reject_log)) {
+ ptr = list_entry(tomoyo_reject_log.next,
+ struct tomoyo_log_entry, list);
+ list_del(&ptr->list);
+ tomoyo_reject_log_count--;
+ tomoyo_audit_log_memory_size -= ptr->size;
+ }
+ spin_unlock(&tomoyo_audit_log_lock);
+ if (ptr) {
+ head->read_buf = ptr->log;
+ head->read_avail = strlen(ptr->log) + 1;
+ head->readbuf_size = head->read_avail;
+ kfree(ptr);
+ }
+}
+
+/**
+ * tomoyo_poll_reject_log - Wait for a reject log.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read a reject log.
+ */
+int tomoyo_poll_reject_log(struct file *file, poll_table *wait)
+{
+ if (tomoyo_reject_log_count)
+ return POLLIN | POLLRDNORM;
+ poll_wait(file, &tomoyo_reject_log_wait, wait);
+ if (tomoyo_reject_log_count)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+#endif

--
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/