diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-12 21:04:42 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-12 21:04:42 -0400 |
commit | 8418263e3547ed3816475e4c55a77004f0426ee6 (patch) | |
tree | 97c548b16e6753e1911870d824a07b7e726b6229 /kernel | |
parent | ccff9b1db693062b0a9c9070f4304deb47ef215c (diff) | |
parent | f81700bd831efcd12eb7f0e66b24b16c2ad00a32 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull third pile of VFS updates from Al Viro:
"Stuff from Jeff Layton, mostly. Sanitizing interplay between audit
and namei, removing a lot of insanity from audit_inode() mess and
getting things ready for his ESTALE patchset."
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
procfs: don't need a PATH_MAX allocation to hold a string representation of an int
vfs: embed struct filename inside of names_cache allocation if possible
audit: make audit_inode take struct filename
vfs: make path_openat take a struct filename pointer
vfs: turn do_path_lookup into wrapper around struct filename variant
audit: allow audit code to satisfy getname requests from its names_list
vfs: define struct filename and have getname() return it
vfs: unexport getname and putname symbols
acct: constify the name arg to acct_on
vfs: allocate page instead of names_cache buffer in mount_block_root
audit: overhaul __audit_inode_child to accomodate retrying
audit: optimize audit_compare_dname_path
audit: make audit_compare_dname_path use parent_len helper
audit: remove dirlen argument to audit_compare_dname_path
audit: set the name_len in audit_inode for parent lookups
audit: add a new "type" field to audit_names struct
audit: reverse arguments to audit_inode_child
audit: no need to walk list in audit_inode if name is NULL
audit: pass in dentry to audit_copy_inode wherever possible
audit: remove unnecessary NULL ptr checks from do_path_lookup
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/acct.c | 6 | ||||
-rw-r--r-- | kernel/audit.h | 7 | ||||
-rw-r--r-- | kernel/audit_watch.c | 3 | ||||
-rw-r--r-- | kernel/auditfilter.c | 65 | ||||
-rw-r--r-- | kernel/auditsc.c | 217 |
5 files changed, 199 insertions, 99 deletions
diff --git a/kernel/acct.c b/kernel/acct.c index 6cd7529c9e6a..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, | |||
193 | } | 193 | } |
194 | } | 194 | } |
195 | 195 | ||
196 | static int acct_on(char *name) | 196 | static int acct_on(struct filename *pathname) |
197 | { | 197 | { |
198 | struct file *file; | 198 | struct file *file; |
199 | struct vfsmount *mnt; | 199 | struct vfsmount *mnt; |
@@ -201,7 +201,7 @@ static int acct_on(char *name) | |||
201 | struct bsd_acct_struct *acct = NULL; | 201 | struct bsd_acct_struct *acct = NULL; |
202 | 202 | ||
203 | /* Difference from BSD - they don't do O_APPEND */ | 203 | /* Difference from BSD - they don't do O_APPEND */ |
204 | file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0); | 204 | file = file_open_name(pathname, O_WRONLY|O_APPEND|O_LARGEFILE, 0); |
205 | if (IS_ERR(file)) | 205 | if (IS_ERR(file)) |
206 | return PTR_ERR(file); | 206 | return PTR_ERR(file); |
207 | 207 | ||
@@ -260,7 +260,7 @@ SYSCALL_DEFINE1(acct, const char __user *, name) | |||
260 | return -EPERM; | 260 | return -EPERM; |
261 | 261 | ||
262 | if (name) { | 262 | if (name) { |
263 | char *tmp = getname(name); | 263 | struct filename *tmp = getname(name); |
264 | if (IS_ERR(tmp)) | 264 | if (IS_ERR(tmp)) |
265 | return (PTR_ERR(tmp)); | 265 | return (PTR_ERR(tmp)); |
266 | error = acct_on(tmp); | 266 | error = acct_on(tmp); |
diff --git a/kernel/audit.h b/kernel/audit.h index 9eb3d79482b6..d51cba868e1b 100644 --- a/kernel/audit.h +++ b/kernel/audit.h | |||
@@ -74,12 +74,15 @@ static inline int audit_hash_ino(u32 ino) | |||
74 | return (ino & (AUDIT_INODE_BUCKETS-1)); | 74 | return (ino & (AUDIT_INODE_BUCKETS-1)); |
75 | } | 75 | } |
76 | 76 | ||
77 | /* Indicates that audit should log the full pathname. */ | ||
78 | #define AUDIT_NAME_FULL -1 | ||
79 | |||
77 | extern int audit_match_class(int class, unsigned syscall); | 80 | extern int audit_match_class(int class, unsigned syscall); |
78 | extern int audit_comparator(const u32 left, const u32 op, const u32 right); | 81 | extern int audit_comparator(const u32 left, const u32 op, const u32 right); |
79 | extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); | 82 | extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); |
80 | extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); | 83 | extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); |
81 | extern int audit_compare_dname_path(const char *dname, const char *path, | 84 | extern int parent_len(const char *path); |
82 | int *dirlen); | 85 | extern int audit_compare_dname_path(const char *dname, const char *path, int plen); |
83 | extern struct sk_buff * audit_make_reply(int pid, int seq, int type, | 86 | extern struct sk_buff * audit_make_reply(int pid, int seq, int type, |
84 | int done, int multi, | 87 | int done, int multi, |
85 | const void *payload, int size); | 88 | const void *payload, int size); |
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 1c22ec3d87bc..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, | |||
265 | /* Run all of the watches on this parent looking for the one that | 265 | /* Run all of the watches on this parent looking for the one that |
266 | * matches the given dname */ | 266 | * matches the given dname */ |
267 | list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) { | 267 | list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) { |
268 | if (audit_compare_dname_path(dname, owatch->path, NULL)) | 268 | if (audit_compare_dname_path(dname, owatch->path, |
269 | AUDIT_NAME_FULL)) | ||
269 | continue; | 270 | continue; |
270 | 271 | ||
271 | /* If the update involves invalidating rules, do the inode-based | 272 | /* If the update involves invalidating rules, do the inode-based |
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index c4bcdbaf4d4d..7f19f23d38a3 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
@@ -1298,41 +1298,60 @@ int audit_gid_comparator(kgid_t left, u32 op, kgid_t right) | |||
1298 | } | 1298 | } |
1299 | } | 1299 | } |
1300 | 1300 | ||
1301 | /* Compare given dentry name with last component in given path, | 1301 | /** |
1302 | * return of 0 indicates a match. */ | 1302 | * parent_len - find the length of the parent portion of a pathname |
1303 | int audit_compare_dname_path(const char *dname, const char *path, | 1303 | * @path: pathname of which to determine length |
1304 | int *dirlen) | 1304 | */ |
1305 | int parent_len(const char *path) | ||
1305 | { | 1306 | { |
1306 | int dlen, plen; | 1307 | int plen; |
1307 | const char *p; | 1308 | const char *p; |
1308 | 1309 | ||
1309 | if (!dname || !path) | ||
1310 | return 1; | ||
1311 | |||
1312 | dlen = strlen(dname); | ||
1313 | plen = strlen(path); | 1310 | plen = strlen(path); |
1314 | if (plen < dlen) | 1311 | |
1315 | return 1; | 1312 | if (plen == 0) |
1313 | return plen; | ||
1316 | 1314 | ||
1317 | /* disregard trailing slashes */ | 1315 | /* disregard trailing slashes */ |
1318 | p = path + plen - 1; | 1316 | p = path + plen - 1; |
1319 | while ((*p == '/') && (p > path)) | 1317 | while ((*p == '/') && (p > path)) |
1320 | p--; | 1318 | p--; |
1321 | 1319 | ||
1322 | /* find last path component */ | 1320 | /* walk backward until we find the next slash or hit beginning */ |
1323 | p = p - dlen + 1; | 1321 | while ((*p != '/') && (p > path)) |
1324 | if (p < path) | 1322 | p--; |
1323 | |||
1324 | /* did we find a slash? Then increment to include it in path */ | ||
1325 | if (*p == '/') | ||
1326 | p++; | ||
1327 | |||
1328 | return p - path; | ||
1329 | } | ||
1330 | |||
1331 | /** | ||
1332 | * audit_compare_dname_path - compare given dentry name with last component in | ||
1333 | * given path. Return of 0 indicates a match. | ||
1334 | * @dname: dentry name that we're comparing | ||
1335 | * @path: full pathname that we're comparing | ||
1336 | * @parentlen: length of the parent if known. Passing in AUDIT_NAME_FULL | ||
1337 | * here indicates that we must compute this value. | ||
1338 | */ | ||
1339 | int audit_compare_dname_path(const char *dname, const char *path, int parentlen) | ||
1340 | { | ||
1341 | int dlen, pathlen; | ||
1342 | const char *p; | ||
1343 | |||
1344 | dlen = strlen(dname); | ||
1345 | pathlen = strlen(path); | ||
1346 | if (pathlen < dlen) | ||
1325 | return 1; | 1347 | return 1; |
1326 | else if (p > path) { | ||
1327 | if (*--p != '/') | ||
1328 | return 1; | ||
1329 | else | ||
1330 | p++; | ||
1331 | } | ||
1332 | 1348 | ||
1333 | /* return length of path's directory component */ | 1349 | parentlen = parentlen == AUDIT_NAME_FULL ? parent_len(path) : parentlen; |
1334 | if (dirlen) | 1350 | if (pathlen - parentlen != dlen) |
1335 | *dirlen = p - path; | 1351 | return 1; |
1352 | |||
1353 | p = path + parentlen; | ||
1354 | |||
1336 | return strncmp(p, dname, dlen); | 1355 | return strncmp(p, dname, dlen); |
1337 | } | 1356 | } |
1338 | 1357 | ||
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index f4a7756f999c..2f186ed80c40 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -81,9 +81,6 @@ | |||
81 | * a name dynamically and also add those to the list anchored by names_list. */ | 81 | * a name dynamically and also add those to the list anchored by names_list. */ |
82 | #define AUDIT_NAMES 5 | 82 | #define AUDIT_NAMES 5 |
83 | 83 | ||
84 | /* Indicates that audit should log the full pathname. */ | ||
85 | #define AUDIT_NAME_FULL -1 | ||
86 | |||
87 | /* no execve audit message should be longer than this (userspace limits) */ | 84 | /* no execve audit message should be longer than this (userspace limits) */ |
88 | #define MAX_EXECVE_AUDIT_LEN 7500 | 85 | #define MAX_EXECVE_AUDIT_LEN 7500 |
89 | 86 | ||
@@ -106,27 +103,29 @@ struct audit_cap_data { | |||
106 | * we don't let putname() free it (instead we free all of the saved | 103 | * we don't let putname() free it (instead we free all of the saved |
107 | * pointers at syscall exit time). | 104 | * pointers at syscall exit time). |
108 | * | 105 | * |
109 | * Further, in fs/namei.c:path_lookup() we store the inode and device. */ | 106 | * Further, in fs/namei.c:path_lookup() we store the inode and device. |
107 | */ | ||
110 | struct audit_names { | 108 | struct audit_names { |
111 | struct list_head list; /* audit_context->names_list */ | 109 | struct list_head list; /* audit_context->names_list */ |
112 | const char *name; | 110 | struct filename *name; |
113 | unsigned long ino; | 111 | unsigned long ino; |
114 | dev_t dev; | 112 | dev_t dev; |
115 | umode_t mode; | 113 | umode_t mode; |
116 | kuid_t uid; | 114 | kuid_t uid; |
117 | kgid_t gid; | 115 | kgid_t gid; |
118 | dev_t rdev; | 116 | dev_t rdev; |
119 | u32 osid; | 117 | u32 osid; |
120 | struct audit_cap_data fcap; | 118 | struct audit_cap_data fcap; |
121 | unsigned int fcap_ver; | 119 | unsigned int fcap_ver; |
122 | int name_len; /* number of name's characters to log */ | 120 | int name_len; /* number of name's characters to log */ |
123 | bool name_put; /* call __putname() for this name */ | 121 | unsigned char type; /* record type */ |
122 | bool name_put; /* call __putname() for this name */ | ||
124 | /* | 123 | /* |
125 | * This was an allocated audit_names and not from the array of | 124 | * This was an allocated audit_names and not from the array of |
126 | * names allocated in the task audit context. Thus this name | 125 | * names allocated in the task audit context. Thus this name |
127 | * should be freed on syscall exit | 126 | * should be freed on syscall exit |
128 | */ | 127 | */ |
129 | bool should_free; | 128 | bool should_free; |
130 | }; | 129 | }; |
131 | 130 | ||
132 | struct audit_aux_data { | 131 | struct audit_aux_data { |
@@ -998,7 +997,7 @@ static inline void audit_free_names(struct audit_context *context) | |||
998 | context->ino_count); | 997 | context->ino_count); |
999 | list_for_each_entry(n, &context->names_list, list) { | 998 | list_for_each_entry(n, &context->names_list, list) { |
1000 | printk(KERN_ERR "names[%d] = %p = %s\n", i, | 999 | printk(KERN_ERR "names[%d] = %p = %s\n", i, |
1001 | n->name, n->name ?: "(null)"); | 1000 | n->name, n->name->name ?: "(null)"); |
1002 | } | 1001 | } |
1003 | dump_stack(); | 1002 | dump_stack(); |
1004 | return; | 1003 | return; |
@@ -1555,7 +1554,7 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n, | |||
1555 | case AUDIT_NAME_FULL: | 1554 | case AUDIT_NAME_FULL: |
1556 | /* log the full path */ | 1555 | /* log the full path */ |
1557 | audit_log_format(ab, " name="); | 1556 | audit_log_format(ab, " name="); |
1558 | audit_log_untrustedstring(ab, n->name); | 1557 | audit_log_untrustedstring(ab, n->name->name); |
1559 | break; | 1558 | break; |
1560 | case 0: | 1559 | case 0: |
1561 | /* name was specified as a relative path and the | 1560 | /* 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, | |||
1565 | default: | 1564 | default: |
1566 | /* log the name's directory component */ | 1565 | /* log the name's directory component */ |
1567 | audit_log_format(ab, " name="); | 1566 | audit_log_format(ab, " name="); |
1568 | audit_log_n_untrustedstring(ab, n->name, | 1567 | audit_log_n_untrustedstring(ab, n->name->name, |
1569 | n->name_len); | 1568 | n->name_len); |
1570 | } | 1569 | } |
1571 | } else | 1570 | } else |
@@ -1995,7 +1994,8 @@ retry: | |||
1995 | #endif | 1994 | #endif |
1996 | } | 1995 | } |
1997 | 1996 | ||
1998 | static struct audit_names *audit_alloc_name(struct audit_context *context) | 1997 | static struct audit_names *audit_alloc_name(struct audit_context *context, |
1998 | unsigned char type) | ||
1999 | { | 1999 | { |
2000 | struct audit_names *aname; | 2000 | struct audit_names *aname; |
2001 | 2001 | ||
@@ -2010,6 +2010,7 @@ static struct audit_names *audit_alloc_name(struct audit_context *context) | |||
2010 | } | 2010 | } |
2011 | 2011 | ||
2012 | aname->ino = (unsigned long)-1; | 2012 | aname->ino = (unsigned long)-1; |
2013 | aname->type = type; | ||
2013 | list_add_tail(&aname->list, &context->names_list); | 2014 | list_add_tail(&aname->list, &context->names_list); |
2014 | 2015 | ||
2015 | context->name_count++; | 2016 | context->name_count++; |
@@ -2020,13 +2021,36 @@ static struct audit_names *audit_alloc_name(struct audit_context *context) | |||
2020 | } | 2021 | } |
2021 | 2022 | ||
2022 | /** | 2023 | /** |
2024 | * audit_reusename - fill out filename with info from existing entry | ||
2025 | * @uptr: userland ptr to pathname | ||
2026 | * | ||
2027 | * Search the audit_names list for the current audit context. If there is an | ||
2028 | * existing entry with a matching "uptr" then return the filename | ||
2029 | * associated with that audit_name. If not, return NULL. | ||
2030 | */ | ||
2031 | struct filename * | ||
2032 | __audit_reusename(const __user char *uptr) | ||
2033 | { | ||
2034 | struct audit_context *context = current->audit_context; | ||
2035 | struct audit_names *n; | ||
2036 | |||
2037 | list_for_each_entry(n, &context->names_list, list) { | ||
2038 | if (!n->name) | ||
2039 | continue; | ||
2040 | if (n->name->uptr == uptr) | ||
2041 | return n->name; | ||
2042 | } | ||
2043 | return NULL; | ||
2044 | } | ||
2045 | |||
2046 | /** | ||
2023 | * audit_getname - add a name to the list | 2047 | * audit_getname - add a name to the list |
2024 | * @name: name to add | 2048 | * @name: name to add |
2025 | * | 2049 | * |
2026 | * Add a name to the list of audit names for this context. | 2050 | * Add a name to the list of audit names for this context. |
2027 | * Called from fs/namei.c:getname(). | 2051 | * Called from fs/namei.c:getname(). |
2028 | */ | 2052 | */ |
2029 | void __audit_getname(const char *name) | 2053 | void __audit_getname(struct filename *name) |
2030 | { | 2054 | { |
2031 | struct audit_context *context = current->audit_context; | 2055 | struct audit_context *context = current->audit_context; |
2032 | struct audit_names *n; | 2056 | struct audit_names *n; |
@@ -2040,13 +2064,19 @@ void __audit_getname(const char *name) | |||
2040 | return; | 2064 | return; |
2041 | } | 2065 | } |
2042 | 2066 | ||
2043 | n = audit_alloc_name(context); | 2067 | #if AUDIT_DEBUG |
2068 | /* The filename _must_ have a populated ->name */ | ||
2069 | BUG_ON(!name->name); | ||
2070 | #endif | ||
2071 | |||
2072 | n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); | ||
2044 | if (!n) | 2073 | if (!n) |
2045 | return; | 2074 | return; |
2046 | 2075 | ||
2047 | n->name = name; | 2076 | n->name = name; |
2048 | n->name_len = AUDIT_NAME_FULL; | 2077 | n->name_len = AUDIT_NAME_FULL; |
2049 | n->name_put = true; | 2078 | n->name_put = true; |
2079 | name->aname = n; | ||
2050 | 2080 | ||
2051 | if (!context->pwd.dentry) | 2081 | if (!context->pwd.dentry) |
2052 | get_fs_pwd(current->fs, &context->pwd); | 2082 | get_fs_pwd(current->fs, &context->pwd); |
@@ -2059,7 +2089,7 @@ void __audit_getname(const char *name) | |||
2059 | * then we delay the putname until syscall exit. | 2089 | * then we delay the putname until syscall exit. |
2060 | * Called from include/linux/fs.h:putname(). | 2090 | * Called from include/linux/fs.h:putname(). |
2061 | */ | 2091 | */ |
2062 | void audit_putname(const char *name) | 2092 | void audit_putname(struct filename *name) |
2063 | { | 2093 | { |
2064 | struct audit_context *context = current->audit_context; | 2094 | struct audit_context *context = current->audit_context; |
2065 | 2095 | ||
@@ -2074,7 +2104,7 @@ void audit_putname(const char *name) | |||
2074 | 2104 | ||
2075 | list_for_each_entry(n, &context->names_list, list) | 2105 | list_for_each_entry(n, &context->names_list, list) |
2076 | printk(KERN_ERR "name[%d] = %p = %s\n", i, | 2106 | printk(KERN_ERR "name[%d] = %p = %s\n", i, |
2077 | n->name, n->name ?: "(null)"); | 2107 | n->name, n->name->name ?: "(null)"); |
2078 | } | 2108 | } |
2079 | #endif | 2109 | #endif |
2080 | __putname(name); | 2110 | __putname(name); |
@@ -2088,8 +2118,8 @@ void audit_putname(const char *name) | |||
2088 | " put_count=%d\n", | 2118 | " put_count=%d\n", |
2089 | __FILE__, __LINE__, | 2119 | __FILE__, __LINE__, |
2090 | context->serial, context->major, | 2120 | context->serial, context->major, |
2091 | context->in_syscall, name, context->name_count, | 2121 | context->in_syscall, name->name, |
2092 | context->put_count); | 2122 | context->name_count, context->put_count); |
2093 | dump_stack(); | 2123 | dump_stack(); |
2094 | } | 2124 | } |
2095 | } | 2125 | } |
@@ -2132,13 +2162,13 @@ static void audit_copy_inode(struct audit_names *name, const struct dentry *dent | |||
2132 | } | 2162 | } |
2133 | 2163 | ||
2134 | /** | 2164 | /** |
2135 | * audit_inode - store the inode and device from a lookup | 2165 | * __audit_inode - store the inode and device from a lookup |
2136 | * @name: name being audited | 2166 | * @name: name being audited |
2137 | * @dentry: dentry being audited | 2167 | * @dentry: dentry being audited |
2138 | * | 2168 | * @parent: does this dentry represent the parent? |
2139 | * Called from fs/namei.c:path_lookup(). | ||
2140 | */ | 2169 | */ |
2141 | void __audit_inode(const char *name, const struct dentry *dentry) | 2170 | void __audit_inode(struct filename *name, const struct dentry *dentry, |
2171 | unsigned int parent) | ||
2142 | { | 2172 | { |
2143 | struct audit_context *context = current->audit_context; | 2173 | struct audit_context *context = current->audit_context; |
2144 | const struct inode *inode = dentry->d_inode; | 2174 | const struct inode *inode = dentry->d_inode; |
@@ -2147,24 +2177,69 @@ void __audit_inode(const char *name, const struct dentry *dentry) | |||
2147 | if (!context->in_syscall) | 2177 | if (!context->in_syscall) |
2148 | return; | 2178 | return; |
2149 | 2179 | ||
2180 | if (!name) | ||
2181 | goto out_alloc; | ||
2182 | |||
2183 | #if AUDIT_DEBUG | ||
2184 | /* The struct filename _must_ have a populated ->name */ | ||
2185 | BUG_ON(!name->name); | ||
2186 | #endif | ||
2187 | /* | ||
2188 | * If we have a pointer to an audit_names entry already, then we can | ||
2189 | * just use it directly if the type is correct. | ||
2190 | */ | ||
2191 | n = name->aname; | ||
2192 | if (n) { | ||
2193 | if (parent) { | ||
2194 | if (n->type == AUDIT_TYPE_PARENT || | ||
2195 | n->type == AUDIT_TYPE_UNKNOWN) | ||
2196 | goto out; | ||
2197 | } else { | ||
2198 | if (n->type != AUDIT_TYPE_PARENT) | ||
2199 | goto out; | ||
2200 | } | ||
2201 | } | ||
2202 | |||
2150 | list_for_each_entry_reverse(n, &context->names_list, list) { | 2203 | list_for_each_entry_reverse(n, &context->names_list, list) { |
2151 | if (n->name && (n->name == name)) | 2204 | /* does the name pointer match? */ |
2152 | goto out; | 2205 | if (!n->name || n->name->name != name->name) |
2206 | continue; | ||
2207 | |||
2208 | /* match the correct record type */ | ||
2209 | if (parent) { | ||
2210 | if (n->type == AUDIT_TYPE_PARENT || | ||
2211 | n->type == AUDIT_TYPE_UNKNOWN) | ||
2212 | goto out; | ||
2213 | } else { | ||
2214 | if (n->type != AUDIT_TYPE_PARENT) | ||
2215 | goto out; | ||
2216 | } | ||
2153 | } | 2217 | } |
2154 | 2218 | ||
2155 | /* unable to find the name from a previous getname() */ | 2219 | out_alloc: |
2156 | n = audit_alloc_name(context); | 2220 | /* unable to find the name from a previous getname(). Allocate a new |
2221 | * anonymous entry. | ||
2222 | */ | ||
2223 | n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); | ||
2157 | if (!n) | 2224 | if (!n) |
2158 | return; | 2225 | return; |
2159 | out: | 2226 | out: |
2227 | if (parent) { | ||
2228 | n->name_len = n->name ? parent_len(n->name->name) : AUDIT_NAME_FULL; | ||
2229 | n->type = AUDIT_TYPE_PARENT; | ||
2230 | } else { | ||
2231 | n->name_len = AUDIT_NAME_FULL; | ||
2232 | n->type = AUDIT_TYPE_NORMAL; | ||
2233 | } | ||
2160 | handle_path(dentry); | 2234 | handle_path(dentry); |
2161 | audit_copy_inode(n, dentry, inode); | 2235 | audit_copy_inode(n, dentry, inode); |
2162 | } | 2236 | } |
2163 | 2237 | ||
2164 | /** | 2238 | /** |
2165 | * audit_inode_child - collect inode info for created/removed objects | 2239 | * __audit_inode_child - collect inode info for created/removed objects |
2166 | * @dentry: dentry being audited | ||
2167 | * @parent: inode of dentry parent | 2240 | * @parent: inode of dentry parent |
2241 | * @dentry: dentry being audited | ||
2242 | * @type: AUDIT_TYPE_* value that we're looking for | ||
2168 | * | 2243 | * |
2169 | * For syscalls that create or remove filesystem objects, audit_inode | 2244 | * For syscalls that create or remove filesystem objects, audit_inode |
2170 | * can only collect information for the filesystem object's parent. | 2245 | * can only collect information for the filesystem object's parent. |
@@ -2174,15 +2249,14 @@ out: | |||
2174 | * must be hooked prior, in order to capture the target inode during | 2249 | * must be hooked prior, in order to capture the target inode during |
2175 | * unsuccessful attempts. | 2250 | * unsuccessful attempts. |
2176 | */ | 2251 | */ |
2177 | void __audit_inode_child(const struct dentry *dentry, | 2252 | void __audit_inode_child(const struct inode *parent, |
2178 | const struct inode *parent) | 2253 | const struct dentry *dentry, |
2254 | const unsigned char type) | ||
2179 | { | 2255 | { |
2180 | struct audit_context *context = current->audit_context; | 2256 | struct audit_context *context = current->audit_context; |
2181 | const char *found_parent = NULL, *found_child = NULL; | ||
2182 | const struct inode *inode = dentry->d_inode; | 2257 | const struct inode *inode = dentry->d_inode; |
2183 | const char *dname = dentry->d_name.name; | 2258 | const char *dname = dentry->d_name.name; |
2184 | struct audit_names *n; | 2259 | struct audit_names *n, *found_parent = NULL, *found_child = NULL; |
2185 | int dirlen = 0; | ||
2186 | 2260 | ||
2187 | if (!context->in_syscall) | 2261 | if (!context->in_syscall) |
2188 | return; | 2262 | return; |
@@ -2190,62 +2264,65 @@ void __audit_inode_child(const struct dentry *dentry, | |||
2190 | if (inode) | 2264 | if (inode) |
2191 | handle_one(inode); | 2265 | handle_one(inode); |
2192 | 2266 | ||
2193 | /* parent is more likely, look for it first */ | 2267 | /* look for a parent entry first */ |
2194 | list_for_each_entry(n, &context->names_list, list) { | 2268 | list_for_each_entry(n, &context->names_list, list) { |
2195 | if (!n->name) | 2269 | if (!n->name || n->type != AUDIT_TYPE_PARENT) |
2196 | continue; | 2270 | continue; |
2197 | 2271 | ||
2198 | if (n->ino == parent->i_ino && | 2272 | if (n->ino == parent->i_ino && |
2199 | !audit_compare_dname_path(dname, n->name, &dirlen)) { | 2273 | !audit_compare_dname_path(dname, n->name->name, n->name_len)) { |
2200 | n->name_len = dirlen; /* update parent data in place */ | 2274 | found_parent = n; |
2201 | found_parent = n->name; | 2275 | break; |
2202 | goto add_names; | ||
2203 | } | 2276 | } |
2204 | } | 2277 | } |
2205 | 2278 | ||
2206 | /* no matching parent, look for matching child */ | 2279 | /* is there a matching child entry? */ |
2207 | list_for_each_entry(n, &context->names_list, list) { | 2280 | list_for_each_entry(n, &context->names_list, list) { |
2208 | if (!n->name) | 2281 | /* can only match entries that have a name */ |
2282 | if (!n->name || n->type != type) | ||
2209 | continue; | 2283 | continue; |
2210 | 2284 | ||
2211 | /* strcmp() is the more likely scenario */ | 2285 | /* if we found a parent, make sure this one is a child of it */ |
2212 | if (!strcmp(dname, n->name) || | 2286 | if (found_parent && (n->name != found_parent->name)) |
2213 | !audit_compare_dname_path(dname, n->name, &dirlen)) { | 2287 | continue; |
2214 | if (inode) | 2288 | |
2215 | audit_copy_inode(n, NULL, inode); | 2289 | if (!strcmp(dname, n->name->name) || |
2216 | else | 2290 | !audit_compare_dname_path(dname, n->name->name, |
2217 | n->ino = (unsigned long)-1; | 2291 | found_parent ? |
2218 | found_child = n->name; | 2292 | found_parent->name_len : |
2219 | goto add_names; | 2293 | AUDIT_NAME_FULL)) { |
2294 | found_child = n; | ||
2295 | break; | ||
2220 | } | 2296 | } |
2221 | } | 2297 | } |
2222 | 2298 | ||
2223 | add_names: | ||
2224 | if (!found_parent) { | 2299 | if (!found_parent) { |
2225 | n = audit_alloc_name(context); | 2300 | /* create a new, "anonymous" parent record */ |
2301 | n = audit_alloc_name(context, AUDIT_TYPE_PARENT); | ||
2226 | if (!n) | 2302 | if (!n) |
2227 | return; | 2303 | return; |
2228 | audit_copy_inode(n, NULL, parent); | 2304 | audit_copy_inode(n, NULL, parent); |
2229 | } | 2305 | } |
2230 | 2306 | ||
2231 | if (!found_child) { | 2307 | if (!found_child) { |
2232 | n = audit_alloc_name(context); | 2308 | found_child = audit_alloc_name(context, type); |
2233 | if (!n) | 2309 | if (!found_child) |
2234 | return; | 2310 | return; |
2235 | 2311 | ||
2236 | /* Re-use the name belonging to the slot for a matching parent | 2312 | /* Re-use the name belonging to the slot for a matching parent |
2237 | * directory. All names for this context are relinquished in | 2313 | * directory. All names for this context are relinquished in |
2238 | * audit_free_names() */ | 2314 | * audit_free_names() */ |
2239 | if (found_parent) { | 2315 | if (found_parent) { |
2240 | n->name = found_parent; | 2316 | found_child->name = found_parent->name; |
2241 | n->name_len = AUDIT_NAME_FULL; | 2317 | found_child->name_len = AUDIT_NAME_FULL; |
2242 | /* don't call __putname() */ | 2318 | /* don't call __putname() */ |
2243 | n->name_put = false; | 2319 | found_child->name_put = false; |
2244 | } | 2320 | } |
2245 | |||
2246 | if (inode) | ||
2247 | audit_copy_inode(n, NULL, inode); | ||
2248 | } | 2321 | } |
2322 | if (inode) | ||
2323 | audit_copy_inode(found_child, dentry, inode); | ||
2324 | else | ||
2325 | found_child->ino = (unsigned long)-1; | ||
2249 | } | 2326 | } |
2250 | EXPORT_SYMBOL_GPL(__audit_inode_child); | 2327 | EXPORT_SYMBOL_GPL(__audit_inode_child); |
2251 | 2328 | ||