xref: /linux-6.15/kernel/audit_watch.c (revision c86b300b)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cfcad62cSEric Paris /* audit_watch.c -- watching inodes
3cfcad62cSEric Paris  *
4cfcad62cSEric Paris  * Copyright 2003-2009 Red Hat, Inc.
5cfcad62cSEric Paris  * Copyright 2005 Hewlett-Packard Development Company, L.P.
6cfcad62cSEric Paris  * Copyright 2005 IBM Corporation
7cfcad62cSEric Paris  */
8cfcad62cSEric Paris 
95efc2443SMateusz Guzik #include <linux/file.h>
10cfcad62cSEric Paris #include <linux/kernel.h>
11cfcad62cSEric Paris #include <linux/audit.h>
12cfcad62cSEric Paris #include <linux/kthread.h>
13cfcad62cSEric Paris #include <linux/mutex.h>
14cfcad62cSEric Paris #include <linux/fs.h>
15e9fd702aSEric Paris #include <linux/fsnotify_backend.h>
16cfcad62cSEric Paris #include <linux/namei.h>
17cfcad62cSEric Paris #include <linux/netlink.h>
18bd120dedSElena Reshetova #include <linux/refcount.h>
19cfcad62cSEric Paris #include <linux/sched.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
21cfcad62cSEric Paris #include <linux/security.h>
22cfcad62cSEric Paris #include "audit.h"
23cfcad62cSEric Paris 
24cfcad62cSEric Paris /*
25cfcad62cSEric Paris  * Reference counting:
26cfcad62cSEric Paris  *
27e9fd702aSEric Paris  * audit_parent: lifetime is from audit_init_parent() to receipt of an FS_IGNORED
28cfcad62cSEric Paris  * 	event.  Each audit_watch holds a reference to its associated parent.
29cfcad62cSEric Paris  *
30cfcad62cSEric Paris  * audit_watch: if added to lists, lifetime is from audit_init_watch() to
31cfcad62cSEric Paris  * 	audit_remove_watch().  Additionally, an audit_watch may exist
32cfcad62cSEric Paris  * 	temporarily to assist in searching existing filter data.  Each
33cfcad62cSEric Paris  * 	audit_krule holds a reference to its associated watch.
34cfcad62cSEric Paris  */
35cfcad62cSEric Paris 
36cfcad62cSEric Paris struct audit_watch {
37bd120dedSElena Reshetova 	refcount_t		count;	/* reference count */
38cfcad62cSEric Paris 	dev_t			dev;	/* associated superblock device */
39e08b061eSEric Paris 	char			*path;	/* insertion path */
40cfcad62cSEric Paris 	unsigned long		ino;	/* associated inode number */
41cfcad62cSEric Paris 	struct audit_parent	*parent; /* associated parent */
42cfcad62cSEric Paris 	struct list_head	wlist;	/* entry in parent->watches list */
43ae7b8f41SEric Paris 	struct list_head	rules;	/* anchor for krule->rlist */
44cfcad62cSEric Paris };
45cfcad62cSEric Paris 
46cfcad62cSEric Paris struct audit_parent {
47ae7b8f41SEric Paris 	struct list_head	watches; /* anchor for audit_watch->wlist */
48e61ce867SEric Paris 	struct fsnotify_mark mark; /* fsnotify mark on the inode */
49cfcad62cSEric Paris };
50cfcad62cSEric Paris 
51e9fd702aSEric Paris /* fsnotify handle. */
52b8800aa5SStephen Hemminger static struct fsnotify_group *audit_watch_group;
53cfcad62cSEric Paris 
54e9fd702aSEric Paris /* fsnotify events we care about. */
55e9fd702aSEric Paris #define AUDIT_FS_WATCH (FS_MOVE | FS_CREATE | FS_DELETE | FS_DELETE_SELF |\
567dbe6080SAmir Goldstein 			FS_MOVE_SELF | FS_UNMOUNT)
57cfcad62cSEric Paris 
audit_free_parent(struct audit_parent * parent)58ae7b8f41SEric Paris static void audit_free_parent(struct audit_parent *parent)
59ae7b8f41SEric Paris {
60ae7b8f41SEric Paris 	WARN_ON(!list_empty(&parent->watches));
61ae7b8f41SEric Paris 	kfree(parent);
62ae7b8f41SEric Paris }
63ae7b8f41SEric Paris 
audit_watch_free_mark(struct fsnotify_mark * entry)64e61ce867SEric Paris static void audit_watch_free_mark(struct fsnotify_mark *entry)
65cfcad62cSEric Paris {
66cfcad62cSEric Paris 	struct audit_parent *parent;
67cfcad62cSEric Paris 
68e9fd702aSEric Paris 	parent = container_of(entry, struct audit_parent, mark);
69ae7b8f41SEric Paris 	audit_free_parent(parent);
70cfcad62cSEric Paris }
71cfcad62cSEric Paris 
audit_get_parent(struct audit_parent * parent)72e9fd702aSEric Paris static void audit_get_parent(struct audit_parent *parent)
73e9fd702aSEric Paris {
74e9fd702aSEric Paris 	if (likely(parent))
75e9fd702aSEric Paris 		fsnotify_get_mark(&parent->mark);
76e9fd702aSEric Paris }
77e9fd702aSEric Paris 
audit_put_parent(struct audit_parent * parent)78e9fd702aSEric Paris static void audit_put_parent(struct audit_parent *parent)
79e9fd702aSEric Paris {
80e9fd702aSEric Paris 	if (likely(parent))
81e9fd702aSEric Paris 		fsnotify_put_mark(&parent->mark);
82e9fd702aSEric Paris }
83e9fd702aSEric Paris 
84e9fd702aSEric Paris /*
85e9fd702aSEric Paris  * Find and return the audit_parent on the given inode.  If found a reference
86e9fd702aSEric Paris  * is taken on this parent.
87e9fd702aSEric Paris  */
audit_find_parent(struct inode * inode)88e9fd702aSEric Paris static inline struct audit_parent *audit_find_parent(struct inode *inode)
89e9fd702aSEric Paris {
90e9fd702aSEric Paris 	struct audit_parent *parent = NULL;
91e61ce867SEric Paris 	struct fsnotify_mark *entry;
92e9fd702aSEric Paris 
93230d97d3SAmir Goldstein 	entry = fsnotify_find_inode_mark(inode, audit_watch_group);
94e9fd702aSEric Paris 	if (entry)
95e9fd702aSEric Paris 		parent = container_of(entry, struct audit_parent, mark);
96e9fd702aSEric Paris 
97e9fd702aSEric Paris 	return parent;
98e9fd702aSEric Paris }
99e9fd702aSEric Paris 
audit_get_watch(struct audit_watch * watch)100cfcad62cSEric Paris void audit_get_watch(struct audit_watch *watch)
101cfcad62cSEric Paris {
102bd120dedSElena Reshetova 	refcount_inc(&watch->count);
103cfcad62cSEric Paris }
104cfcad62cSEric Paris 
audit_put_watch(struct audit_watch * watch)105cfcad62cSEric Paris void audit_put_watch(struct audit_watch *watch)
106cfcad62cSEric Paris {
107bd120dedSElena Reshetova 	if (refcount_dec_and_test(&watch->count)) {
108cfcad62cSEric Paris 		WARN_ON(watch->parent);
109cfcad62cSEric Paris 		WARN_ON(!list_empty(&watch->rules));
110cfcad62cSEric Paris 		kfree(watch->path);
111cfcad62cSEric Paris 		kfree(watch);
112cfcad62cSEric Paris 	}
113cfcad62cSEric Paris }
114cfcad62cSEric Paris 
audit_remove_watch(struct audit_watch * watch)115b8800aa5SStephen Hemminger static void audit_remove_watch(struct audit_watch *watch)
116cfcad62cSEric Paris {
117cfcad62cSEric Paris 	list_del(&watch->wlist);
118e9fd702aSEric Paris 	audit_put_parent(watch->parent);
119cfcad62cSEric Paris 	watch->parent = NULL;
120cfcad62cSEric Paris 	audit_put_watch(watch); /* match initial get */
121cfcad62cSEric Paris }
122cfcad62cSEric Paris 
audit_watch_path(struct audit_watch * watch)123cfcad62cSEric Paris char *audit_watch_path(struct audit_watch *watch)
124cfcad62cSEric Paris {
125cfcad62cSEric Paris 	return watch->path;
126cfcad62cSEric Paris }
127cfcad62cSEric Paris 
audit_watch_compare(struct audit_watch * watch,unsigned long ino,dev_t dev)128ae7b8f41SEric Paris int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev)
129cfcad62cSEric Paris {
13084cb777eSRichard Guy Briggs 	return (watch->ino != AUDIT_INO_UNSET) &&
131ae7b8f41SEric Paris 		(watch->ino == ino) &&
132ae7b8f41SEric Paris 		(watch->dev == dev);
133cfcad62cSEric Paris }
134cfcad62cSEric Paris 
135cfcad62cSEric Paris /* Initialize a parent watch entry. */
audit_init_parent(const struct path * path)136bf2e1ae4SAl Viro static struct audit_parent *audit_init_parent(const struct path *path)
137cfcad62cSEric Paris {
1383b362157SDavid Howells 	struct inode *inode = d_backing_inode(path->dentry);
139cfcad62cSEric Paris 	struct audit_parent *parent;
140e9fd702aSEric Paris 	int ret;
141cfcad62cSEric Paris 
142cfcad62cSEric Paris 	parent = kzalloc(sizeof(*parent), GFP_KERNEL);
143cfcad62cSEric Paris 	if (unlikely(!parent))
144cfcad62cSEric Paris 		return ERR_PTR(-ENOMEM);
145cfcad62cSEric Paris 
146cfcad62cSEric Paris 	INIT_LIST_HEAD(&parent->watches);
147cfcad62cSEric Paris 
148054c636eSJan Kara 	fsnotify_init_mark(&parent->mark, audit_watch_group);
149e9fd702aSEric Paris 	parent->mark.mask = AUDIT_FS_WATCH;
150b249f5beSAmir Goldstein 	ret = fsnotify_add_inode_mark(&parent->mark, inode, 0);
151e9fd702aSEric Paris 	if (ret < 0) {
152ae7b8f41SEric Paris 		audit_free_parent(parent);
153e9fd702aSEric Paris 		return ERR_PTR(ret);
154cfcad62cSEric Paris 	}
155cfcad62cSEric Paris 
156cfcad62cSEric Paris 	return parent;
157cfcad62cSEric Paris }
158cfcad62cSEric Paris 
159cfcad62cSEric Paris /* Initialize a watch entry. */
audit_init_watch(char * path)160cfcad62cSEric Paris static struct audit_watch *audit_init_watch(char *path)
161cfcad62cSEric Paris {
162cfcad62cSEric Paris 	struct audit_watch *watch;
163cfcad62cSEric Paris 
164cfcad62cSEric Paris 	watch = kzalloc(sizeof(*watch), GFP_KERNEL);
165cfcad62cSEric Paris 	if (unlikely(!watch))
166cfcad62cSEric Paris 		return ERR_PTR(-ENOMEM);
167cfcad62cSEric Paris 
168cfcad62cSEric Paris 	INIT_LIST_HEAD(&watch->rules);
169bd120dedSElena Reshetova 	refcount_set(&watch->count, 1);
170cfcad62cSEric Paris 	watch->path = path;
17184cb777eSRichard Guy Briggs 	watch->dev = AUDIT_DEV_UNSET;
17284cb777eSRichard Guy Briggs 	watch->ino = AUDIT_INO_UNSET;
173cfcad62cSEric Paris 
174cfcad62cSEric Paris 	return watch;
175cfcad62cSEric Paris }
176cfcad62cSEric Paris 
177fd97646bSWei Yuan /* Translate a watch string to kernel representation. */
audit_to_watch(struct audit_krule * krule,char * path,int len,u32 op)178cfcad62cSEric Paris int audit_to_watch(struct audit_krule *krule, char *path, int len, u32 op)
179cfcad62cSEric Paris {
180cfcad62cSEric Paris 	struct audit_watch *watch;
181cfcad62cSEric Paris 
182e9fd702aSEric Paris 	if (!audit_watch_group)
183cfcad62cSEric Paris 		return -EOPNOTSUPP;
184cfcad62cSEric Paris 
185cfcad62cSEric Paris 	if (path[0] != '/' || path[len-1] == '/' ||
18667daf270SPaul Moore 	    (krule->listnr != AUDIT_FILTER_EXIT &&
18767daf270SPaul Moore 	     krule->listnr != AUDIT_FILTER_URING_EXIT) ||
188cfcad62cSEric Paris 	    op != Audit_equal ||
189cfcad62cSEric Paris 	    krule->inode_f || krule->watch || krule->tree)
190cfcad62cSEric Paris 		return -EINVAL;
191cfcad62cSEric Paris 
192cfcad62cSEric Paris 	watch = audit_init_watch(path);
193cfcad62cSEric Paris 	if (IS_ERR(watch))
194cfcad62cSEric Paris 		return PTR_ERR(watch);
195cfcad62cSEric Paris 
196cfcad62cSEric Paris 	krule->watch = watch;
197cfcad62cSEric Paris 
198cfcad62cSEric Paris 	return 0;
199cfcad62cSEric Paris }
200cfcad62cSEric Paris 
201cfcad62cSEric Paris /* Duplicate the given audit watch.  The new watch's rules list is initialized
202cfcad62cSEric Paris  * to an empty list and wlist is undefined. */
audit_dupe_watch(struct audit_watch * old)203cfcad62cSEric Paris static struct audit_watch *audit_dupe_watch(struct audit_watch *old)
204cfcad62cSEric Paris {
205cfcad62cSEric Paris 	char *path;
206cfcad62cSEric Paris 	struct audit_watch *new;
207cfcad62cSEric Paris 
208cfcad62cSEric Paris 	path = kstrdup(old->path, GFP_KERNEL);
209cfcad62cSEric Paris 	if (unlikely(!path))
210cfcad62cSEric Paris 		return ERR_PTR(-ENOMEM);
211cfcad62cSEric Paris 
212cfcad62cSEric Paris 	new = audit_init_watch(path);
213cfcad62cSEric Paris 	if (IS_ERR(new)) {
214cfcad62cSEric Paris 		kfree(path);
215cfcad62cSEric Paris 		goto out;
216cfcad62cSEric Paris 	}
217cfcad62cSEric Paris 
218cfcad62cSEric Paris 	new->dev = old->dev;
219cfcad62cSEric Paris 	new->ino = old->ino;
220e9fd702aSEric Paris 	audit_get_parent(old->parent);
221cfcad62cSEric Paris 	new->parent = old->parent;
222cfcad62cSEric Paris 
223cfcad62cSEric Paris out:
224cfcad62cSEric Paris 	return new;
225cfcad62cSEric Paris }
226cfcad62cSEric Paris 
audit_watch_log_rule_change(struct audit_krule * r,struct audit_watch * w,char * op)227cfcad62cSEric Paris static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watch *w, char *op)
228cfcad62cSEric Paris {
229cfcad62cSEric Paris 	struct audit_buffer *ab;
2304fa7f086SRichard Guy Briggs 
2314fa7f086SRichard Guy Briggs 	if (!audit_enabled)
2324fa7f086SRichard Guy Briggs 		return;
233626abcd1SRichard Guy Briggs 	ab = audit_log_start(audit_context(), GFP_NOFS, AUDIT_CONFIG_CHANGE);
2344fa7f086SRichard Guy Briggs 	if (!ab)
2350644ec0cSKees Cook 		return;
236a2c97da1SRichard Guy Briggs 	audit_log_session_info(ab);
237d0a3f18aSPaul Moore 	audit_log_format(ab, "op=%s path=", op);
238cfcad62cSEric Paris 	audit_log_untrustedstring(ab, w->path);
2399d960985SEric Paris 	audit_log_key(ab, r->filterkey);
240cfcad62cSEric Paris 	audit_log_format(ab, " list=%d res=1", r->listnr);
241cfcad62cSEric Paris 	audit_log_end(ab);
242cfcad62cSEric Paris }
243cfcad62cSEric Paris 
244cfcad62cSEric Paris /* Update inode info in audit rules based on filesystem event. */
audit_update_watch(struct audit_parent * parent,const struct qstr * dname,dev_t dev,unsigned long ino,unsigned invalidating)245cfcad62cSEric Paris static void audit_update_watch(struct audit_parent *parent,
2466921d4ebSAl Viro 			       const struct qstr *dname, dev_t dev,
247cfcad62cSEric Paris 			       unsigned long ino, unsigned invalidating)
248cfcad62cSEric Paris {
249cfcad62cSEric Paris 	struct audit_watch *owatch, *nwatch, *nextw;
250cfcad62cSEric Paris 	struct audit_krule *r, *nextr;
251cfcad62cSEric Paris 	struct audit_entry *oentry, *nentry;
252cfcad62cSEric Paris 
253cfcad62cSEric Paris 	mutex_lock(&audit_filter_mutex);
254ae7b8f41SEric Paris 	/* Run all of the watches on this parent looking for the one that
255ae7b8f41SEric Paris 	 * matches the given dname */
256cfcad62cSEric Paris 	list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
257795d673aSAl Viro 		if (audit_compare_dname_path(dname, owatch->path,
258e3d6b07bSJeff Layton 					     AUDIT_NAME_FULL))
259cfcad62cSEric Paris 			continue;
260cfcad62cSEric Paris 
261cfcad62cSEric Paris 		/* If the update involves invalidating rules, do the inode-based
262cfcad62cSEric Paris 		 * filtering now, so we don't omit records. */
263ae7b8f41SEric Paris 		if (invalidating && !audit_dummy_context())
264cdfb6b34SRichard Guy Briggs 			audit_filter_inodes(current, audit_context());
265cfcad62cSEric Paris 
266ae7b8f41SEric Paris 		/* updating ino will likely change which audit_hash_list we
267ae7b8f41SEric Paris 		 * are on so we need a new watch for the new list */
268cfcad62cSEric Paris 		nwatch = audit_dupe_watch(owatch);
269cfcad62cSEric Paris 		if (IS_ERR(nwatch)) {
270cfcad62cSEric Paris 			mutex_unlock(&audit_filter_mutex);
271cfcad62cSEric Paris 			audit_panic("error updating watch, skipping");
272cfcad62cSEric Paris 			return;
273cfcad62cSEric Paris 		}
274cfcad62cSEric Paris 		nwatch->dev = dev;
275cfcad62cSEric Paris 		nwatch->ino = ino;
276cfcad62cSEric Paris 
277cfcad62cSEric Paris 		list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) {
278cfcad62cSEric Paris 
279cfcad62cSEric Paris 			oentry = container_of(r, struct audit_entry, rule);
280cfcad62cSEric Paris 			list_del(&oentry->rule.rlist);
281cfcad62cSEric Paris 			list_del_rcu(&oentry->list);
282cfcad62cSEric Paris 
283ae7b8f41SEric Paris 			nentry = audit_dupe_rule(&oentry->rule);
284cfcad62cSEric Paris 			if (IS_ERR(nentry)) {
285cfcad62cSEric Paris 				list_del(&oentry->rule.list);
286cfcad62cSEric Paris 				audit_panic("error updating watch, removing");
287cfcad62cSEric Paris 			} else {
288cfcad62cSEric Paris 				int h = audit_hash_ino((u32)ino);
289ae7b8f41SEric Paris 
290ae7b8f41SEric Paris 				/*
291ae7b8f41SEric Paris 				 * nentry->rule.watch == oentry->rule.watch so
292ae7b8f41SEric Paris 				 * we must drop that reference and set it to our
293ae7b8f41SEric Paris 				 * new watch.
294ae7b8f41SEric Paris 				 */
295ae7b8f41SEric Paris 				audit_put_watch(nentry->rule.watch);
296ae7b8f41SEric Paris 				audit_get_watch(nwatch);
297ae7b8f41SEric Paris 				nentry->rule.watch = nwatch;
298cfcad62cSEric Paris 				list_add(&nentry->rule.rlist, &nwatch->rules);
299cfcad62cSEric Paris 				list_add_rcu(&nentry->list, &audit_inode_hash[h]);
300cfcad62cSEric Paris 				list_replace(&oentry->rule.list,
301cfcad62cSEric Paris 					     &nentry->rule.list);
302cfcad62cSEric Paris 			}
30334d99af5SRichard Guy Briggs 			if (oentry->rule.exe)
30434d99af5SRichard Guy Briggs 				audit_remove_mark(oentry->rule.exe);
305cfcad62cSEric Paris 
306cfcad62cSEric Paris 			call_rcu(&oentry->rcu, audit_free_rule_rcu);
307cfcad62cSEric Paris 		}
308cfcad62cSEric Paris 
309cfcad62cSEric Paris 		audit_remove_watch(owatch);
310cfcad62cSEric Paris 		goto add_watch_to_parent; /* event applies to a single watch */
311cfcad62cSEric Paris 	}
312cfcad62cSEric Paris 	mutex_unlock(&audit_filter_mutex);
313cfcad62cSEric Paris 	return;
314cfcad62cSEric Paris 
315cfcad62cSEric Paris add_watch_to_parent:
316cfcad62cSEric Paris 	list_add(&nwatch->wlist, &parent->watches);
317cfcad62cSEric Paris 	mutex_unlock(&audit_filter_mutex);
318cfcad62cSEric Paris 	return;
319cfcad62cSEric Paris }
320cfcad62cSEric Paris 
321cfcad62cSEric Paris /* Remove all watches & rules associated with a parent that is going away. */
audit_remove_parent_watches(struct audit_parent * parent)322cfcad62cSEric Paris static void audit_remove_parent_watches(struct audit_parent *parent)
323cfcad62cSEric Paris {
324cfcad62cSEric Paris 	struct audit_watch *w, *nextw;
325cfcad62cSEric Paris 	struct audit_krule *r, *nextr;
326cfcad62cSEric Paris 	struct audit_entry *e;
327cfcad62cSEric Paris 
328cfcad62cSEric Paris 	mutex_lock(&audit_filter_mutex);
329cfcad62cSEric Paris 	list_for_each_entry_safe(w, nextw, &parent->watches, wlist) {
330cfcad62cSEric Paris 		list_for_each_entry_safe(r, nextr, &w->rules, rlist) {
331cfcad62cSEric Paris 			e = container_of(r, struct audit_entry, rule);
332e7df61f4SBurn Alting 			audit_watch_log_rule_change(r, w, "remove_rule");
33334d99af5SRichard Guy Briggs 			if (e->rule.exe)
33434d99af5SRichard Guy Briggs 				audit_remove_mark(e->rule.exe);
335cfcad62cSEric Paris 			list_del(&r->rlist);
336cfcad62cSEric Paris 			list_del(&r->list);
337cfcad62cSEric Paris 			list_del_rcu(&e->list);
338cfcad62cSEric Paris 			call_rcu(&e->rcu, audit_free_rule_rcu);
339cfcad62cSEric Paris 		}
340cfcad62cSEric Paris 		audit_remove_watch(w);
341cfcad62cSEric Paris 	}
342cfcad62cSEric Paris 	mutex_unlock(&audit_filter_mutex);
343e9fd702aSEric Paris 
344e2a29943SLino Sanfilippo 	fsnotify_destroy_mark(&parent->mark, audit_watch_group);
345cfcad62cSEric Paris }
346cfcad62cSEric Paris 
347cfcad62cSEric Paris /* Get path information necessary for adding watches. */
audit_get_nd(struct audit_watch * watch,struct path * parent)34815a9155fSAl Viro static int audit_get_nd(struct audit_watch *watch, struct path *parent)
349cfcad62cSEric Paris {
350*c86b300bSChristian Brauner 	struct dentry *d;
351*c86b300bSChristian Brauner 
352*c86b300bSChristian Brauner 	d = kern_path_locked_negative(watch->path, parent);
35379714f72SAl Viro 	if (IS_ERR(d))
35415a9155fSAl Viro 		return PTR_ERR(d);
355*c86b300bSChristian Brauner 
356*c86b300bSChristian Brauner 	if (d_is_positive(d)) {
35715a9155fSAl Viro 		/* update watch filter fields */
358fc64005cSAl Viro 		watch->dev = d->d_sb->s_dev;
3593b362157SDavid Howells 		watch->ino = d_backing_inode(d)->i_ino;
360*c86b300bSChristian Brauner 	}
3611c3cb50bSNeilBrown 
36269924b89SAl Viro 	inode_unlock(d_backing_inode(parent->dentry));
36315a9155fSAl Viro 	dput(d);
364cfcad62cSEric Paris 	return 0;
365cfcad62cSEric Paris }
366cfcad62cSEric Paris 
367e9fd702aSEric Paris /* Associate the given rule with an existing parent.
368cfcad62cSEric Paris  * Caller must hold audit_filter_mutex. */
audit_add_to_parent(struct audit_krule * krule,struct audit_parent * parent)369cfcad62cSEric Paris static void audit_add_to_parent(struct audit_krule *krule,
370cfcad62cSEric Paris 				struct audit_parent *parent)
371cfcad62cSEric Paris {
372cfcad62cSEric Paris 	struct audit_watch *w, *watch = krule->watch;
373cfcad62cSEric Paris 	int watch_found = 0;
374cfcad62cSEric Paris 
375e9fd702aSEric Paris 	BUG_ON(!mutex_is_locked(&audit_filter_mutex));
376e9fd702aSEric Paris 
377cfcad62cSEric Paris 	list_for_each_entry(w, &parent->watches, wlist) {
378cfcad62cSEric Paris 		if (strcmp(watch->path, w->path))
379cfcad62cSEric Paris 			continue;
380cfcad62cSEric Paris 
381cfcad62cSEric Paris 		watch_found = 1;
382cfcad62cSEric Paris 
383f8259b26SRichard Guy Briggs 		/* put krule's ref to temporary watch */
384cfcad62cSEric Paris 		audit_put_watch(watch);
385cfcad62cSEric Paris 
386cfcad62cSEric Paris 		audit_get_watch(w);
387cfcad62cSEric Paris 		krule->watch = watch = w;
388aa7c043dSRichard Guy Briggs 
389aa7c043dSRichard Guy Briggs 		audit_put_parent(parent);
390cfcad62cSEric Paris 		break;
391cfcad62cSEric Paris 	}
392cfcad62cSEric Paris 
393cfcad62cSEric Paris 	if (!watch_found) {
394cfcad62cSEric Paris 		watch->parent = parent;
395cfcad62cSEric Paris 
396f8259b26SRichard Guy Briggs 		audit_get_watch(watch);
397cfcad62cSEric Paris 		list_add(&watch->wlist, &parent->watches);
398cfcad62cSEric Paris 	}
399cfcad62cSEric Paris 	list_add(&krule->rlist, &watch->rules);
400cfcad62cSEric Paris }
401cfcad62cSEric Paris 
402cfcad62cSEric Paris /* Find a matching watch entry, or add this one.
403cfcad62cSEric Paris  * Caller must hold audit_filter_mutex. */
audit_add_watch(struct audit_krule * krule,struct list_head ** list)404ae7b8f41SEric Paris int audit_add_watch(struct audit_krule *krule, struct list_head **list)
405cfcad62cSEric Paris {
406cfcad62cSEric Paris 	struct audit_watch *watch = krule->watch;
407cfcad62cSEric Paris 	struct audit_parent *parent;
40815a9155fSAl Viro 	struct path parent_path;
409ae7b8f41SEric Paris 	int h, ret = 0;
410cfcad62cSEric Paris 
411baa2a4fdSRonny Chevalier 	/*
412baa2a4fdSRonny Chevalier 	 * When we will be calling audit_add_to_parent, krule->watch might have
413baa2a4fdSRonny Chevalier 	 * been updated and watch might have been freed.
414baa2a4fdSRonny Chevalier 	 * So we need to keep a reference of watch.
415baa2a4fdSRonny Chevalier 	 */
416baa2a4fdSRonny Chevalier 	audit_get_watch(watch);
417baa2a4fdSRonny Chevalier 
41835fe4d0bSEric Paris 	mutex_unlock(&audit_filter_mutex);
41935fe4d0bSEric Paris 
42035fe4d0bSEric Paris 	/* Avoid calling path_lookup under audit_filter_mutex. */
42115a9155fSAl Viro 	ret = audit_get_nd(watch, &parent_path);
42215a9155fSAl Viro 
42335fe4d0bSEric Paris 	/* caller expects mutex locked */
42435fe4d0bSEric Paris 	mutex_lock(&audit_filter_mutex);
42535fe4d0bSEric Paris 
426*c86b300bSChristian Brauner 	if (ret) {
427baa2a4fdSRonny Chevalier 		audit_put_watch(watch);
42815a9155fSAl Viro 		return ret;
429baa2a4fdSRonny Chevalier 	}
430cfcad62cSEric Paris 
431e118e9c5SEric Paris 	/* either find an old parent or attach a new one */
4323b362157SDavid Howells 	parent = audit_find_parent(d_backing_inode(parent_path.dentry));
433e9fd702aSEric Paris 	if (!parent) {
43415a9155fSAl Viro 		parent = audit_init_parent(&parent_path);
435cfcad62cSEric Paris 		if (IS_ERR(parent)) {
43635fe4d0bSEric Paris 			ret = PTR_ERR(parent);
43735fe4d0bSEric Paris 			goto error;
438cfcad62cSEric Paris 		}
439e9fd702aSEric Paris 	}
440cfcad62cSEric Paris 
441cfcad62cSEric Paris 	audit_add_to_parent(krule, parent);
442cfcad62cSEric Paris 
443ae7b8f41SEric Paris 	h = audit_hash_ino((u32)watch->ino);
444ae7b8f41SEric Paris 	*list = &audit_inode_hash[h];
44535fe4d0bSEric Paris error:
44615a9155fSAl Viro 	path_put(&parent_path);
447baa2a4fdSRonny Chevalier 	audit_put_watch(watch);
448cfcad62cSEric Paris 	return ret;
449cfcad62cSEric Paris }
450cfcad62cSEric Paris 
audit_remove_watch_rule(struct audit_krule * krule)451a05fb6ccSEric Paris void audit_remove_watch_rule(struct audit_krule *krule)
452cfcad62cSEric Paris {
453cfcad62cSEric Paris 	struct audit_watch *watch = krule->watch;
454cfcad62cSEric Paris 	struct audit_parent *parent = watch->parent;
455cfcad62cSEric Paris 
456cfcad62cSEric Paris 	list_del(&krule->rlist);
457cfcad62cSEric Paris 
458cfcad62cSEric Paris 	if (list_empty(&watch->rules)) {
459d76036abSJan Kara 		/*
460d76036abSJan Kara 		 * audit_remove_watch() drops our reference to 'parent' which
461d76036abSJan Kara 		 * can get freed. Grab our own reference to be safe.
462d76036abSJan Kara 		 */
463e9fd702aSEric Paris 		audit_get_parent(parent);
464d76036abSJan Kara 		audit_remove_watch(watch);
465d76036abSJan Kara 		if (list_empty(&parent->watches))
466e2a29943SLino Sanfilippo 			fsnotify_destroy_mark(&parent->mark, audit_watch_group);
467a05fb6ccSEric Paris 		audit_put_parent(parent);
468cfcad62cSEric Paris 	}
469cfcad62cSEric Paris }
470cfcad62cSEric Paris 
471e9fd702aSEric Paris /* Update watch data in audit rules based on fsnotify events. */
audit_watch_handle_event(struct fsnotify_mark * inode_mark,u32 mask,struct inode * inode,struct inode * dir,const struct qstr * dname,u32 cookie)472b9a1b977SAmir Goldstein static int audit_watch_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
473b9a1b977SAmir Goldstein 				    struct inode *inode, struct inode *dir,
474950cc0d2SAmir Goldstein 				    const struct qstr *dname, u32 cookie)
475e9fd702aSEric Paris {
476e9fd702aSEric Paris 	struct audit_parent *parent;
477e9fd702aSEric Paris 
478ce8f76fbSEric Paris 	parent = container_of(inode_mark, struct audit_parent, mark);
479e9fd702aSEric Paris 
480dabe729dSAmir Goldstein 	if (WARN_ON_ONCE(inode_mark->group != audit_watch_group))
481b9a1b977SAmir Goldstein 		return 0;
482e9fd702aSEric Paris 
483e9fd702aSEric Paris 	if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
4846921d4ebSAl Viro 		audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0);
485e9fd702aSEric Paris 	else if (mask & (FS_DELETE|FS_MOVED_FROM))
4866921d4ebSAl Viro 		audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1);
487e9fd702aSEric Paris 	else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
488e9fd702aSEric Paris 		audit_remove_parent_watches(parent);
489e9fd702aSEric Paris 
490e9fd702aSEric Paris 	return 0;
491e9fd702aSEric Paris }
492e9fd702aSEric Paris 
493e9fd702aSEric Paris static const struct fsnotify_ops audit_watch_fsnotify_ops = {
494b9a1b977SAmir Goldstein 	.handle_inode_event =	audit_watch_handle_event,
495054c636eSJan Kara 	.free_mark =		audit_watch_free_mark,
496cfcad62cSEric Paris };
497cfcad62cSEric Paris 
audit_watch_init(void)498cfcad62cSEric Paris static int __init audit_watch_init(void)
499cfcad62cSEric Paris {
500867a448dSAmir Goldstein 	audit_watch_group = fsnotify_alloc_group(&audit_watch_fsnotify_ops, 0);
501e9fd702aSEric Paris 	if (IS_ERR(audit_watch_group)) {
502e9fd702aSEric Paris 		audit_watch_group = NULL;
503e9fd702aSEric Paris 		audit_panic("cannot create audit fsnotify group");
504e9fd702aSEric Paris 	}
505cfcad62cSEric Paris 	return 0;
506cfcad62cSEric Paris }
5071a3aedbcSEric Paris device_initcall(audit_watch_init);
50834d99af5SRichard Guy Briggs 
audit_dupe_exe(struct audit_krule * new,struct audit_krule * old)50934d99af5SRichard Guy Briggs int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old)
51034d99af5SRichard Guy Briggs {
51134d99af5SRichard Guy Briggs 	struct audit_fsnotify_mark *audit_mark;
51234d99af5SRichard Guy Briggs 	char *pathname;
51334d99af5SRichard Guy Briggs 
51434d99af5SRichard Guy Briggs 	pathname = kstrdup(audit_mark_path(old->exe), GFP_KERNEL);
51534d99af5SRichard Guy Briggs 	if (!pathname)
51634d99af5SRichard Guy Briggs 		return -ENOMEM;
51734d99af5SRichard Guy Briggs 
51834d99af5SRichard Guy Briggs 	audit_mark = audit_alloc_mark(new, pathname, strlen(pathname));
51934d99af5SRichard Guy Briggs 	if (IS_ERR(audit_mark)) {
52034d99af5SRichard Guy Briggs 		kfree(pathname);
52134d99af5SRichard Guy Briggs 		return PTR_ERR(audit_mark);
52234d99af5SRichard Guy Briggs 	}
52334d99af5SRichard Guy Briggs 	new->exe = audit_mark;
52434d99af5SRichard Guy Briggs 
52534d99af5SRichard Guy Briggs 	return 0;
52634d99af5SRichard Guy Briggs }
52734d99af5SRichard Guy Briggs 
audit_exe_compare(struct task_struct * tsk,struct audit_fsnotify_mark * mark)52834d99af5SRichard Guy Briggs int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark)
52934d99af5SRichard Guy Briggs {
53015ce414bSRichard Guy Briggs 	struct file *exe_file;
53115ce414bSRichard Guy Briggs 	unsigned long ino;
53215ce414bSRichard Guy Briggs 	dev_t dev;
53334d99af5SRichard Guy Briggs 
53447846d51SPaul Moore 	/* only do exe filtering if we are recording @current events/records */
53547846d51SPaul Moore 	if (tsk != current)
53647846d51SPaul Moore 		return 0;
53747846d51SPaul Moore 
538969d90ecSPaul Moore 	if (!current->mm)
53947846d51SPaul Moore 		return 0;
54047846d51SPaul Moore 	exe_file = get_mm_exe_file(current->mm);
5415efc2443SMateusz Guzik 	if (!exe_file)
5425efc2443SMateusz Guzik 		return 0;
54345063097SAl Viro 	ino = file_inode(exe_file)->i_ino;
54445063097SAl Viro 	dev = file_inode(exe_file)->i_sb->s_dev;
5455efc2443SMateusz Guzik 	fput(exe_file);
54647846d51SPaul Moore 
54734d99af5SRichard Guy Briggs 	return audit_mark_compare(mark, ino, dev);
54834d99af5SRichard Guy Briggs }
549