In order to accomodate retrying path-based syscalls, we need to add a new "type" argument to audit_inode_child. This will tell us whether we're looking for a child entry that represents a create or a delete. If we find a parent, don't automatically assume that we need to create a new entry. Instead, use the information we have to try to find an existing entry first. Update it if one is found and create a new one if not.
Unified · Split
| @@ -638,7 +638,7 @@ static int btrfs_may_delete(struct inode *dir,struct dentry *victim,int isdir) · 637 unmodified lines | ||
| 638 | 638 | return -ENOENT; |
| 639 | 639 | |
| 640 | 640 | BUG_ON(victim->d_parent->d_inode != dir); |
| 641 | − audit_inode_child(dir, victim); | |
| 641 | + audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); | |
| 642 | 642 | |
| 643 | 643 | error = inode_permission(dir, MAY_WRITE | MAY_EXEC); |
| 644 | 644 | if (error) |
| @@ -2176,7 +2176,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) · 2175 unmodified lines | ||
| 2176 | 2176 | return -ENOENT; |
| 2177 | 2177 | |
| 2178 | 2178 | BUG_ON(victim->d_parent->d_inode != dir); |
| 2179 | − audit_inode_child(dir, victim); | |
| 2179 | + audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); | |
| 2180 | 2180 | |
| 2181 | 2181 | error = inode_permission(dir, MAY_WRITE | MAY_EXEC); |
| 2182 | 2182 | if (error) |
| @@ -457,6 +457,8 @@ extern int audit_classify_arch(int arch); · 456 unmodified lines | ||
| 457 | 457 | #define AUDIT_TYPE_UNKNOWN 0 /* we don't know yet */ |
| 458 | 458 | #define AUDIT_TYPE_NORMAL 1 /* a "normal" audit record */ |
| 459 | 459 | #define AUDIT_TYPE_PARENT 2 /* a parent audit record */ |
| 460 | +#define AUDIT_TYPE_CHILD_DELETE 3 /* a child being deleted */ | |
| 461 | +#define AUDIT_TYPE_CHILD_CREATE 4 /* a child being created */ | |
| 460 | 462 | |
| 461 | 463 | #ifdef CONFIG_AUDITSYSCALL |
| 462 | 464 | /* These are defined in auditsc.c */ |
| @@ -472,7 +474,8 @@ extern void audit_putname(const char *name); · 9 unmodified lines | ||
| 472 | 474 | extern void __audit_inode(const char *name, const struct dentry *dentry, |
| 473 | 475 | unsigned int parent); |
| 474 | 476 | extern void __audit_inode_child(const struct inode *parent, |
| 475 | − const struct dentry *dentry); | |
| 477 | + const struct dentry *dentry, | |
| 478 | + const unsigned char type); | |
| 476 | 479 | extern void __audit_seccomp(unsigned long syscall, long signr, int code); |
| 477 | 480 | extern void __audit_ptrace(struct task_struct *t); |
| 478 | 481 | |
| @@ -513,9 +516,10 @@ static inline void audit_inode(const char *name, const struct dentry *dentry, · 34 unmodified lines | ||
| 513 | 516 | __audit_inode(name, dentry, parent); |
| 514 | 517 | } |
| 515 | 518 | static inline void audit_inode_child(const struct inode *parent, |
| 516 | − const struct dentry *dentry) { | |
| 519 | + const struct dentry *dentry, | |
| 520 | + const unsigned char type) { | |
| 517 | 521 | if (unlikely(!audit_dummy_context())) |
| 518 | − __audit_inode_child(parent, dentry); | |
| 522 | + __audit_inode_child(parent, dentry, type); | |
| 519 | 523 | } |
| 520 | 524 | void audit_core_dumps(long signr); |
| 521 | 525 | |
| @@ -667,13 +671,15 @@ static inline void __audit_inode(const char *name, const struct dentry *dentry, · 145 unmodified lines | ||
| 667 | 671 | unsigned int parent) |
| 668 | 672 | { } |
| 669 | 673 | static inline void __audit_inode_child(const struct inode *parent, |
| 670 | − const struct dentry *dentry) | |
| 674 | + const struct dentry *dentry, | |
| 675 | + const unsigned char type) | |
| 671 | 676 | { } |
| 672 | 677 | static inline void audit_inode(const char *name, const struct dentry *dentry, |
| 673 | 678 | unsigned int parent) |
| 674 | 679 | { } |
| 675 | 680 | static inline void audit_inode_child(const struct inode *parent, |
| 676 | − const struct dentry *dentry) | |
| 681 | + const struct dentry *dentry, | |
| 682 | + const unsigned char type) | |
| 677 | 683 | { } |
| 678 | 684 | static inline void audit_core_dumps(long signr) |
| 679 | 685 | { } |
| @@ -109,7 +109,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, · 108 unmodified lines | ||
| 109 | 109 | |
| 110 | 110 | if (source) |
| 111 | 111 | fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE, NULL, 0); |
| 112 | − audit_inode_child(new_dir, moved); | |
| 112 | + audit_inode_child(new_dir, moved, AUDIT_TYPE_CHILD_CREATE); | |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | 115 | /* |
| @@ -155,7 +155,7 @@ static inline void fsnotify_inoderemove(struct inode *inode) · 39 unmodified lines | ||
| 155 | 155 | */ |
| 156 | 156 | static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) |
| 157 | 157 | { |
| 158 | − audit_inode_child(inode, dentry); | |
| 158 | + audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE); | |
| 159 | 159 | |
| 160 | 160 | fsnotify(inode, FS_CREATE, dentry->d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); |
| 161 | 161 | } |
| @@ -168,7 +168,7 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) · 6 unmodified lines | ||
| 168 | 168 | static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct dentry *new_dentry) |
| 169 | 169 | { |
| 170 | 170 | fsnotify_link_count(inode); |
| 171 | − audit_inode_child(dir, new_dentry); | |
| 171 | + audit_inode_child(dir, new_dentry, AUDIT_TYPE_CHILD_CREATE); | |
| 172 | 172 | |
| 173 | 173 | fsnotify(dir, FS_CREATE, inode, FSNOTIFY_EVENT_INODE, new_dentry->d_name.name, 0); |
| 174 | 174 | } |
| @@ -181,7 +181,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) · 6 unmodified lines | ||
| 181 | 181 | __u32 mask = (FS_CREATE | FS_ISDIR); |
| 182 | 182 | struct inode *d_inode = dentry->d_inode; |
| 183 | 183 | |
| 184 | − audit_inode_child(inode, dentry); | |
| 184 | + audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE); | |
| 185 | 185 | |
| 186 | 186 | fsnotify(inode, mask, d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); |
| 187 | 187 | } |
| @@ -2189,6 +2189,7 @@ out: · 2188 unmodified lines | ||
| 2189 | 2189 | * __audit_inode_child - collect inode info for created/removed objects |
| 2190 | 2190 | * @parent: inode of dentry parent |
| 2191 | 2191 | * @dentry: dentry being audited |
| 2192 | + * @type: AUDIT_TYPE_* value that we're looking for | |
| 2192 | 2193 | * |
| 2193 | 2194 | * For syscalls that create or remove filesystem objects, audit_inode |
| 2194 | 2195 | * can only collect information for the filesystem object's parent. |
| @@ -2199,13 +2200,13 @@ out: · 4 unmodified lines | ||
| 2199 | 2200 | * unsuccessful attempts. |
| 2200 | 2201 | */ |
| 2201 | 2202 | void __audit_inode_child(const struct inode *parent, |
| 2202 | − const struct dentry *dentry) | |
| 2203 | + const struct dentry *dentry, | |
| 2204 | + const unsigned char type) | |
| 2203 | 2205 | { |
| 2204 | 2206 | struct audit_context *context = current->audit_context; |
| 2205 | − const char *found_parent = NULL, *found_child = NULL; | |
| 2206 | 2207 | const struct inode *inode = dentry->d_inode; |
| 2207 | 2208 | const char *dname = dentry->d_name.name; |
| 2208 | − struct audit_names *n; | |
| 2209 | + struct audit_names *n, *found_parent = NULL, *found_child = NULL; | |
| 2209 | 2210 | |
| 2210 | 2211 | if (!context->in_syscall) |
| 2211 | 2212 | return; |
| @@ -2213,63 +2214,65 @@ void __audit_inode_child(const struct inode *parent, · 1 unmodified line | ||
| 2213 | 2214 | if (inode) |
| 2214 | 2215 | handle_one(inode); |
| 2215 | 2216 | |
| 2216 | − /* parent is more likely, look for it first */ | |
| 2217 | + /* look for a parent entry first */ | |
| 2217 | 2218 | list_for_each_entry(n, &context->names_list, list) { |
| 2218 | − if (!n->name) | |
| 2219 | + if (!n->name || n->type != AUDIT_TYPE_PARENT) | |
| 2219 | 2220 | continue; |
| 2220 | 2221 | |
| 2221 | 2222 | if (n->ino == parent->i_ino && |
| 2222 | 2223 | !audit_compare_dname_path(dname, n->name, n->name_len)) { |
| 2223 | − found_parent = n->name; | |
| 2224 | − goto add_names; | |
| 2224 | + found_parent = n; | |
| 2225 | + break; | |
| 2225 | 2226 | } |
| 2226 | 2227 | } |
| 2227 | 2228 | |
| 2228 | − /* no matching parent, look for matching child */ | |
| 2229 | + /* is there a matching child entry? */ | |
| 2229 | 2230 | list_for_each_entry(n, &context->names_list, list) { |
| 2230 | − if (!n->name) | |
| 2231 | + /* can only match entries that have a name */ | |
| 2232 | + if (!n->name || n->type != type) | |
| 2233 | + continue; | |
| 2234 | + | |
| 2235 | + /* if we found a parent, make sure this one is a child of it */ | |
| 2236 | + if (found_parent && (n->name != found_parent->name)) | |
| 2231 | 2237 | continue; |
| 2232 | 2238 | |
| 2233 | − /* strcmp() is the more likely scenario */ | |
| 2234 | 2239 | if (!strcmp(dname, n->name) || |
| 2235 | 2240 | !audit_compare_dname_path(dname, n->name, |
| 2241 | + found_parent ? | |
| 2242 | + found_parent->name_len : | |
| 2236 | 2243 | AUDIT_NAME_FULL)) { |
| 2237 | − if (inode) | |
| 2238 | − audit_copy_inode(n, dentry, inode); | |
| 2239 | − else | |
| 2240 | − n->ino = (unsigned long)-1; | |
| 2241 | − n->type = AUDIT_TYPE_NORMAL; | |
| 2242 | − found_child = n->name; | |
| 2243 | − goto add_names; | |
| 2244 | + found_child = n; | |
| 2245 | + break; | |
| 2244 | 2246 | } |
| 2245 | 2247 | } |
| 2246 | 2248 | |
| 2247 | −add_names: | |
| 2248 | 2249 | if (!found_parent) { |
| 2249 | − n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); | |
| 2250 | + /* create a new, "anonymous" parent record */ | |
| 2251 | + n = audit_alloc_name(context, AUDIT_TYPE_PARENT); | |
| 2250 | 2252 | if (!n) |
| 2251 | 2253 | return; |
| 2252 | 2254 | audit_copy_inode(n, NULL, parent); |
| 2253 | 2255 | } |
| 2254 | 2256 | |
| 2255 | 2257 | if (!found_child) { |
| 2256 | − n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); | |
| 2257 | − if (!n) | |
| 2258 | + found_child = audit_alloc_name(context, type); | |
| 2259 | + if (!found_child) | |
| 2258 | 2260 | return; |
| 2259 | 2261 | |
| 2260 | 2262 | /* Re-use the name belonging to the slot for a matching parent |
| 2261 | 2263 | * directory. All names for this context are relinquished in |
| 2262 | 2264 | * audit_free_names() */ |
| 2263 | 2265 | if (found_parent) { |
| 2264 | − n->name = found_parent; | |
| 2265 | − n->name_len = AUDIT_NAME_FULL; | |
| 2266 | + found_child->name = found_parent->name; | |
| 2267 | + found_child->name_len = AUDIT_NAME_FULL; | |
| 2266 | 2268 | /* don't call __putname() */ |
| 2267 | − n->name_put = false; | |
| 2269 | + found_child->name_put = false; | |
| 2268 | 2270 | } |
| 2269 | − | |
| 2270 | − if (inode) | |
| 2271 | − audit_copy_inode(n, dentry, inode); | |
| 2272 | 2271 | } |
| 2272 | + if (inode) | |
| 2273 | + audit_copy_inode(found_child, dentry, inode); | |
| 2274 | + else | |
| 2275 | + found_child->ino = (unsigned long)-1; | |
| 2273 | 2276 | } |
| 2274 | 2277 | EXPORT_SYMBOL_GPL(__audit_inode_child); |
| 2275 | 2278 | |