diff options
Diffstat (limited to 'kernel/auditsc.c')
| -rw-r--r-- | kernel/auditsc.c | 217 |
1 files changed, 147 insertions, 70 deletions
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 | ||
