[PATCH] choose_new_parent: remove unused arg, sanitize exit_state check
[pandora-kernel.git] / kernel / auditsc.c
index d8a6850..7f160df 100644 (file)
@@ -2,6 +2,8 @@
  * Handles all system-call specific auditing features.
  *
  * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
+ * Copyright 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2005 IBM Corporation
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * this file -- see entry.S) is based on a GPL'd patch written by
  * okir@suse.de and Copyright 2003 SuSE Linux AG.
  *
+ * The support of additional filter rules compares (>, <, >=, <=) was
+ * added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005.
+ *
+ * Modified by Amy Griffis <amy.griffis@hp.com> to collect additional
+ * filesystem information.
+ *
+ * Subject and object context labeling support added by <danjones@us.ibm.com>
+ * and <dustin.kirkland@us.ibm.com> for LSPP certification compliance.
  */
 
 #include <linux/init.h>
+#include <asm/types.h>
 #include <asm/atomic.h>
 #include <asm/types.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
 #include <linux/personality.h>
 #include <linux/time.h>
-#include <linux/kthread.h>
 #include <linux/netlink.h>
 #include <linux/compiler.h>
 #include <asm/unistd.h>
+#include <linux/security.h>
+#include <linux/list.h>
+#include <linux/tty.h>
+
+#include "audit.h"
 
-/* 0 = no checking
-   1 = put_count checking
-   2 = verbose put_count checking
-*/
-#define AUDIT_DEBUG 0
+extern struct list_head audit_filter_list[];
 
 /* No syscall auditing will take place unless audit_enabled != 0. */
 extern int audit_enabled;
@@ -62,29 +75,6 @@ extern int audit_enabled;
  * path_lookup. */
 #define AUDIT_NAMES_RESERVED 7
 
-/* At task start time, the audit_state is set in the audit_context using
-   a per-task filter.  At syscall entry, the audit_state is augmented by
-   the syscall filter. */
-enum audit_state {
-       AUDIT_DISABLED,         /* Do not create per-task audit_context.
-                                * No syscall-specific audit records can
-                                * be generated. */
-       AUDIT_SETUP_CONTEXT,    /* Create the per-task audit_context,
-                                * but don't necessarily fill it in at
-                                * syscall entry time (i.e., filter
-                                * instead). */
-       AUDIT_BUILD_CONTEXT,    /* Create the per-task audit_context,
-                                * and always fill it in at syscall
-                                * entry time.  This makes a full
-                                * syscall record available if some
-                                * other part of the kernel decides it
-                                * should be recorded. */
-       AUDIT_RECORD_CONTEXT    /* Create the per-task audit_context,
-                                * always fill it in at syscall entry
-                                * time, and always write out the audit
-                                * record at syscall exit time.  */
-};
-
 /* When fs/namei.c:getname() is called, we store the pointer in name and
  * we don't let putname() free it (instead we free all of the saved
  * pointers at syscall exit time).
@@ -93,12 +83,13 @@ enum audit_state {
 struct audit_names {
        const char      *name;
        unsigned long   ino;
+       unsigned long   pino;
        dev_t           dev;
        umode_t         mode;
        uid_t           uid;
        gid_t           gid;
        dev_t           rdev;
-       unsigned        flags;
+       char            *ctx;
 };
 
 struct audit_aux_data {
@@ -115,6 +106,7 @@ struct audit_aux_data_ipcctl {
        uid_t                   uid;
        gid_t                   gid;
        mode_t                  mode;
+       char                    *ctx;
 };
 
 struct audit_aux_data_socketcall {
@@ -167,290 +159,72 @@ struct audit_context {
 #endif
 };
 
-                               /* Public API */
-/* There are three lists of rules -- one to search at task creation
- * time, one to search at syscall entry time, and another to search at
- * syscall exit time. */
-static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
-       LIST_HEAD_INIT(audit_filter_list[0]),
-       LIST_HEAD_INIT(audit_filter_list[1]),
-       LIST_HEAD_INIT(audit_filter_list[2]),
-       LIST_HEAD_INIT(audit_filter_list[3]),
-       LIST_HEAD_INIT(audit_filter_list[4]),
-#if AUDIT_NR_FILTERS != 5
-#error Fix audit_filter_list initialiser
-#endif
-};
-
-struct audit_entry {
-       struct list_head  list;
-       struct rcu_head   rcu;
-       struct audit_rule rule;
-};
-
-extern int audit_pid;
-
-/* Copy rule from user-space to kernel-space.  Called from 
- * audit_add_rule during AUDIT_ADD. */
-static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
-{
-       int i;
-
-       if (s->action != AUDIT_NEVER
-           && s->action != AUDIT_POSSIBLE
-           && s->action != AUDIT_ALWAYS)
-               return -1;
-       if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS)
-               return -1;
-       if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS)
-               return -1;
-
-       d->flags        = s->flags;
-       d->action       = s->action;
-       d->field_count  = s->field_count;
-       for (i = 0; i < d->field_count; i++) {
-               d->fields[i] = s->fields[i];
-               d->values[i] = s->values[i];
-       }
-       for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i];
-       return 0;
-}
-
-/* Check to see if two rules are identical.  It is called from
- * audit_add_rule during AUDIT_ADD and 
- * audit_del_rule during AUDIT_DEL. */
-static inline int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
-{
-       int i;
-
-       if (a->flags != b->flags)
-               return 1;
-
-       if (a->action != b->action)
-               return 1;
-
-       if (a->field_count != b->field_count)
-               return 1;
-
-       for (i = 0; i < a->field_count; i++) {
-               if (a->fields[i] != b->fields[i]
-                   || a->values[i] != b->values[i])
-                       return 1;
-       }
-
-       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
-               if (a->mask[i] != b->mask[i])
-                       return 1;
-
-       return 0;
-}
-
-/* Note that audit_add_rule and audit_del_rule are called via
- * audit_receive() in audit.c, and are protected by
- * audit_netlink_sem. */
-static inline int audit_add_rule(struct audit_rule *rule,
-                                 struct list_head *list)
-{
-       struct audit_entry  *entry;
-
-       /* Do not use the _rcu iterator here, since this is the only
-        * addition routine. */
-       list_for_each_entry(entry, list, list) {
-               if (!audit_compare_rule(rule, &entry->rule)) {
-                       return -EEXIST;
-               }
-       }
-
-       if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
-               return -ENOMEM;
-       if (audit_copy_rule(&entry->rule, rule)) {
-               kfree(entry);
-               return -EINVAL;
-       }
-
-       if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
-               entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
-               list_add_rcu(&entry->list, list);
-       } else {
-               list_add_tail_rcu(&entry->list, list);
-       }
-
-       return 0;
-}
-
-static inline void audit_free_rule(struct rcu_head *head)
-{
-       struct audit_entry *e = container_of(head, struct audit_entry, rcu);
-       kfree(e);
-}
-
-/* Note that audit_add_rule and audit_del_rule are called via
- * audit_receive() in audit.c, and are protected by
- * audit_netlink_sem. */
-static inline int audit_del_rule(struct audit_rule *rule,
-                                struct list_head *list)
-{
-       struct audit_entry  *e;
-
-       /* Do not use the _rcu iterator here, since this is the only
-        * deletion routine. */
-       list_for_each_entry(e, list, list) {
-               if (!audit_compare_rule(rule, &e->rule)) {
-                       list_del_rcu(&e->list);
-                       call_rcu(&e->rcu, audit_free_rule);
-                       return 0;
-               }
-       }
-       return -ENOENT;         /* No matching rule */
-}
-
-static int audit_list_rules(void *_dest)
-{
-       int pid, seq;
-       int *dest = _dest;
-       struct audit_entry *entry;
-       int i;
-
-       pid = dest[0];
-       seq = dest[1];
-       kfree(dest);
-
-       down(&audit_netlink_sem);
-
-       /* The *_rcu iterators not needed here because we are
-          always called with audit_netlink_sem held. */
-       for (i=0; i<AUDIT_NR_FILTERS; i++) {
-               list_for_each_entry(entry, &audit_filter_list[i], list)
-                       audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
-                                        &entry->rule, sizeof(entry->rule));
-       }
-       audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
-       
-       up(&audit_netlink_sem);
-       return 0;
-}
-
-int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
-                                                       uid_t loginuid)
-{
-       struct task_struct *tsk;
-       int *dest;
-       int                err = 0;
-       unsigned listnr;
-
-       switch (type) {
-       case AUDIT_LIST:
-               /* We can't just spew out the rules here because we might fill
-                * the available socket buffer space and deadlock waiting for
-                * auditctl to read from it... which isn't ever going to
-                * happen if we're actually running in the context of auditctl
-                * trying to _send_ the stuff */
-                
-               dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
-               if (!dest)
-                       return -ENOMEM;
-               dest[0] = pid;
-               dest[1] = seq;
-
-               tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
-               if (IS_ERR(tsk)) {
-                       kfree(dest);
-                       err = PTR_ERR(tsk);
-               }
-               break;
-       case AUDIT_ADD:
-               listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
-               if (listnr >= AUDIT_NR_FILTERS)
-                       return -EINVAL;
-
-               err = audit_add_rule(data, &audit_filter_list[listnr]);
-               if (!err)
-                       audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                                 "auid=%u added an audit rule\n", loginuid);
-               break;
-       case AUDIT_DEL:
-               listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
-               if (listnr >= AUDIT_NR_FILTERS)
-                       return -EINVAL;
-
-               err = audit_del_rule(data, &audit_filter_list[listnr]);
-               if (!err)
-                       audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                                 "auid=%u removed an audit rule\n", loginuid);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return err;
-}
 
 /* Compare a task_struct with an audit_rule.  Return 1 on match, 0
  * otherwise. */
 static int audit_filter_rules(struct task_struct *tsk,
-                             struct audit_rule *rule,
+                             struct audit_krule *rule,
                              struct audit_context *ctx,
                              enum audit_state *state)
 {
        int i, j;
 
        for (i = 0; i < rule->field_count; i++) {
-               u32 field  = rule->fields[i] & ~AUDIT_NEGATE;
-               u32 value  = rule->values[i];
+               struct audit_field *f = &rule->fields[i];
                int result = 0;
 
-               switch (field) {
+               switch (f->type) {
                case AUDIT_PID:
-                       result = (tsk->pid == value);
+                       result = audit_comparator(tsk->pid, f->op, f->val);
                        break;
                case AUDIT_UID:
-                       result = (tsk->uid == value);
+                       result = audit_comparator(tsk->uid, f->op, f->val);
                        break;
                case AUDIT_EUID:
-                       result = (tsk->euid == value);
+                       result = audit_comparator(tsk->euid, f->op, f->val);
                        break;
                case AUDIT_SUID:
-                       result = (tsk->suid == value);
+                       result = audit_comparator(tsk->suid, f->op, f->val);
                        break;
                case AUDIT_FSUID:
-                       result = (tsk->fsuid == value);
+                       result = audit_comparator(tsk->fsuid, f->op, f->val);
                        break;
                case AUDIT_GID:
-                       result = (tsk->gid == value);
+                       result = audit_comparator(tsk->gid, f->op, f->val);
                        break;
                case AUDIT_EGID:
-                       result = (tsk->egid == value);
+                       result = audit_comparator(tsk->egid, f->op, f->val);
                        break;
                case AUDIT_SGID:
-                       result = (tsk->sgid == value);
+                       result = audit_comparator(tsk->sgid, f->op, f->val);
                        break;
                case AUDIT_FSGID:
-                       result = (tsk->fsgid == value);
+                       result = audit_comparator(tsk->fsgid, f->op, f->val);
                        break;
                case AUDIT_PERS:
-                       result = (tsk->personality == value);
+                       result = audit_comparator(tsk->personality, f->op, f->val);
                        break;
                case AUDIT_ARCH:
-                       if (ctx) 
-                               result = (ctx->arch == value);
+                       if (ctx)
+                               result = audit_comparator(ctx->arch, f->op, f->val);
                        break;
 
                case AUDIT_EXIT:
                        if (ctx && ctx->return_valid)
-                               result = (ctx->return_code == value);
+                               result = audit_comparator(ctx->return_code, f->op, f->val);
                        break;
                case AUDIT_SUCCESS:
                        if (ctx && ctx->return_valid) {
-                               if (value)
-                                       result = (ctx->return_valid == AUDITSC_SUCCESS);
+                               if (f->val)
+                                       result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS);
                                else
-                                       result = (ctx->return_valid == AUDITSC_FAILURE);
+                                       result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE);
                        }
                        break;
                case AUDIT_DEVMAJOR:
                        if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
-                                       if (MAJOR(ctx->names[j].dev)==value) {
+                                       if (audit_comparator(MAJOR(ctx->names[j].dev),  f->op, f->val)) {
                                                ++result;
                                                break;
                                        }
@@ -460,7 +234,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_DEVMINOR:
                        if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
-                                       if (MINOR(ctx->names[j].dev)==value) {
+                                       if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) {
                                                ++result;
                                                break;
                                        }
@@ -470,7 +244,8 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_INODE:
                        if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
-                                       if (ctx->names[j].ino == value) {
+                                       if (audit_comparator(ctx->names[j].ino, f->op, f->val) ||
+                                           audit_comparator(ctx->names[j].pino, f->op, f->val)) {
                                                ++result;
                                                break;
                                        }
@@ -480,19 +255,17 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_LOGINUID:
                        result = 0;
                        if (ctx)
-                               result = (ctx->loginuid == value);
+                               result = audit_comparator(ctx->loginuid, f->op, f->val);
                        break;
                case AUDIT_ARG0:
                case AUDIT_ARG1:
                case AUDIT_ARG2:
                case AUDIT_ARG3:
                        if (ctx)
-                               result = (ctx->argv[field-AUDIT_ARG0]==value);
+                               result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
                        break;
                }
 
-               if (rule->fields[i] & AUDIT_NEGATE)
-                       result = !result;
                if (!result)
                        return 0;
        }
@@ -527,7 +300,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk)
 /* At syscall entry and exit time, this filter is called if the
  * audit_state is not low enough that auditing cannot take place, but is
  * also not high enough that we already know we have to write an audit
- * record (i.e., the state is AUDIT_SETUP_CONTEXT or  AUDIT_BUILD_CONTEXT).
+ * record (i.e., the state is AUDIT_SETUP_CONTEXT or AUDIT_BUILD_CONTEXT).
  */
 static enum audit_state audit_filter_syscall(struct task_struct *tsk,
                                             struct audit_context *ctx,
@@ -541,77 +314,19 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
 
        rcu_read_lock();
        if (!list_empty(list)) {
-                   int word = AUDIT_WORD(ctx->major);
-                   int bit  = AUDIT_BIT(ctx->major);
-
-                   list_for_each_entry_rcu(e, list, list) {
-                           if ((e->rule.mask[word] & bit) == bit
-                               && audit_filter_rules(tsk, &e->rule, ctx, &state)) {
-                                   rcu_read_unlock();
-                                   return state;
-                           }
-                   }
-       }
-       rcu_read_unlock();
-       return AUDIT_BUILD_CONTEXT;
-}
-
-static int audit_filter_user_rules(struct netlink_skb_parms *cb,
-                             struct audit_rule *rule,
-                             enum audit_state *state)
-{
-       int i;
-
-       for (i = 0; i < rule->field_count; i++) {
-               u32 field  = rule->fields[i] & ~AUDIT_NEGATE;
-               u32 value  = rule->values[i];
-               int result = 0;
-
-               switch (field) {
-               case AUDIT_PID:
-                       result = (cb->creds.pid == value);
-                       break;
-               case AUDIT_UID:
-                       result = (cb->creds.uid == value);
-                       break;
-               case AUDIT_GID:
-                       result = (cb->creds.gid == value);
-                       break;
-               case AUDIT_LOGINUID:
-                       result = (cb->loginuid == value);
-                       break;
-               }
-
-               if (rule->fields[i] & AUDIT_NEGATE)
-                       result = !result;
-               if (!result)
-                       return 0;
-       }
-       switch (rule->action) {
-       case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
-       case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT;  break;
-       case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
-       }
-       return 1;
-}
-
-int audit_filter_user(struct netlink_skb_parms *cb, int type)
-{
-       struct audit_entry *e;
-       enum audit_state   state;
-       int ret = 1;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
-               if (audit_filter_user_rules(cb, &e->rule, &state)) {
-                       if (state == AUDIT_DISABLED)
-                               ret = 0;
-                       break;
+               int word = AUDIT_WORD(ctx->major);
+               int bit  = AUDIT_BIT(ctx->major);
+
+               list_for_each_entry_rcu(e, list, list) {
+                       if ((e->rule.mask[word] & bit) == bit
+                                       && audit_filter_rules(tsk, &e->rule, ctx, &state)) {
+                               rcu_read_unlock();
+                               return state;
+                       }
                }
        }
        rcu_read_unlock();
-
-       return ret; /* Audit by default */
+       return AUDIT_BUILD_CONTEXT;
 }
 
 /* This should be called with task_lock() held. */
@@ -654,17 +369,18 @@ static inline void audit_free_names(struct audit_context *context)
 #if AUDIT_DEBUG == 2
        if (context->auditable
            ||context->put_count + context->ino_count != context->name_count) {
-               printk(KERN_ERR "audit.c:%d(:%d): major=%d in_syscall=%d"
+               printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d"
                       " name_count=%d put_count=%d"
                       " ino_count=%d [NOT freeing]\n",
-                      __LINE__,
+                      __FILE__, __LINE__,
                       context->serial, context->major, context->in_syscall,
                       context->name_count, context->put_count,
                       context->ino_count);
-               for (i = 0; i < context->name_count; i++)
+               for (i = 0; i < context->name_count; i++) {
                        printk(KERN_ERR "names[%d] = %p = %s\n", i,
                               context->names[i].name,
-                              context->names[i].name);
+                              context->names[i].name ?: "(null)");
+               }
                dump_stack();
                return;
        }
@@ -674,9 +390,13 @@ static inline void audit_free_names(struct audit_context *context)
        context->ino_count  = 0;
 #endif
 
-       for (i = 0; i < context->name_count; i++)
+       for (i = 0; i < context->name_count; i++) {
+               char *p = context->names[i].ctx;
+               context->names[i].ctx = NULL;
+               kfree(p);
                if (context->names[i].name)
                        __putname(context->names[i].name);
+       }
        context->name_count = 0;
        if (context->pwd)
                dput(context->pwd);
@@ -696,6 +416,12 @@ static inline void audit_free_aux(struct audit_context *context)
                        dput(axi->dentry);
                        mntput(axi->mnt);
                }
+               if ( aux->type == AUDIT_IPC ) {
+                       struct audit_aux_data_ipcctl *axi = (void *)aux;
+                       if (axi->ctx)
+                               kfree(axi->ctx);
+               }
+
                context->aux = aux->next;
                kfree(aux);
        }
@@ -721,10 +447,15 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state)
        return context;
 }
 
-/* Filter on the task information and allocate a per-task audit context
+/**
+ * audit_alloc - allocate an audit context block for a task
+ * @tsk: task
+ *
+ * Filter on the task information and allocate a per-task audit context
  * if necessary.  Doing so turns on system call auditing for the
  * specified task.  This is called from copy_process, so no lock is
- * needed. */
+ * needed.
+ */
 int audit_alloc(struct task_struct *tsk)
 {
        struct audit_context *context;
@@ -775,7 +506,37 @@ static inline void audit_free_context(struct audit_context *context)
                printk(KERN_ERR "audit: freed %d contexts\n", count);
 }
 
-static void audit_log_task_info(struct audit_buffer *ab)
+static void audit_log_task_context(struct audit_buffer *ab, gfp_t gfp_mask)
+{
+       char *ctx = NULL;
+       ssize_t len = 0;
+
+       len = security_getprocattr(current, "current", NULL, 0);
+       if (len < 0) {
+               if (len != -EINVAL)
+                       goto error_path;
+               return;
+       }
+
+       ctx = kmalloc(len, gfp_mask);
+       if (!ctx)
+               goto error_path;
+
+       len = security_getprocattr(current, "current", ctx, len);
+       if (len < 0 )
+               goto error_path;
+
+       audit_log_format(ab, " subj=%s", ctx);
+       return;
+
+error_path:
+       if (ctx)
+               kfree(ctx);
+       audit_panic("error in audit_log_task_context");
+       return;
+}
+
+static void audit_log_task_info(struct audit_buffer *ab, gfp_t gfp_mask)
 {
        char name[sizeof(current->comm)];
        struct mm_struct *mm = current->mm;
@@ -788,6 +549,10 @@ static void audit_log_task_info(struct audit_buffer *ab)
        if (!mm)
                return;
 
+       /*
+        * this is brittle; all callers that pass GFP_ATOMIC will have
+        * NULL current->mm and we won't get here.
+        */
        down_read(&mm->mmap_sem);
        vma = mm->mmap;
        while (vma) {
@@ -801,6 +566,7 @@ static void audit_log_task_info(struct audit_buffer *ab)
                vma = vma->vm_next;
        }
        up_read(&mm->mmap_sem);
+       audit_log_task_context(ab, gfp_mask);
 }
 
 static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
@@ -808,6 +574,7 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
        int i;
        struct audit_buffer *ab;
        struct audit_aux_data *aux;
+       const char *tty;
 
        ab = audit_log_start(context, gfp_mask, AUDIT_SYSCALL);
        if (!ab)
@@ -820,11 +587,15 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
                audit_log_format(ab, " success=%s exit=%ld", 
                                 (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
                                 context->return_code);
+       if (current->signal->tty && current->signal->tty->name)
+               tty = current->signal->tty->name;
+       else
+               tty = "(none)";
        audit_log_format(ab,
                  " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
                  " pid=%d auid=%u uid=%u gid=%u"
                  " euid=%u suid=%u fsuid=%u"
-                 " egid=%u sgid=%u fsgid=%u",
+                 " egid=%u sgid=%u fsgid=%u tty=%s",
                  context->argv[0],
                  context->argv[1],
                  context->argv[2],
@@ -835,13 +606,13 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
                  context->uid,
                  context->gid,
                  context->euid, context->suid, context->fsuid,
-                 context->egid, context->sgid, context->fsgid);
-       audit_log_task_info(ab);
+                 context->egid, context->sgid, context->fsgid, tty);
+       audit_log_task_info(ab, gfp_mask);
        audit_log_end(ab);
 
        for (aux = context->aux; aux; aux = aux->next) {
 
-               ab = audit_log_start(context, GFP_KERNEL, aux->type);
+               ab = audit_log_start(context, gfp_mask, aux->type);
                if (!ab)
                        continue; /* audit_panic has been called */
 
@@ -849,8 +620,8 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
                case AUDIT_IPC: {
                        struct audit_aux_data_ipcctl *axi = (void *)aux;
                        audit_log_format(ab, 
-                                        " qbytes=%lx iuid=%u igid=%u mode=%x",
-                                        axi->qbytes, axi->uid, axi->gid, axi->mode);
+                                        " qbytes=%lx iuid=%u igid=%u mode=%x obj=%s",
+                                        axi->qbytes, axi->uid, axi->gid, axi->mode, axi->ctx);
                        break; }
 
                case AUDIT_SOCKETCALL: {
@@ -878,49 +649,69 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
        }
 
        if (context->pwd && context->pwdmnt) {
-               ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
+               ab = audit_log_start(context, gfp_mask, AUDIT_CWD);
                if (ab) {
                        audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt);
                        audit_log_end(ab);
                }
        }
        for (i = 0; i < context->name_count; i++) {
-               ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
+               unsigned long ino  = context->names[i].ino;
+               unsigned long pino = context->names[i].pino;
+
+               ab = audit_log_start(context, gfp_mask, AUDIT_PATH);
                if (!ab)
                        continue; /* audit_panic has been called */
 
                audit_log_format(ab, "item=%d", i);
-               if (context->names[i].name) {
-                       audit_log_format(ab, " name=");
+
+               audit_log_format(ab, " name=");
+               if (context->names[i].name)
                        audit_log_untrustedstring(ab, context->names[i].name);
-               }
-               audit_log_format(ab, " flags=%x\n", context->names[i].flags);
-                        
-               if (context->names[i].ino != (unsigned long)-1)
-                       audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o"
-                                            " ouid=%u ogid=%u rdev=%02x:%02x",
-                                        context->names[i].ino,
-                                        MAJOR(context->names[i].dev),
-                                        MINOR(context->names[i].dev),
-                                        context->names[i].mode,
-                                        context->names[i].uid,
-                                        context->names[i].gid,
-                                        MAJOR(context->names[i].rdev),
+               else
+                       audit_log_format(ab, "(null)");
+
+               if (pino != (unsigned long)-1)
+                       audit_log_format(ab, " parent=%lu",  pino);
+               if (ino != (unsigned long)-1)
+                       audit_log_format(ab, " inode=%lu",  ino);
+               if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1))
+                       audit_log_format(ab, " dev=%02x:%02x mode=%#o" 
+                                        " ouid=%u ogid=%u rdev=%02x:%02x", 
+                                        MAJOR(context->names[i].dev), 
+                                        MINOR(context->names[i].dev), 
+                                        context->names[i].mode, 
+                                        context->names[i].uid, 
+                                        context->names[i].gid, 
+                                        MAJOR(context->names[i].rdev), 
                                         MINOR(context->names[i].rdev));
+               if (context->names[i].ctx) {
+                       audit_log_format(ab, " obj=%s",
+                                       context->names[i].ctx);
+               }
+
                audit_log_end(ab);
        }
 }
 
-/* Free a per-task audit context.  Called from copy_process and
- * __put_task_struct. */
+/**
+ * audit_free - free a per-task audit context
+ * @tsk: task whose audit context block to free
+ *
+ * Called from copy_process and __put_task_struct.
+ */
 void audit_free(struct task_struct *tsk)
 {
        struct audit_context *context;
 
-       task_lock(tsk);
+       /*
+        * No need to lock the task - when we execute audit_free()
+        * then the task has no external references anymore, and
+        * we are tearing it down. (The locking also confuses
+        * DEBUG_LOCKDEP - this freeing may occur in softirq
+        * contexts as well, via RCU.)
+        */
        context = audit_get_context(tsk, 0, 0);
-       task_unlock(tsk);
-
        if (likely(!context))
                return;
 
@@ -934,13 +725,24 @@ void audit_free(struct task_struct *tsk)
        audit_free_context(context);
 }
 
-/* Fill in audit context at syscall entry.  This only happens if the
+/**
+ * audit_syscall_entry - fill in an audit record at syscall entry
+ * @tsk: task being audited
+ * @arch: architecture type
+ * @major: major syscall type (function)
+ * @a1: additional syscall register 1
+ * @a2: additional syscall register 2
+ * @a3: additional syscall register 3
+ * @a4: additional syscall register 4
+ *
+ * Fill in audit context at syscall entry.  This only happens if the
  * audit context was created when the task was created and the state or
  * filters demand the audit context be built.  If the state from the
  * per-task filter or from the per-syscall filter is AUDIT_RECORD_CONTEXT,
  * then the record will be written at syscall exit time (otherwise, it
  * will only be written if another part of the kernel requests that it
- * be written). */
+ * be written).
+ */
 void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
                         unsigned long a1, unsigned long a2,
                         unsigned long a3, unsigned long a4)
@@ -950,7 +752,8 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
 
        BUG_ON(!context);
 
-       /* This happens only on certain architectures that make system
+       /*
+        * This happens only on certain architectures that make system
         * calls in kernel_thread via the entry.S interface, instead of
         * with direct calls.  (If you are porting to a new
         * architecture, hitting this condition can indicate that you
@@ -958,7 +761,7 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
         *
         * i386     no
         * x86_64   no
-        * ppc64    yes (see arch/ppc64/kernel/misc.S)
+        * ppc64    yes (see arch/powerpc/platforms/iseries/misc.S)
         *
         * This also happens with vm86 emulation in a non-nested manner
         * (entries without exits), so this case must be caught.
@@ -966,11 +769,6 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
        if (context->in_syscall) {
                struct audit_context *newctx;
 
-#if defined(__NR_vm86) && defined(__NR_vm86old)
-               /* vm86 mode should only be entered once */
-               if (major == __NR_vm86 || major == __NR_vm86old)
-                       return;
-#endif
 #if AUDIT_DEBUG
                printk(KERN_ERR
                       "audit(:%d) pid=%d in syscall=%d;"
@@ -1014,11 +812,18 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
        context->auditable  = !!(state == AUDIT_RECORD_CONTEXT);
 }
 
-/* Tear down after system call.  If the audit context has been marked as
+/**
+ * audit_syscall_exit - deallocate audit context after a system call
+ * @tsk: task being audited
+ * @valid: success/failure flag
+ * @return_code: syscall return value
+ *
+ * Tear down after system call.  If the audit context has been marked as
  * auditable (either because of the AUDIT_RECORD_CONTEXT state from
  * filtering, or because some other part of the kernel write an audit
  * message), then write out the syscall information.  In call cases,
- * free the names stored from getname(). */
+ * free the names stored from getname().
+ */
 void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
 {
        struct audit_context *context;
@@ -1053,7 +858,13 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
        put_task_struct(tsk);
 }
 
-/* Add a name to the list.  Called from fs/namei.c:getname(). */
+/**
+ * audit_getname - add a name to the list
+ * @name: name to add
+ *
+ * Add a name to the list of audit names for this context.
+ * Called from fs/namei.c:getname().
+ */
 void audit_getname(const char *name)
 {
        struct audit_context *context = current->audit_context;
@@ -1082,10 +893,13 @@ void audit_getname(const char *name)
                
 }
 
-/* Intercept a putname request.  Called from
- * include/linux/fs.h:putname().  If we have stored the name from
- * getname in the audit context, then we delay the putname until syscall
- * exit. */
+/* audit_putname - intercept a putname request
+ * @name: name to intercept and delay for putname
+ *
+ * If we have stored the name from getname in the audit context,
+ * then we delay the putname until syscall exit.
+ * Called from include/linux/fs.h:putname().
+ */
 void audit_putname(const char *name)
 {
        struct audit_context *context = current->audit_context;
@@ -1100,7 +914,7 @@ void audit_putname(const char *name)
                        for (i = 0; i < context->name_count; i++)
                                printk(KERN_ERR "name[%d] = %p = %s\n", i,
                                       context->names[i].name,
-                                      context->names[i].name);
+                                      context->names[i].name ?: "(null)");
                }
 #endif
                __putname(name);
@@ -1122,9 +936,52 @@ void audit_putname(const char *name)
 #endif
 }
 
-/* Store the inode and device from a lookup.  Called from
- * fs/namei.c:path_lookup(). */
-void audit_inode(const char *name, const struct inode *inode, unsigned flags)
+void audit_inode_context(int idx, const struct inode *inode)
+{
+       struct audit_context *context = current->audit_context;
+       const char *suffix = security_inode_xattr_getsuffix();
+       char *ctx = NULL;
+       int len = 0;
+
+       if (!suffix)
+               goto ret;
+
+       len = security_inode_getsecurity(inode, suffix, NULL, 0, 0);
+       if (len == -EOPNOTSUPP)
+               goto ret;
+       if (len < 0) 
+               goto error_path;
+
+       ctx = kmalloc(len, GFP_KERNEL);
+       if (!ctx) 
+               goto error_path;
+
+       len = security_inode_getsecurity(inode, suffix, ctx, len, 0);
+       if (len < 0)
+               goto error_path;
+
+       kfree(context->names[idx].ctx);
+       context->names[idx].ctx = ctx;
+       goto ret;
+
+error_path:
+       if (ctx)
+               kfree(ctx);
+       audit_panic("error in audit_inode_context");
+ret:
+       return;
+}
+
+
+/**
+ * audit_inode - store the inode and device from a lookup
+ * @name: name being audited
+ * @inode: inode being audited
+ * @flags: lookup flags (as used in path_lookup())
+ *
+ * Called from fs/namei.c:path_lookup().
+ */
+void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
 {
        int idx;
        struct audit_context *context = current->audit_context;
@@ -1150,15 +1007,105 @@ void audit_inode(const char *name, const struct inode *inode, unsigned flags)
                ++context->ino_count;
 #endif
        }
-       context->names[idx].flags = flags;
-       context->names[idx].ino   = inode->i_ino;
        context->names[idx].dev   = inode->i_sb->s_dev;
        context->names[idx].mode  = inode->i_mode;
        context->names[idx].uid   = inode->i_uid;
        context->names[idx].gid   = inode->i_gid;
        context->names[idx].rdev  = inode->i_rdev;
+       audit_inode_context(idx, inode);
+       if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && 
+           (strcmp(name, ".") != 0)) {
+               context->names[idx].ino   = (unsigned long)-1;
+               context->names[idx].pino  = inode->i_ino;
+       } else {
+               context->names[idx].ino   = inode->i_ino;
+               context->names[idx].pino  = (unsigned long)-1;
+       }
+}
+
+/**
+ * audit_inode_child - collect inode info for created/removed objects
+ * @dname: inode's dentry name
+ * @inode: inode being audited
+ * @pino: inode number of dentry parent
+ *
+ * For syscalls that create or remove filesystem objects, audit_inode
+ * can only collect information for the filesystem object's parent.
+ * This call updates the audit context with the child's information.
+ * Syscalls that create a new filesystem object must be hooked after
+ * the object is created.  Syscalls that remove a filesystem object
+ * must be hooked prior, in order to capture the target inode during
+ * unsuccessful attempts.
+ */
+void __audit_inode_child(const char *dname, const struct inode *inode,
+                        unsigned long pino)
+{
+       int idx;
+       struct audit_context *context = current->audit_context;
+
+       if (!context->in_syscall)
+               return;
+
+       /* determine matching parent */
+       if (dname)
+               for (idx = 0; idx < context->name_count; idx++)
+                       if (context->names[idx].pino == pino) {
+                               const char *n;
+                               const char *name = context->names[idx].name;
+                               int dlen = strlen(dname);
+                               int nlen = name ? strlen(name) : 0;
+
+                               if (nlen < dlen)
+                                       continue;
+                               
+                               /* disregard trailing slashes */
+                               n = name + nlen - 1;
+                               while ((*n == '/') && (n > name))
+                                       n--;
+
+                               /* find last path component */
+                               n = n - dlen + 1;
+                               if (n < name)
+                                       continue;
+                               else if (n > name) {
+                                       if (*--n != '/')
+                                               continue;
+                                       else
+                                               n++;
+                               }
+
+                               if (strncmp(n, dname, dlen) == 0)
+                                       goto update_context;
+                       }
+
+       /* catch-all in case match not found */
+       idx = context->name_count++;
+       context->names[idx].name  = NULL;
+       context->names[idx].pino  = pino;
+#if AUDIT_DEBUG
+       context->ino_count++;
+#endif
+
+update_context:
+       if (inode) {
+               context->names[idx].ino   = inode->i_ino;
+               context->names[idx].dev   = inode->i_sb->s_dev;
+               context->names[idx].mode  = inode->i_mode;
+               context->names[idx].uid   = inode->i_uid;
+               context->names[idx].gid   = inode->i_gid;
+               context->names[idx].rdev  = inode->i_rdev;
+               audit_inode_context(idx, inode);
+       }
 }
 
+/**
+ * auditsc_get_stamp - get local copies of audit_context values
+ * @ctx: audit_context for the task
+ * @t: timespec to store time recorded in the audit_context
+ * @serial: serial value that is recorded in the audit_context
+ *
+ * Also sets the context as auditable.
+ */
 void auditsc_get_stamp(struct audit_context *ctx,
                       struct timespec *t, unsigned int *serial)
 {
@@ -1170,6 +1117,15 @@ void auditsc_get_stamp(struct audit_context *ctx,
        ctx->auditable = 1;
 }
 
+/**
+ * audit_set_loginuid - set a task's audit_context loginuid
+ * @task: task whose audit context is being modified
+ * @loginuid: loginuid value
+ *
+ * Returns 0.
+ *
+ * Called (set) from fs/proc/base.c::proc_loginuid_write().
+ */
 int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
 {
        if (task->audit_context) {
@@ -1188,12 +1144,59 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
        return 0;
 }
 
+/**
+ * audit_get_loginuid - get the loginuid for an audit_context
+ * @ctx: the audit_context
+ *
+ * Returns the context's loginuid or -1 if @ctx is NULL.
+ */
 uid_t audit_get_loginuid(struct audit_context *ctx)
 {
        return ctx ? ctx->loginuid : -1;
 }
 
-int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
+static char *audit_ipc_context(struct kern_ipc_perm *ipcp)
+{
+       struct audit_context *context = current->audit_context;
+       char *ctx = NULL;
+       int len = 0;
+
+       if (likely(!context))
+               return NULL;
+
+       len = security_ipc_getsecurity(ipcp, NULL, 0);
+       if (len == -EOPNOTSUPP)
+               goto ret;
+       if (len < 0)
+               goto error_path;
+
+       ctx = kmalloc(len, GFP_ATOMIC);
+       if (!ctx)
+               goto error_path;
+
+       len = security_ipc_getsecurity(ipcp, ctx, len);
+       if (len < 0)
+               goto error_path;
+
+       return ctx;
+
+error_path:
+       kfree(ctx);
+       audit_panic("error in audit_ipc_context");
+ret:
+       return NULL;
+}
+
+/**
+ * audit_ipc_perms - record audit data for ipc
+ * @qbytes: msgq bytes
+ * @uid: msgq user id
+ * @gid: msgq group id
+ * @mode: msgq mode (permissions)
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp)
 {
        struct audit_aux_data_ipcctl *ax;
        struct audit_context *context = current->audit_context;
@@ -1201,7 +1204,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
        if (likely(!context))
                return 0;
 
-       ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
        if (!ax)
                return -ENOMEM;
 
@@ -1209,6 +1212,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
        ax->uid = uid;
        ax->gid = gid;
        ax->mode = mode;
+       ax->ctx = audit_ipc_context(ipcp);
 
        ax->d.type = AUDIT_IPC;
        ax->d.next = context->aux;
@@ -1216,6 +1220,13 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
        return 0;
 }
 
+/**
+ * audit_socketcall - record audit data for sys_socketcall
+ * @nargs: number of args
+ * @args: args array
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
 int audit_socketcall(int nargs, unsigned long *args)
 {
        struct audit_aux_data_socketcall *ax;
@@ -1237,6 +1248,13 @@ int audit_socketcall(int nargs, unsigned long *args)
        return 0;
 }
 
+/**
+ * audit_sockaddr - record audit data for sys_bind, sys_connect, sys_sendto
+ * @len: data length in user space
+ * @a: data address in kernel space
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
 int audit_sockaddr(int len, void *a)
 {
        struct audit_aux_data_sockaddr *ax;
@@ -1258,6 +1276,15 @@ int audit_sockaddr(int len, void *a)
        return 0;
 }
 
+/**
+ * audit_avc_path - record the granting or denial of permissions
+ * @dentry: dentry to record
+ * @mnt: mnt to record
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ *
+ * Called from security/selinux/avc.c::avc_audit()
+ */
 int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
 {
        struct audit_aux_data_path *ax;
@@ -1279,6 +1306,14 @@ int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
        return 0;
 }
 
+/**
+ * audit_signal_info - record signal info for shutting down audit subsystem
+ * @sig: signal value
+ * @t: task being signaled
+ *
+ * If the audit subsystem is being terminated, record the task (pid)
+ * and uid that is doing that.
+ */
 void audit_signal_info(int sig, struct task_struct *t)
 {
        extern pid_t audit_sig_pid;
@@ -1295,4 +1330,3 @@ void audit_signal_info(int sig, struct task_struct *t)
                }
        }
 }
-