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