Merge tag 'for-usb-linus-2012-10-11' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / kernel / auditsc.c
index f4a7756..2f186ed 100644 (file)
@@ -81,9 +81,6 @@
  * a name dynamically and also add those to the list anchored by names_list. */
 #define AUDIT_NAMES    5
 
-/* Indicates that audit should log the full pathname. */
-#define AUDIT_NAME_FULL -1
-
 /* no execve audit message should be longer than this (userspace limits) */
 #define MAX_EXECVE_AUDIT_LEN 7500
 
@@ -106,27 +103,29 @@ struct audit_cap_data {
  * we don't let putname() free it (instead we free all of the saved
  * pointers at syscall exit time).
  *
- * Further, in fs/namei.c:path_lookup() we store the inode and device. */
+ * Further, in fs/namei.c:path_lookup() we store the inode and device.
+ */
 struct audit_names {
-       struct list_head list;          /* audit_context->names_list */
-       const char      *name;
-       unsigned long   ino;
-       dev_t           dev;
-       umode_t         mode;
-       kuid_t          uid;
-       kgid_t          gid;
-       dev_t           rdev;
-       u32             osid;
-       struct audit_cap_data fcap;
-       unsigned int    fcap_ver;
-       int             name_len;       /* number of name's characters to log */
-       bool            name_put;       /* call __putname() for this name */
+       struct list_head        list;           /* audit_context->names_list */
+       struct filename *name;
+       unsigned long           ino;
+       dev_t                   dev;
+       umode_t                 mode;
+       kuid_t                  uid;
+       kgid_t                  gid;
+       dev_t                   rdev;
+       u32                     osid;
+       struct audit_cap_data    fcap;
+       unsigned int            fcap_ver;
+       int                     name_len;       /* number of name's characters to log */
+       unsigned char           type;           /* record type */
+       bool                    name_put;       /* call __putname() for this name */
        /*
         * This was an allocated audit_names and not from the array of
         * names allocated in the task audit context.  Thus this name
         * should be freed on syscall exit
         */
-       bool            should_free;
+       bool                    should_free;
 };
 
 struct audit_aux_data {
@@ -998,7 +997,7 @@ static inline void audit_free_names(struct audit_context *context)
                       context->ino_count);
                list_for_each_entry(n, &context->names_list, list) {
                        printk(KERN_ERR "names[%d] = %p = %s\n", i,
-                              n->name, n->name ?: "(null)");
+                              n->name, n->name->name ?: "(null)");
                }
                dump_stack();
                return;
@@ -1555,7 +1554,7 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n,
                case AUDIT_NAME_FULL:
                        /* log the full path */
                        audit_log_format(ab, " name=");
-                       audit_log_untrustedstring(ab, n->name);
+                       audit_log_untrustedstring(ab, n->name->name);
                        break;
                case 0:
                        /* name was specified as a relative path and the
@@ -1565,7 +1564,7 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n,
                default:
                        /* log the name's directory component */
                        audit_log_format(ab, " name=");
-                       audit_log_n_untrustedstring(ab, n->name,
+                       audit_log_n_untrustedstring(ab, n->name->name,
                                                    n->name_len);
                }
        } else
@@ -1995,7 +1994,8 @@ retry:
 #endif
 }
 
-static struct audit_names *audit_alloc_name(struct audit_context *context)
+static struct audit_names *audit_alloc_name(struct audit_context *context,
+                                               unsigned char type)
 {
        struct audit_names *aname;
 
@@ -2010,6 +2010,7 @@ static struct audit_names *audit_alloc_name(struct audit_context *context)
        }
 
        aname->ino = (unsigned long)-1;
+       aname->type = type;
        list_add_tail(&aname->list, &context->names_list);
 
        context->name_count++;
@@ -2019,6 +2020,29 @@ static struct audit_names *audit_alloc_name(struct audit_context *context)
        return aname;
 }
 
+/**
+ * audit_reusename - fill out filename with info from existing entry
+ * @uptr: userland ptr to pathname
+ *
+ * Search the audit_names list for the current audit context. If there is an
+ * existing entry with a matching "uptr" then return the filename
+ * associated with that audit_name. If not, return NULL.
+ */
+struct filename *
+__audit_reusename(const __user char *uptr)
+{
+       struct audit_context *context = current->audit_context;
+       struct audit_names *n;
+
+       list_for_each_entry(n, &context->names_list, list) {
+               if (!n->name)
+                       continue;
+               if (n->name->uptr == uptr)
+                       return n->name;
+       }
+       return NULL;
+}
+
 /**
  * audit_getname - add a name to the list
  * @name: name to add
@@ -2026,7 +2050,7 @@ static struct audit_names *audit_alloc_name(struct audit_context *context)
  * Add a name to the list of audit names for this context.
  * Called from fs/namei.c:getname().
  */
-void __audit_getname(const char *name)
+void __audit_getname(struct filename *name)
 {
        struct audit_context *context = current->audit_context;
        struct audit_names *n;
@@ -2040,13 +2064,19 @@ void __audit_getname(const char *name)
                return;
        }
 
-       n = audit_alloc_name(context);
+#if AUDIT_DEBUG
+       /* The filename _must_ have a populated ->name */
+       BUG_ON(!name->name);
+#endif
+
+       n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN);
        if (!n)
                return;
 
        n->name = name;
        n->name_len = AUDIT_NAME_FULL;
        n->name_put = true;
+       name->aname = n;
 
        if (!context->pwd.dentry)
                get_fs_pwd(current->fs, &context->pwd);
@@ -2059,7 +2089,7 @@ void __audit_getname(const char *name)
  * then we delay the putname until syscall exit.
  * Called from include/linux/fs.h:putname().
  */
-void audit_putname(const char *name)
+void audit_putname(struct filename *name)
 {
        struct audit_context *context = current->audit_context;
 
@@ -2074,7 +2104,7 @@ void audit_putname(const char *name)
 
                        list_for_each_entry(n, &context->names_list, list)
                                printk(KERN_ERR "name[%d] = %p = %s\n", i,
-                                      n->name, n->name ?: "(null)");
+                                      n->name, n->name->name ?: "(null)");
                        }
 #endif
                __putname(name);
@@ -2088,8 +2118,8 @@ void audit_putname(const char *name)
                               " put_count=%d\n",
                               __FILE__, __LINE__,
                               context->serial, context->major,
-                              context->in_syscall, name, context->name_count,
-                              context->put_count);
+                              context->in_syscall, name->name,
+                              context->name_count, context->put_count);
                        dump_stack();
                }
        }
@@ -2132,13 +2162,13 @@ static void audit_copy_inode(struct audit_names *name, const struct dentry *dent
 }
 
 /**
- * audit_inode - store the inode and device from a lookup
+ * __audit_inode - store the inode and device from a lookup
  * @name: name being audited
  * @dentry: dentry being audited
- *
- * Called from fs/namei.c:path_lookup().
+ * @parent: does this dentry represent the parent?
  */
-void __audit_inode(const char *name, const struct dentry *dentry)
+void __audit_inode(struct filename *name, const struct dentry *dentry,
+                  unsigned int parent)
 {
        struct audit_context *context = current->audit_context;
        const struct inode *inode = dentry->d_inode;
@@ -2147,24 +2177,69 @@ void __audit_inode(const char *name, const struct dentry *dentry)
        if (!context->in_syscall)
                return;
 
+       if (!name)
+               goto out_alloc;
+
+#if AUDIT_DEBUG
+       /* The struct filename _must_ have a populated ->name */
+       BUG_ON(!name->name);
+#endif
+       /*
+        * If we have a pointer to an audit_names entry already, then we can
+        * just use it directly if the type is correct.
+        */
+       n = name->aname;
+       if (n) {
+               if (parent) {
+                       if (n->type == AUDIT_TYPE_PARENT ||
+                           n->type == AUDIT_TYPE_UNKNOWN)
+                               goto out;
+               } else {
+                       if (n->type != AUDIT_TYPE_PARENT)
+                               goto out;
+               }
+       }
+
        list_for_each_entry_reverse(n, &context->names_list, list) {
-               if (n->name && (n->name == name))
-                       goto out;
+               /* does the name pointer match? */
+               if (!n->name || n->name->name != name->name)
+                       continue;
+
+               /* match the correct record type */
+               if (parent) {
+                       if (n->type == AUDIT_TYPE_PARENT ||
+                           n->type == AUDIT_TYPE_UNKNOWN)
+                               goto out;
+               } else {
+                       if (n->type != AUDIT_TYPE_PARENT)
+                               goto out;
+               }
        }
 
-       /* unable to find the name from a previous getname() */
-       n = audit_alloc_name(context);
+out_alloc:
+       /* unable to find the name from a previous getname(). Allocate a new
+        * anonymous entry.
+        */
+       n = audit_alloc_name(context, AUDIT_TYPE_NORMAL);
        if (!n)
                return;
 out:
+       if (parent) {
+               n->name_len = n->name ? parent_len(n->name->name) : AUDIT_NAME_FULL;
+               n->type = AUDIT_TYPE_PARENT;
+       } else {
+               n->name_len = AUDIT_NAME_FULL;
+               n->type = AUDIT_TYPE_NORMAL;
+       }
        handle_path(dentry);
        audit_copy_inode(n, dentry, inode);
 }
 
 /**
- * audit_inode_child - collect inode info for created/removed objects
- * @dentry: dentry being audited
+ * __audit_inode_child - collect inode info for created/removed objects
  * @parent: inode of dentry parent
+ * @dentry: dentry being audited
+ * @type:   AUDIT_TYPE_* value that we're looking for
  *
  * For syscalls that create or remove filesystem objects, audit_inode
  * can only collect information for the filesystem object's parent.
@@ -2174,15 +2249,14 @@ out:
  * must be hooked prior, in order to capture the target inode during
  * unsuccessful attempts.
  */
-void __audit_inode_child(const struct dentry *dentry,
-                        const struct inode *parent)
+void __audit_inode_child(const struct inode *parent,
+                        const struct dentry *dentry,
+                        const unsigned char type)
 {
        struct audit_context *context = current->audit_context;
-       const char *found_parent = NULL, *found_child = NULL;
        const struct inode *inode = dentry->d_inode;
        const char *dname = dentry->d_name.name;
-       struct audit_names *n;
-       int dirlen = 0;
+       struct audit_names *n, *found_parent = NULL, *found_child = NULL;
 
        if (!context->in_syscall)
                return;
@@ -2190,62 +2264,65 @@ void __audit_inode_child(const struct dentry *dentry,
        if (inode)
                handle_one(inode);
 
-       /* parent is more likely, look for it first */
+       /* look for a parent entry first */
        list_for_each_entry(n, &context->names_list, list) {
-               if (!n->name)
+               if (!n->name || n->type != AUDIT_TYPE_PARENT)
                        continue;
 
                if (n->ino == parent->i_ino &&
-                   !audit_compare_dname_path(dname, n->name, &dirlen)) {
-                       n->name_len = dirlen; /* update parent data in place */
-                       found_parent = n->name;
-                       goto add_names;
+                   !audit_compare_dname_path(dname, n->name->name, n->name_len)) {
+                       found_parent = n;
+                       break;
                }
        }
 
-       /* no matching parent, look for matching child */
+       /* is there a matching child entry? */
        list_for_each_entry(n, &context->names_list, list) {
-               if (!n->name)
+               /* can only match entries that have a name */
+               if (!n->name || n->type != type)
                        continue;
 
-               /* strcmp() is the more likely scenario */
-               if (!strcmp(dname, n->name) ||
-                    !audit_compare_dname_path(dname, n->name, &dirlen)) {
-                       if (inode)
-                               audit_copy_inode(n, NULL, inode);
-                       else
-                               n->ino = (unsigned long)-1;
-                       found_child = n->name;
-                       goto add_names;
+               /* if we found a parent, make sure this one is a child of it */
+               if (found_parent && (n->name != found_parent->name))
+                       continue;
+
+               if (!strcmp(dname, n->name->name) ||
+                   !audit_compare_dname_path(dname, n->name->name,
+                                               found_parent ?
+                                               found_parent->name_len :
+                                               AUDIT_NAME_FULL)) {
+                       found_child = n;
+                       break;
                }
        }
 
-add_names:
        if (!found_parent) {
-               n = audit_alloc_name(context);
+               /* create a new, "anonymous" parent record */
+               n = audit_alloc_name(context, AUDIT_TYPE_PARENT);
                if (!n)
                        return;
                audit_copy_inode(n, NULL, parent);
        }
 
        if (!found_child) {
-               n = audit_alloc_name(context);
-               if (!n)
+               found_child = audit_alloc_name(context, type);
+               if (!found_child)
                        return;
 
                /* Re-use the name belonging to the slot for a matching parent
                 * directory. All names for this context are relinquished in
                 * audit_free_names() */
                if (found_parent) {
-                       n->name = found_parent;
-                       n->name_len = AUDIT_NAME_FULL;
+                       found_child->name = found_parent->name;
+                       found_child->name_len = AUDIT_NAME_FULL;
                        /* don't call __putname() */
-                       n->name_put = false;
+                       found_child->name_put = false;
                }
-
-               if (inode)
-                       audit_copy_inode(n, NULL, inode);
        }
+       if (inode)
+               audit_copy_inode(found_child, dentry, inode);
+       else
+               found_child->ino = (unsigned long)-1;
 }
 EXPORT_SYMBOL_GPL(__audit_inode_child);