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 | ||
