From 1c2e51e8c162417d2831007ec256ede06c3a0201 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:20 -0400 Subject: audit: pass in dentry to audit_copy_inode wherever possible In some cases, we were passing in NULL even when we have a dentry. Reported-by: Eric Paris Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/auditsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index f4a7756f999c..4d1bd62b090b 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2212,7 +2212,7 @@ void __audit_inode_child(const struct dentry *dentry, if (!strcmp(dname, n->name) || !audit_compare_dname_path(dname, n->name, &dirlen)) { if (inode) - audit_copy_inode(n, NULL, inode); + audit_copy_inode(n, dentry, inode); else n->ino = (unsigned long)-1; found_child = n->name; @@ -2244,7 +2244,7 @@ add_names: } if (inode) - audit_copy_inode(n, NULL, inode); + audit_copy_inode(n, dentry, inode); } } EXPORT_SYMBOL_GPL(__audit_inode_child); -- cgit From 9cec9d68ae53aae60b4a1fca4505c75a1d026392 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:21 -0400 Subject: audit: no need to walk list in audit_inode if name is NULL If name is NULL then the condition in the loop will never be true. Also, with this change, we can eliminate the check for n->name == NULL since the equivalence check will never be true if it is. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/auditsc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4d1bd62b090b..2e481141b014 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2147,11 +2147,15 @@ void __audit_inode(const char *name, const struct dentry *dentry) if (!context->in_syscall) return; + if (!name) + goto out_alloc; + list_for_each_entry_reverse(n, &context->names_list, list) { - if (n->name && (n->name == name)) + if (n->name == name) goto out; } +out_alloc: /* unable to find the name from a previous getname() */ n = audit_alloc_name(context); if (!n) -- cgit From c43a25abba97c7d87131e71db6be24b24d7791a5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:21 -0400 Subject: audit: reverse arguments to audit_inode_child Most of the callers get called with an inode and dentry in the reverse order. The compiler then has to reshuffle the arg registers and/or stack in order to pass them on to audit_inode_child. Reverse those arguments for a micro-optimization. Reported-by: Eric Paris Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/auditsc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 2e481141b014..40743af02d8f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2166,9 +2166,9 @@ out: } /** - * 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 * * For syscalls that create or remove filesystem objects, audit_inode * can only collect information for the filesystem object's parent. @@ -2178,8 +2178,8 @@ 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) { struct audit_context *context = current->audit_context; const char *found_parent = NULL, *found_child = NULL; -- cgit From 78e2e802a8519031e5858595070b39713e26340d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:22 -0400 Subject: audit: add a new "type" field to audit_names struct For now, we just have two possibilities: UNKNOWN: for a new audit_names record that we don't know anything about yet NORMAL: for everything else In later patches, we'll add other types so we can distinguish and update records created under different circumstances. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/auditsc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'kernel') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 40743af02d8f..19b232f86d70 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -120,6 +120,7 @@ struct audit_names { 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 @@ -1995,7 +1996,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 +2012,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++; @@ -2040,7 +2043,7 @@ void __audit_getname(const char *name) return; } - n = audit_alloc_name(context); + n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); if (!n) return; @@ -2157,12 +2160,13 @@ void __audit_inode(const char *name, const struct dentry *dentry) out_alloc: /* unable to find the name from a previous getname() */ - n = audit_alloc_name(context); + n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); if (!n) return; out: handle_path(dentry); audit_copy_inode(n, dentry, inode); + n->type = AUDIT_TYPE_NORMAL; } /** @@ -2219,6 +2223,7 @@ void __audit_inode_child(const struct inode *parent, audit_copy_inode(n, dentry, inode); else n->ino = (unsigned long)-1; + n->type = AUDIT_TYPE_NORMAL; found_child = n->name; goto add_names; } @@ -2226,14 +2231,14 @@ void __audit_inode_child(const struct inode *parent, add_names: if (!found_parent) { - n = audit_alloc_name(context); + n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); if (!n) return; audit_copy_inode(n, NULL, parent); } if (!found_child) { - n = audit_alloc_name(context); + n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); if (!n) return; -- cgit From bfcec7087458812f575d9022b2d151641f34ee84 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:23 -0400 Subject: audit: set the name_len in audit_inode for parent lookups Currently, this gets set mostly by happenstance when we call into audit_inode_child. While that might be a little more efficient, it seems wrong. If the syscall ends up failing before audit_inode_child ever gets called, then you'll have an audit_names record that shows the full path but has the parent inode info attached. Fix this by passing in a parent flag when we call audit_inode that gets set to the value of LOOKUP_PARENT. We can then fix up the pathname for the audit entry correctly from the get-go. While we're at it, clean up the no-op macro for audit_inode in the !CONFIG_AUDITSYSCALL case. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/audit.h | 1 + kernel/auditfilter.c | 30 ++++++++++++++++++++++++++++++ kernel/auditsc.c | 41 +++++++++++++++++++++++++++++------------ 3 files changed, 60 insertions(+), 12 deletions(-) (limited to 'kernel') diff --git a/kernel/audit.h b/kernel/audit.h index 9eb3d79482b6..163b9a5d9441 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -78,6 +78,7 @@ extern int audit_match_class(int class, unsigned syscall); extern int audit_comparator(const u32 left, const u32 op, const u32 right); extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); +extern int parent_len(const char *path); extern int audit_compare_dname_path(const char *dname, const char *path, int *dirlen); extern struct sk_buff * audit_make_reply(int pid, int seq, int type, diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index c4bcdbaf4d4d..71bb13598df3 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1298,6 +1298,36 @@ int audit_gid_comparator(kgid_t left, u32 op, kgid_t right) } } +/** + * parent_len - find the length of the parent portion of a pathname + * @path: pathname of which to determine length + */ +int parent_len(const char *path) +{ + int plen; + const char *p; + + plen = strlen(path); + + if (plen == 0) + return plen; + + /* disregard trailing slashes */ + p = path + plen - 1; + while ((*p == '/') && (p > path)) + p--; + + /* walk backward until we find the next slash or hit beginning */ + while ((*p != '/') && (p > path)) + p--; + + /* did we find a slash? Then increment to include it in path */ + if (*p == '/') + p++; + + return p - path; +} + /* Compare given dentry name with last component in given path, * return of 0 indicates a match. */ int audit_compare_dname_path(const char *dname, const char *path, diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 19b232f86d70..b87b28947acc 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2135,13 +2135,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(const char *name, const struct dentry *dentry, + unsigned int parent) { struct audit_context *context = current->audit_context; const struct inode *inode = dentry->d_inode; @@ -2154,19 +2154,38 @@ void __audit_inode(const char *name, const struct dentry *dentry) goto out_alloc; list_for_each_entry_reverse(n, &context->names_list, list) { - if (n->name == name) - goto out; + /* does the name pointer match? */ + if (n->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; + } } out_alloc: - /* unable to find the name from a previous getname() */ + /* 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) : 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); - n->type = AUDIT_TYPE_NORMAL; } /** @@ -2190,7 +2209,6 @@ void __audit_inode_child(const struct inode *parent, const struct inode *inode = dentry->d_inode; const char *dname = dentry->d_name.name; struct audit_names *n; - int dirlen = 0; if (!context->in_syscall) return; @@ -2204,8 +2222,7 @@ void __audit_inode_child(const struct inode *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 */ + !audit_compare_dname_path(dname, n->name, NULL)) { found_parent = n->name; goto add_names; } @@ -2218,7 +2235,7 @@ void __audit_inode_child(const struct inode *parent, /* strcmp() is the more likely scenario */ if (!strcmp(dname, n->name) || - !audit_compare_dname_path(dname, n->name, &dirlen)) { + !audit_compare_dname_path(dname, n->name, NULL)) { if (inode) audit_copy_inode(n, dentry, inode); else -- cgit From 563a0d1236c2c58d584ef122a5cdc9930e5860b3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:24 -0400 Subject: audit: remove dirlen argument to audit_compare_dname_path All the callers set this to NULL now. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/audit.h | 3 +-- kernel/audit_watch.c | 2 +- kernel/auditfilter.c | 6 +----- kernel/auditsc.c | 4 ++-- 4 files changed, 5 insertions(+), 10 deletions(-) (limited to 'kernel') diff --git a/kernel/audit.h b/kernel/audit.h index 163b9a5d9441..1038e23eb61c 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -79,8 +79,7 @@ extern int audit_comparator(const u32 left, const u32 op, const u32 right); extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); extern int parent_len(const char *path); -extern int audit_compare_dname_path(const char *dname, const char *path, - int *dirlen); +extern int audit_compare_dname_path(const char *dname, const char *path); extern struct sk_buff * audit_make_reply(int pid, int seq, int type, int done, int multi, const void *payload, int size); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 1c22ec3d87bc..deb97c139e0c 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -265,7 +265,7 @@ static void audit_update_watch(struct audit_parent *parent, /* Run all of the watches on this parent looking for the one that * matches the given dname */ list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) { - if (audit_compare_dname_path(dname, owatch->path, NULL)) + if (audit_compare_dname_path(dname, owatch->path)) continue; /* If the update involves invalidating rules, do the inode-based diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 71bb13598df3..ff4011c19b13 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1330,8 +1330,7 @@ int parent_len(const char *path) /* Compare given dentry name with last component in given path, * return of 0 indicates a match. */ -int audit_compare_dname_path(const char *dname, const char *path, - int *dirlen) +int audit_compare_dname_path(const char *dname, const char *path) { int dlen, plen; const char *p; @@ -1360,9 +1359,6 @@ int audit_compare_dname_path(const char *dname, const char *path, p++; } - /* return length of path's directory component */ - if (dirlen) - *dirlen = p - path; return strncmp(p, dname, dlen); } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b87b28947acc..09c7b6b4f8e6 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2222,7 +2222,7 @@ void __audit_inode_child(const struct inode *parent, continue; if (n->ino == parent->i_ino && - !audit_compare_dname_path(dname, n->name, NULL)) { + !audit_compare_dname_path(dname, n->name)) { found_parent = n->name; goto add_names; } @@ -2235,7 +2235,7 @@ void __audit_inode_child(const struct inode *parent, /* strcmp() is the more likely scenario */ if (!strcmp(dname, n->name) || - !audit_compare_dname_path(dname, n->name, NULL)) { + !audit_compare_dname_path(dname, n->name)) { if (inode) audit_copy_inode(n, dentry, inode); else -- cgit From 29e9a3467c1367549568d7d411d5f30209ae181b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 10 Oct 2012 15:25:24 -0400 Subject: audit: make audit_compare_dname_path use parent_len helper Signed-off-by: Eric Paris Signed-off-by: Al Viro --- kernel/auditfilter.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) (limited to 'kernel') diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index ff4011c19b13..d705eb17661b 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1332,32 +1332,19 @@ int parent_len(const char *path) * return of 0 indicates a match. */ int audit_compare_dname_path(const char *dname, const char *path) { - int dlen, plen; + int dlen, pathlen, parentlen; const char *p; - if (!dname || !path) - return 1; - dlen = strlen(dname); - plen = strlen(path); - if (plen < dlen) + pathlen = strlen(path); + if (pathlen < dlen) return 1; - /* disregard trailing slashes */ - p = path + plen - 1; - while ((*p == '/') && (p > path)) - p--; - - /* find last path component */ - p = p - dlen + 1; - if (p < path) + parentlen = parent_len(path); + if (pathlen - parentlen != dlen) return 1; - else if (p > path) { - if (*--p != '/') - return 1; - else - p++; - } + + p = path + parentlen; return strncmp(p, dname, dlen); } -- cgit From e3d6b07b8ba161f638b026feba0c3c97875d7f1c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:25 -0400 Subject: audit: optimize audit_compare_dname_path In the cases where we already know the length of the parent, pass it as a parm so we don't need to recompute it. In the cases where we don't know the length, pass in AUDIT_NAME_FULL (-1) to indicate that it should be determined. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/audit.h | 5 ++++- kernel/audit_watch.c | 3 ++- kernel/auditfilter.c | 16 +++++++++++----- kernel/auditsc.c | 8 +++----- 4 files changed, 20 insertions(+), 12 deletions(-) (limited to 'kernel') diff --git a/kernel/audit.h b/kernel/audit.h index 1038e23eb61c..d51cba868e1b 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -74,12 +74,15 @@ static inline int audit_hash_ino(u32 ino) return (ino & (AUDIT_INODE_BUCKETS-1)); } +/* Indicates that audit should log the full pathname. */ +#define AUDIT_NAME_FULL -1 + extern int audit_match_class(int class, unsigned syscall); extern int audit_comparator(const u32 left, const u32 op, const u32 right); extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); extern int parent_len(const char *path); -extern int audit_compare_dname_path(const char *dname, const char *path); +extern int audit_compare_dname_path(const char *dname, const char *path, int plen); extern struct sk_buff * audit_make_reply(int pid, int seq, int type, int done, int multi, const void *payload, int size); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index deb97c139e0c..9a9ae6e3d290 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -265,7 +265,8 @@ static void audit_update_watch(struct audit_parent *parent, /* Run all of the watches on this parent looking for the one that * matches the given dname */ list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) { - if (audit_compare_dname_path(dname, owatch->path)) + if (audit_compare_dname_path(dname, owatch->path, + AUDIT_NAME_FULL)) continue; /* If the update involves invalidating rules, do the inode-based diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index d705eb17661b..7f19f23d38a3 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1328,11 +1328,17 @@ int parent_len(const char *path) return p - path; } -/* Compare given dentry name with last component in given path, - * return of 0 indicates a match. */ -int audit_compare_dname_path(const char *dname, const char *path) +/** + * audit_compare_dname_path - compare given dentry name with last component in + * given path. Return of 0 indicates a match. + * @dname: dentry name that we're comparing + * @path: full pathname that we're comparing + * @parentlen: length of the parent if known. Passing in AUDIT_NAME_FULL + * here indicates that we must compute this value. + */ +int audit_compare_dname_path(const char *dname, const char *path, int parentlen) { - int dlen, pathlen, parentlen; + int dlen, pathlen; const char *p; dlen = strlen(dname); @@ -1340,7 +1346,7 @@ int audit_compare_dname_path(const char *dname, const char *path) if (pathlen < dlen) return 1; - parentlen = parent_len(path); + parentlen = parentlen == AUDIT_NAME_FULL ? parent_len(path) : parentlen; if (pathlen - parentlen != dlen) return 1; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 09c7b6b4f8e6..0160a68b4d7f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -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 @@ -2222,7 +2219,7 @@ void __audit_inode_child(const struct inode *parent, continue; if (n->ino == parent->i_ino && - !audit_compare_dname_path(dname, n->name)) { + !audit_compare_dname_path(dname, n->name, n->name_len)) { found_parent = n->name; goto add_names; } @@ -2235,7 +2232,8 @@ void __audit_inode_child(const struct inode *parent, /* strcmp() is the more likely scenario */ if (!strcmp(dname, n->name) || - !audit_compare_dname_path(dname, n->name)) { + !audit_compare_dname_path(dname, n->name, + AUDIT_NAME_FULL)) { if (inode) audit_copy_inode(n, dentry, inode); else -- cgit From 4fa6b5ecbf092c6ee752ece8a55d71f663d23254 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:25 -0400 Subject: audit: overhaul __audit_inode_child to accomodate retrying In order to accomodate retrying path-based syscalls, we need to add a new "type" argument to audit_inode_child. This will tell us whether we're looking for a child entry that represents a create or a delete. If we find a parent, don't automatically assume that we need to create a new entry. Instead, use the information we have to try to find an existing entry first. Update it if one is found and create a new one if not. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/auditsc.c | 57 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 27 deletions(-) (limited to 'kernel') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 0160a68b4d7f..d147585e9ef3 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2189,6 +2189,7 @@ out: * __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. @@ -2199,13 +2200,13 @@ out: * unsuccessful attempts. */ void __audit_inode_child(const struct inode *parent, - const struct dentry *dentry) + 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; + struct audit_names *n, *found_parent = NULL, *found_child = NULL; if (!context->in_syscall) return; @@ -2213,63 +2214,65 @@ void __audit_inode_child(const struct inode *parent, 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, n->name_len)) { - found_parent = n->name; - goto add_names; + 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; + + /* if we found a parent, make sure this one is a child of it */ + if (found_parent && (n->name != found_parent->name)) continue; - /* strcmp() is the more likely scenario */ if (!strcmp(dname, n->name) || !audit_compare_dname_path(dname, n->name, + found_parent ? + found_parent->name_len : AUDIT_NAME_FULL)) { - if (inode) - audit_copy_inode(n, dentry, inode); - else - n->ino = (unsigned long)-1; - n->type = AUDIT_TYPE_NORMAL; - found_child = n->name; - goto add_names; + found_child = n; + break; } } -add_names: if (!found_parent) { - n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); + /* 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, AUDIT_TYPE_NORMAL); - 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, dentry, inode); } + if (inode) + audit_copy_inode(found_child, dentry, inode); + else + found_child->ino = (unsigned long)-1; } EXPORT_SYMBOL_GPL(__audit_inode_child); -- cgit From cfd4da175599938f21a81cdd80df02fa4151dcba Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:27 -0400 Subject: acct: constify the name arg to acct_on Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/acct.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/acct.c b/kernel/acct.c index 6cd7529c9e6a..5be01017d30f 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -193,7 +193,7 @@ static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file, } } -static int acct_on(char *name) +static int acct_on(const char *name) { struct file *file; struct vfsmount *mnt; -- cgit From 91a27b2a756784714e924e5e854b919273082d26 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:28 -0400 Subject: vfs: define struct filename and have getname() return it getname() is intended to copy pathname strings from userspace into a kernel buffer. The result is just a string in kernel space. It would however be quite helpful to be able to attach some ancillary info to the string. For instance, we could attach some audit-related info to reduce the amount of audit-related processing needed. When auditing is enabled, we could also call getname() on the string more than once and not need to recopy it from userspace. This patchset converts the getname()/putname() interfaces to return a struct instead of a string. For now, the struct just tracks the string in kernel space and the original userland pointer for it. Later, we'll add other information to the struct as it becomes convenient. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/acct.c | 4 ++-- kernel/auditsc.c | 64 +++++++++++++++++++++++++++++++------------------------- 2 files changed, 37 insertions(+), 31 deletions(-) (limited to 'kernel') diff --git a/kernel/acct.c b/kernel/acct.c index 5be01017d30f..08354195eecc 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -260,10 +260,10 @@ SYSCALL_DEFINE1(acct, const char __user *, name) return -EPERM; if (name) { - char *tmp = getname(name); + struct filename *tmp = getname(name); if (IS_ERR(tmp)) return (PTR_ERR(tmp)); - error = acct_on(tmp); + error = acct_on(tmp->name); putname(tmp); } else { struct bsd_acct_struct *acct; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d147585e9ef3..d4d82319eed5 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -103,28 +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 */ - unsigned char type; /* record type */ - 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 { @@ -996,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; @@ -1553,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 @@ -1563,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 @@ -2026,7 +2027,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,6 +2041,11 @@ void __audit_getname(const char *name) return; } +#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; @@ -2059,7 +2065,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 +2080,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 +2094,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(); } } @@ -2152,7 +2158,7 @@ void __audit_inode(const char *name, const struct dentry *dentry, list_for_each_entry_reverse(n, &context->names_list, list) { /* does the name pointer match? */ - if (n->name != name) + if (!n->name || n->name->name != name) continue; /* match the correct record type */ @@ -2175,7 +2181,7 @@ out_alloc: return; out: if (parent) { - n->name_len = n->name ? parent_len(n->name) : AUDIT_NAME_FULL; + 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; @@ -2220,7 +2226,7 @@ void __audit_inode_child(const struct inode *parent, continue; if (n->ino == parent->i_ino && - !audit_compare_dname_path(dname, n->name, n->name_len)) { + !audit_compare_dname_path(dname, n->name->name, n->name_len)) { found_parent = n; break; } @@ -2236,8 +2242,8 @@ void __audit_inode_child(const struct inode *parent, if (found_parent && (n->name != found_parent->name)) continue; - if (!strcmp(dname, n->name) || - !audit_compare_dname_path(dname, n->name, + if (!strcmp(dname, n->name->name) || + !audit_compare_dname_path(dname, n->name->name, found_parent ? found_parent->name_len : AUDIT_NAME_FULL)) { -- cgit From 7ac86265dc8f665cc49d6e60a125e608cd2fca14 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:28 -0400 Subject: audit: allow audit code to satisfy getname requests from its names_list Currently, if we call getname() on a userland string more than once, we'll get multiple copies of the string and multiple audit_names records. Add a function that will allow the audit_names code to satisfy getname requests using info from the audit_names list, avoiding a new allocation and audit_names records. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/auditsc.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'kernel') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d4d82319eed5..521163a5d65f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2020,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 -- cgit From 669abf4e5539c8aa48bf28c965be05c0a7b58a27 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 16:43:10 -0400 Subject: vfs: make path_openat take a struct filename pointer ...and fix up the callers. For do_file_open_root, just declare a struct filename on the stack and fill out the .name field. For do_filp_open, make it also take a struct filename pointer, and fix up its callers to call it appropriately. For filp_open, add a variant that takes a struct filename pointer and turn filp_open into a wrapper around it. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/acct.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/acct.c b/kernel/acct.c index 08354195eecc..051e071a06e7 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -193,7 +193,7 @@ static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file, } } -static int acct_on(const char *name) +static int acct_on(struct filename *pathname) { struct file *file; struct vfsmount *mnt; @@ -201,7 +201,7 @@ static int acct_on(const char *name) struct bsd_acct_struct *acct = NULL; /* Difference from BSD - they don't do O_APPEND */ - file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0); + file = file_open_name(pathname, O_WRONLY|O_APPEND|O_LARGEFILE, 0); if (IS_ERR(file)) return PTR_ERR(file); @@ -263,7 +263,7 @@ SYSCALL_DEFINE1(acct, const char __user *, name) struct filename *tmp = getname(name); if (IS_ERR(tmp)) return (PTR_ERR(tmp)); - error = acct_on(tmp->name); + error = acct_on(tmp); putname(tmp); } else { struct bsd_acct_struct *acct; -- cgit From adb5c2473d3f91526c79db972aafb20a56d3fbb3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 16:43:13 -0400 Subject: audit: make audit_inode take struct filename Keep a pointer to the audit_names "slot" in struct filename. Have all of the audit_inode callers pass a struct filename ponter to audit_inode instead of a string pointer. If the aname field is already populated, then we can skip walking the list altogether and just use it directly. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/auditsc.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 521163a5d65f..2f186ed80c40 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2076,6 +2076,7 @@ void __audit_getname(struct filename *name) 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); @@ -2166,7 +2167,7 @@ static void audit_copy_inode(struct audit_names *name, const struct dentry *dent * @dentry: dentry being audited * @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; @@ -2179,9 +2180,29 @@ void __audit_inode(const char *name, const struct dentry *dentry, 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) { /* does the name pointer match? */ - if (!n->name || n->name->name != name) + if (!n->name || n->name->name != name->name) continue; /* match the correct record type */ -- cgit