Blame view

kernel/audit_tree.c 21.8 KB
74c3cbe33   Al Viro   [PATCH] audit: wa...
1
  #include "audit.h"
28a3a7eb3   Eric Paris   audit: reimplemen...
2
  #include <linux/fsnotify_backend.h>
74c3cbe33   Al Viro   [PATCH] audit: wa...
3
4
  #include <linux/namei.h>
  #include <linux/mount.h>
916d75761   Al Viro   Fix rule eviction...
5
  #include <linux/kthread.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
6
  #include <linux/slab.h>
74c3cbe33   Al Viro   [PATCH] audit: wa...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  
  struct audit_tree;
  struct audit_chunk;
  
  struct audit_tree {
  	atomic_t count;
  	int goner;
  	struct audit_chunk *root;
  	struct list_head chunks;
  	struct list_head rules;
  	struct list_head list;
  	struct list_head same_root;
  	struct rcu_head head;
  	char pathname[];
  };
  
  struct audit_chunk {
  	struct list_head hash;
e61ce8673   Eric Paris   fsnotify: rename ...
25
  	struct fsnotify_mark mark;
74c3cbe33   Al Viro   [PATCH] audit: wa...
26
27
28
  	struct list_head trees;		/* with root here */
  	int dead;
  	int count;
8f7b0ba1c   Al Viro   Fix inotify watch...
29
  	atomic_long_t refs;
74c3cbe33   Al Viro   [PATCH] audit: wa...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  	struct rcu_head head;
  	struct node {
  		struct list_head list;
  		struct audit_tree *owner;
  		unsigned index;		/* index; upper bit indicates 'will prune' */
  	} owners[];
  };
  
  static LIST_HEAD(tree_list);
  static LIST_HEAD(prune_list);
  
  /*
   * One struct chunk is attached to each inode of interest.
   * We replace struct chunk on tagging/untagging.
   * Rules have pointer to struct audit_tree.
   * Rules have struct list_head rlist forming a list of rules over
   * the same tree.
   * References to struct chunk are collected at audit_inode{,_child}()
   * time and used in AUDIT_TREE rule matching.
   * These references are dropped at the same time we are calling
   * audit_free_names(), etc.
   *
   * Cyclic lists galore:
   * tree.chunks anchors chunk.owners[].list			hash_lock
   * tree.rules anchors rule.rlist				audit_filter_mutex
   * chunk.trees anchors tree.same_root				hash_lock
   * chunk.hash is a hash with middle bits of watch.inode as
   * a hash function.						RCU, hash_lock
   *
   * tree is refcounted; one reference for "some rules on rules_list refer to
   * it", one for each chunk with pointer to it.
   *
28a3a7eb3   Eric Paris   audit: reimplemen...
62
   * chunk is refcounted by embedded fsnotify_mark + .refs (non-zero refcount
8f7b0ba1c   Al Viro   Fix inotify watch...
63
   * of watch contributes 1 to .refs).
74c3cbe33   Al Viro   [PATCH] audit: wa...
64
65
66
67
68
69
   *
   * node.index allows to get from node.list to containing chunk.
   * MSB of that sucker is stolen to mark taggings that we might have to
   * revert - several operations have very unpleasant cleanup logics and
   * that makes a difference.  Some.
   */
28a3a7eb3   Eric Paris   audit: reimplemen...
70
  static struct fsnotify_group *audit_tree_group;
74c3cbe33   Al Viro   [PATCH] audit: wa...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  
  static struct audit_tree *alloc_tree(const char *s)
  {
  	struct audit_tree *tree;
  
  	tree = kmalloc(sizeof(struct audit_tree) + strlen(s) + 1, GFP_KERNEL);
  	if (tree) {
  		atomic_set(&tree->count, 1);
  		tree->goner = 0;
  		INIT_LIST_HEAD(&tree->chunks);
  		INIT_LIST_HEAD(&tree->rules);
  		INIT_LIST_HEAD(&tree->list);
  		INIT_LIST_HEAD(&tree->same_root);
  		tree->root = NULL;
  		strcpy(tree->pathname, s);
  	}
  	return tree;
  }
  
  static inline void get_tree(struct audit_tree *tree)
  {
  	atomic_inc(&tree->count);
  }
  
  static void __put_tree(struct rcu_head *rcu)
  {
  	struct audit_tree *tree = container_of(rcu, struct audit_tree, head);
  	kfree(tree);
  }
  
  static inline void put_tree(struct audit_tree *tree)
  {
  	if (atomic_dec_and_test(&tree->count))
  		call_rcu(&tree->head, __put_tree);
  }
  
  /* to avoid bringing the entire thing in audit.h */
  const char *audit_tree_path(struct audit_tree *tree)
  {
  	return tree->pathname;
  }
8f7b0ba1c   Al Viro   Fix inotify watch...
112
  static void free_chunk(struct audit_chunk *chunk)
74c3cbe33   Al Viro   [PATCH] audit: wa...
113
  {
74c3cbe33   Al Viro   [PATCH] audit: wa...
114
115
116
117
118
119
120
121
  	int i;
  
  	for (i = 0; i < chunk->count; i++) {
  		if (chunk->owners[i].owner)
  			put_tree(chunk->owners[i].owner);
  	}
  	kfree(chunk);
  }
8f7b0ba1c   Al Viro   Fix inotify watch...
122
  void audit_put_chunk(struct audit_chunk *chunk)
74c3cbe33   Al Viro   [PATCH] audit: wa...
123
  {
8f7b0ba1c   Al Viro   Fix inotify watch...
124
125
  	if (atomic_long_dec_and_test(&chunk->refs))
  		free_chunk(chunk);
74c3cbe33   Al Viro   [PATCH] audit: wa...
126
  }
8f7b0ba1c   Al Viro   Fix inotify watch...
127
  static void __put_chunk(struct rcu_head *rcu)
74c3cbe33   Al Viro   [PATCH] audit: wa...
128
  {
8f7b0ba1c   Al Viro   Fix inotify watch...
129
130
  	struct audit_chunk *chunk = container_of(rcu, struct audit_chunk, head);
  	audit_put_chunk(chunk);
74c3cbe33   Al Viro   [PATCH] audit: wa...
131
  }
e61ce8673   Eric Paris   fsnotify: rename ...
132
  static void audit_tree_destroy_watch(struct fsnotify_mark *entry)
28a3a7eb3   Eric Paris   audit: reimplemen...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  {
  	struct audit_chunk *chunk = container_of(entry, struct audit_chunk, mark);
  	call_rcu(&chunk->head, __put_chunk);
  }
  
  static struct audit_chunk *alloc_chunk(int count)
  {
  	struct audit_chunk *chunk;
  	size_t size;
  	int i;
  
  	size = offsetof(struct audit_chunk, owners) + count * sizeof(struct node);
  	chunk = kzalloc(size, GFP_KERNEL);
  	if (!chunk)
  		return NULL;
  
  	INIT_LIST_HEAD(&chunk->hash);
  	INIT_LIST_HEAD(&chunk->trees);
  	chunk->count = count;
  	atomic_long_set(&chunk->refs, 1);
  	for (i = 0; i < count; i++) {
  		INIT_LIST_HEAD(&chunk->owners[i].list);
  		chunk->owners[i].index = i;
  	}
  	fsnotify_init_mark(&chunk->mark, audit_tree_destroy_watch);
  	return chunk;
  }
74c3cbe33   Al Viro   [PATCH] audit: wa...
160
161
162
163
164
165
166
167
168
  enum {HASH_SIZE = 128};
  static struct list_head chunk_hash_heads[HASH_SIZE];
  static __cacheline_aligned_in_smp DEFINE_SPINLOCK(hash_lock);
  
  static inline struct list_head *chunk_hash(const struct inode *inode)
  {
  	unsigned long n = (unsigned long)inode / L1_CACHE_BYTES;
  	return chunk_hash_heads + n % HASH_SIZE;
  }
28a3a7eb3   Eric Paris   audit: reimplemen...
169
  /* hash_lock & entry->lock is held by caller */
74c3cbe33   Al Viro   [PATCH] audit: wa...
170
171
  static void insert_hash(struct audit_chunk *chunk)
  {
e61ce8673   Eric Paris   fsnotify: rename ...
172
  	struct fsnotify_mark *entry = &chunk->mark;
28a3a7eb3   Eric Paris   audit: reimplemen...
173
  	struct list_head *list;
2823e04de   Eric Paris   fsnotify: put ino...
174
  	if (!entry->i.inode)
28a3a7eb3   Eric Paris   audit: reimplemen...
175
  		return;
2823e04de   Eric Paris   fsnotify: put ino...
176
  	list = chunk_hash(entry->i.inode);
74c3cbe33   Al Viro   [PATCH] audit: wa...
177
178
179
180
181
182
183
  	list_add_rcu(&chunk->hash, list);
  }
  
  /* called under rcu_read_lock */
  struct audit_chunk *audit_tree_lookup(const struct inode *inode)
  {
  	struct list_head *list = chunk_hash(inode);
6793a051f   Paul E. McKenney   [PATCH] list_for_...
184
  	struct audit_chunk *p;
74c3cbe33   Al Viro   [PATCH] audit: wa...
185

6793a051f   Paul E. McKenney   [PATCH] list_for_...
186
  	list_for_each_entry_rcu(p, list, hash) {
28a3a7eb3   Eric Paris   audit: reimplemen...
187
  		/* mark.inode may have gone NULL, but who cares? */
2823e04de   Eric Paris   fsnotify: put ino...
188
  		if (p->mark.i.inode == inode) {
8f7b0ba1c   Al Viro   Fix inotify watch...
189
  			atomic_long_inc(&p->refs);
74c3cbe33   Al Viro   [PATCH] audit: wa...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
  			return p;
  		}
  	}
  	return NULL;
  }
  
  int audit_tree_match(struct audit_chunk *chunk, struct audit_tree *tree)
  {
  	int n;
  	for (n = 0; n < chunk->count; n++)
  		if (chunk->owners[n].owner == tree)
  			return 1;
  	return 0;
  }
  
  /* tagging and untagging inodes with trees */
8f7b0ba1c   Al Viro   Fix inotify watch...
206
207
208
209
210
211
212
213
  static struct audit_chunk *find_chunk(struct node *p)
  {
  	int index = p->index & ~(1U<<31);
  	p -= index;
  	return container_of(p, struct audit_chunk, owners[0]);
  }
  
  static void untag_chunk(struct node *p)
74c3cbe33   Al Viro   [PATCH] audit: wa...
214
  {
8f7b0ba1c   Al Viro   Fix inotify watch...
215
  	struct audit_chunk *chunk = find_chunk(p);
e61ce8673   Eric Paris   fsnotify: rename ...
216
  	struct fsnotify_mark *entry = &chunk->mark;
74c3cbe33   Al Viro   [PATCH] audit: wa...
217
218
219
220
  	struct audit_chunk *new;
  	struct audit_tree *owner;
  	int size = chunk->count - 1;
  	int i, j;
28a3a7eb3   Eric Paris   audit: reimplemen...
221
  	fsnotify_get_mark(entry);
8f7b0ba1c   Al Viro   Fix inotify watch...
222
223
  
  	spin_unlock(&hash_lock);
28a3a7eb3   Eric Paris   audit: reimplemen...
224
  	spin_lock(&entry->lock);
2823e04de   Eric Paris   fsnotify: put ino...
225
  	if (chunk->dead || !entry->i.inode) {
28a3a7eb3   Eric Paris   audit: reimplemen...
226
  		spin_unlock(&entry->lock);
8f7b0ba1c   Al Viro   Fix inotify watch...
227
  		goto out;
74c3cbe33   Al Viro   [PATCH] audit: wa...
228
229
230
231
232
233
234
235
236
237
238
239
240
  	}
  
  	owner = p->owner;
  
  	if (!size) {
  		chunk->dead = 1;
  		spin_lock(&hash_lock);
  		list_del_init(&chunk->trees);
  		if (owner->root == chunk)
  			owner->root = NULL;
  		list_del_init(&p->list);
  		list_del_rcu(&chunk->hash);
  		spin_unlock(&hash_lock);
28a3a7eb3   Eric Paris   audit: reimplemen...
241
  		spin_unlock(&entry->lock);
d07754412   Eric Paris   fsnotify: rename ...
242
  		fsnotify_destroy_mark(entry);
28a3a7eb3   Eric Paris   audit: reimplemen...
243
  		fsnotify_put_mark(entry);
8f7b0ba1c   Al Viro   Fix inotify watch...
244
  		goto out;
74c3cbe33   Al Viro   [PATCH] audit: wa...
245
246
247
248
249
  	}
  
  	new = alloc_chunk(size);
  	if (!new)
  		goto Fallback;
28a3a7eb3   Eric Paris   audit: reimplemen...
250
  	fsnotify_duplicate_mark(&new->mark, entry);
5444e2981   Eric Paris   fsnotify: split g...
251
  	if (fsnotify_add_mark(&new->mark, new->mark.group, new->mark.i.inode, NULL, 1)) {
74c3cbe33   Al Viro   [PATCH] audit: wa...
252
253
254
255
256
257
258
259
260
261
262
  		free_chunk(new);
  		goto Fallback;
  	}
  
  	chunk->dead = 1;
  	spin_lock(&hash_lock);
  	list_replace_init(&chunk->trees, &new->trees);
  	if (owner->root == chunk) {
  		list_del_init(&owner->same_root);
  		owner->root = NULL;
  	}
6f5d51148   Al Viro   fix braindamage i...
263
  	for (i = j = 0; j <= size; i++, j++) {
74c3cbe33   Al Viro   [PATCH] audit: wa...
264
265
266
267
268
269
270
271
272
273
274
275
  		struct audit_tree *s;
  		if (&chunk->owners[j] == p) {
  			list_del_init(&p->list);
  			i--;
  			continue;
  		}
  		s = chunk->owners[j].owner;
  		new->owners[i].owner = s;
  		new->owners[i].index = chunk->owners[j].index - j + i;
  		if (!s) /* result of earlier fallback */
  			continue;
  		get_tree(s);
6f5d51148   Al Viro   fix braindamage i...
276
  		list_replace_init(&chunk->owners[j].list, &new->owners[i].list);
74c3cbe33   Al Viro   [PATCH] audit: wa...
277
278
279
280
281
282
  	}
  
  	list_replace_rcu(&chunk->hash, &new->hash);
  	list_for_each_entry(owner, &new->trees, same_root)
  		owner->root = new;
  	spin_unlock(&hash_lock);
28a3a7eb3   Eric Paris   audit: reimplemen...
283
  	spin_unlock(&entry->lock);
d07754412   Eric Paris   fsnotify: rename ...
284
  	fsnotify_destroy_mark(entry);
28a3a7eb3   Eric Paris   audit: reimplemen...
285
  	fsnotify_put_mark(entry);
8f7b0ba1c   Al Viro   Fix inotify watch...
286
  	goto out;
74c3cbe33   Al Viro   [PATCH] audit: wa...
287
288
289
290
291
292
293
294
295
296
297
298
  
  Fallback:
  	// do the best we can
  	spin_lock(&hash_lock);
  	if (owner->root == chunk) {
  		list_del_init(&owner->same_root);
  		owner->root = NULL;
  	}
  	list_del_init(&p->list);
  	p->owner = NULL;
  	put_tree(owner);
  	spin_unlock(&hash_lock);
28a3a7eb3   Eric Paris   audit: reimplemen...
299
  	spin_unlock(&entry->lock);
8f7b0ba1c   Al Viro   Fix inotify watch...
300
  out:
28a3a7eb3   Eric Paris   audit: reimplemen...
301
  	fsnotify_put_mark(entry);
8f7b0ba1c   Al Viro   Fix inotify watch...
302
  	spin_lock(&hash_lock);
74c3cbe33   Al Viro   [PATCH] audit: wa...
303
304
305
306
  }
  
  static int create_chunk(struct inode *inode, struct audit_tree *tree)
  {
e61ce8673   Eric Paris   fsnotify: rename ...
307
  	struct fsnotify_mark *entry;
74c3cbe33   Al Viro   [PATCH] audit: wa...
308
309
310
  	struct audit_chunk *chunk = alloc_chunk(1);
  	if (!chunk)
  		return -ENOMEM;
28a3a7eb3   Eric Paris   audit: reimplemen...
311
  	entry = &chunk->mark;
5444e2981   Eric Paris   fsnotify: split g...
312
  	if (fsnotify_add_mark(entry, audit_tree_group, inode, NULL, 0)) {
74c3cbe33   Al Viro   [PATCH] audit: wa...
313
314
315
  		free_chunk(chunk);
  		return -ENOSPC;
  	}
28a3a7eb3   Eric Paris   audit: reimplemen...
316
  	spin_lock(&entry->lock);
74c3cbe33   Al Viro   [PATCH] audit: wa...
317
318
319
320
  	spin_lock(&hash_lock);
  	if (tree->goner) {
  		spin_unlock(&hash_lock);
  		chunk->dead = 1;
28a3a7eb3   Eric Paris   audit: reimplemen...
321
  		spin_unlock(&entry->lock);
d07754412   Eric Paris   fsnotify: rename ...
322
  		fsnotify_destroy_mark(entry);
28a3a7eb3   Eric Paris   audit: reimplemen...
323
  		fsnotify_put_mark(entry);
74c3cbe33   Al Viro   [PATCH] audit: wa...
324
325
326
327
328
329
330
331
332
333
334
335
  		return 0;
  	}
  	chunk->owners[0].index = (1U << 31);
  	chunk->owners[0].owner = tree;
  	get_tree(tree);
  	list_add(&chunk->owners[0].list, &tree->chunks);
  	if (!tree->root) {
  		tree->root = chunk;
  		list_add(&tree->same_root, &chunk->trees);
  	}
  	insert_hash(chunk);
  	spin_unlock(&hash_lock);
28a3a7eb3   Eric Paris   audit: reimplemen...
336
  	spin_unlock(&entry->lock);
74c3cbe33   Al Viro   [PATCH] audit: wa...
337
338
339
340
341
342
  	return 0;
  }
  
  /* the first tagged inode becomes root of tree */
  static int tag_chunk(struct inode *inode, struct audit_tree *tree)
  {
e61ce8673   Eric Paris   fsnotify: rename ...
343
  	struct fsnotify_mark *old_entry, *chunk_entry;
74c3cbe33   Al Viro   [PATCH] audit: wa...
344
345
346
347
  	struct audit_tree *owner;
  	struct audit_chunk *chunk, *old;
  	struct node *p;
  	int n;
5444e2981   Eric Paris   fsnotify: split g...
348
  	old_entry = fsnotify_find_inode_mark(audit_tree_group, inode);
28a3a7eb3   Eric Paris   audit: reimplemen...
349
  	if (!old_entry)
74c3cbe33   Al Viro   [PATCH] audit: wa...
350
  		return create_chunk(inode, tree);
28a3a7eb3   Eric Paris   audit: reimplemen...
351
  	old = container_of(old_entry, struct audit_chunk, mark);
74c3cbe33   Al Viro   [PATCH] audit: wa...
352
353
354
355
356
357
  
  	/* are we already there? */
  	spin_lock(&hash_lock);
  	for (n = 0; n < old->count; n++) {
  		if (old->owners[n].owner == tree) {
  			spin_unlock(&hash_lock);
28a3a7eb3   Eric Paris   audit: reimplemen...
358
  			fsnotify_put_mark(old_entry);
74c3cbe33   Al Viro   [PATCH] audit: wa...
359
360
361
362
363
364
  			return 0;
  		}
  	}
  	spin_unlock(&hash_lock);
  
  	chunk = alloc_chunk(old->count + 1);
b4c30aad3   Al Viro   fix more leaks in...
365
  	if (!chunk) {
28a3a7eb3   Eric Paris   audit: reimplemen...
366
  		fsnotify_put_mark(old_entry);
74c3cbe33   Al Viro   [PATCH] audit: wa...
367
  		return -ENOMEM;
b4c30aad3   Al Viro   fix more leaks in...
368
  	}
74c3cbe33   Al Viro   [PATCH] audit: wa...
369

28a3a7eb3   Eric Paris   audit: reimplemen...
370
371
372
  	chunk_entry = &chunk->mark;
  
  	spin_lock(&old_entry->lock);
2823e04de   Eric Paris   fsnotify: put ino...
373
  	if (!old_entry->i.inode) {
28a3a7eb3   Eric Paris   audit: reimplemen...
374
375
376
  		/* old_entry is being shot, lets just lie */
  		spin_unlock(&old_entry->lock);
  		fsnotify_put_mark(old_entry);
74c3cbe33   Al Viro   [PATCH] audit: wa...
377
  		free_chunk(chunk);
28a3a7eb3   Eric Paris   audit: reimplemen...
378
379
380
381
  		return -ENOENT;
  	}
  
  	fsnotify_duplicate_mark(chunk_entry, old_entry);
5444e2981   Eric Paris   fsnotify: split g...
382
  	if (fsnotify_add_mark(chunk_entry, chunk_entry->group, chunk_entry->i.inode, NULL, 1)) {
28a3a7eb3   Eric Paris   audit: reimplemen...
383
384
385
  		spin_unlock(&old_entry->lock);
  		free_chunk(chunk);
  		fsnotify_put_mark(old_entry);
74c3cbe33   Al Viro   [PATCH] audit: wa...
386
387
  		return -ENOSPC;
  	}
28a3a7eb3   Eric Paris   audit: reimplemen...
388
389
390
  
  	/* even though we hold old_entry->lock, this is safe since chunk_entry->lock could NEVER have been grabbed before */
  	spin_lock(&chunk_entry->lock);
74c3cbe33   Al Viro   [PATCH] audit: wa...
391
  	spin_lock(&hash_lock);
28a3a7eb3   Eric Paris   audit: reimplemen...
392
393
  
  	/* we now hold old_entry->lock, chunk_entry->lock, and hash_lock */
74c3cbe33   Al Viro   [PATCH] audit: wa...
394
395
396
  	if (tree->goner) {
  		spin_unlock(&hash_lock);
  		chunk->dead = 1;
28a3a7eb3   Eric Paris   audit: reimplemen...
397
398
  		spin_unlock(&chunk_entry->lock);
  		spin_unlock(&old_entry->lock);
d07754412   Eric Paris   fsnotify: rename ...
399
  		fsnotify_destroy_mark(chunk_entry);
28a3a7eb3   Eric Paris   audit: reimplemen...
400
401
402
  
  		fsnotify_put_mark(chunk_entry);
  		fsnotify_put_mark(old_entry);
74c3cbe33   Al Viro   [PATCH] audit: wa...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
  		return 0;
  	}
  	list_replace_init(&old->trees, &chunk->trees);
  	for (n = 0, p = chunk->owners; n < old->count; n++, p++) {
  		struct audit_tree *s = old->owners[n].owner;
  		p->owner = s;
  		p->index = old->owners[n].index;
  		if (!s) /* result of fallback in untag */
  			continue;
  		get_tree(s);
  		list_replace_init(&old->owners[n].list, &p->list);
  	}
  	p->index = (chunk->count - 1) | (1U<<31);
  	p->owner = tree;
  	get_tree(tree);
  	list_add(&p->list, &tree->chunks);
  	list_replace_rcu(&old->hash, &chunk->hash);
  	list_for_each_entry(owner, &chunk->trees, same_root)
  		owner->root = chunk;
  	old->dead = 1;
  	if (!tree->root) {
  		tree->root = chunk;
  		list_add(&tree->same_root, &chunk->trees);
  	}
  	spin_unlock(&hash_lock);
28a3a7eb3   Eric Paris   audit: reimplemen...
428
429
  	spin_unlock(&chunk_entry->lock);
  	spin_unlock(&old_entry->lock);
d07754412   Eric Paris   fsnotify: rename ...
430
  	fsnotify_destroy_mark(old_entry);
28a3a7eb3   Eric Paris   audit: reimplemen...
431
432
  	fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */
  	fsnotify_put_mark(old_entry); /* and kill it */
74c3cbe33   Al Viro   [PATCH] audit: wa...
433
434
  	return 0;
  }
74c3cbe33   Al Viro   [PATCH] audit: wa...
435
436
437
438
439
440
441
442
443
444
445
446
447
  static void kill_rules(struct audit_tree *tree)
  {
  	struct audit_krule *rule, *next;
  	struct audit_entry *entry;
  	struct audit_buffer *ab;
  
  	list_for_each_entry_safe(rule, next, &tree->rules, rlist) {
  		entry = container_of(rule, struct audit_entry, rule);
  
  		list_del_init(&rule->rlist);
  		if (rule->tree) {
  			/* not a half-baked one */
  			ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
9d9609851   Eric Paris   Audit: clean up a...
448
449
450
  			audit_log_format(ab, "op=");
  			audit_log_string(ab, "remove rule");
  			audit_log_format(ab, " dir=");
74c3cbe33   Al Viro   [PATCH] audit: wa...
451
  			audit_log_untrustedstring(ab, rule->tree->pathname);
9d9609851   Eric Paris   Audit: clean up a...
452
  			audit_log_key(ab, rule->filterkey);
74c3cbe33   Al Viro   [PATCH] audit: wa...
453
454
455
456
  			audit_log_format(ab, " list=%d res=1", rule->listnr);
  			audit_log_end(ab);
  			rule->tree = NULL;
  			list_del_rcu(&entry->list);
e45aa212e   Al Viro   audit rules order...
457
  			list_del(&entry->rule.list);
74c3cbe33   Al Viro   [PATCH] audit: wa...
458
459
460
461
462
463
464
465
466
467
468
469
470
  			call_rcu(&entry->rcu, audit_free_rule_rcu);
  		}
  	}
  }
  
  /*
   * finish killing struct audit_tree
   */
  static void prune_one(struct audit_tree *victim)
  {
  	spin_lock(&hash_lock);
  	while (!list_empty(&victim->chunks)) {
  		struct node *p;
74c3cbe33   Al Viro   [PATCH] audit: wa...
471
472
  
  		p = list_entry(victim->chunks.next, struct node, list);
74c3cbe33   Al Viro   [PATCH] audit: wa...
473

8f7b0ba1c   Al Viro   Fix inotify watch...
474
  		untag_chunk(p);
74c3cbe33   Al Viro   [PATCH] audit: wa...
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
  	}
  	spin_unlock(&hash_lock);
  	put_tree(victim);
  }
  
  /* trim the uncommitted chunks from tree */
  
  static void trim_marked(struct audit_tree *tree)
  {
  	struct list_head *p, *q;
  	spin_lock(&hash_lock);
  	if (tree->goner) {
  		spin_unlock(&hash_lock);
  		return;
  	}
  	/* reorder */
  	for (p = tree->chunks.next; p != &tree->chunks; p = q) {
  		struct node *node = list_entry(p, struct node, list);
  		q = p->next;
  		if (node->index & (1U<<31)) {
  			list_del_init(p);
  			list_add(p, &tree->chunks);
  		}
  	}
  
  	while (!list_empty(&tree->chunks)) {
  		struct node *node;
74c3cbe33   Al Viro   [PATCH] audit: wa...
502
503
504
505
506
507
  
  		node = list_entry(tree->chunks.next, struct node, list);
  
  		/* have we run out of marked? */
  		if (!(node->index & (1U<<31)))
  			break;
8f7b0ba1c   Al Viro   Fix inotify watch...
508
  		untag_chunk(node);
74c3cbe33   Al Viro   [PATCH] audit: wa...
509
510
511
512
513
514
515
516
517
518
519
520
521
  	}
  	if (!tree->root && !tree->goner) {
  		tree->goner = 1;
  		spin_unlock(&hash_lock);
  		mutex_lock(&audit_filter_mutex);
  		kill_rules(tree);
  		list_del_init(&tree->list);
  		mutex_unlock(&audit_filter_mutex);
  		prune_one(tree);
  	} else {
  		spin_unlock(&hash_lock);
  	}
  }
916d75761   Al Viro   Fix rule eviction...
522
  static void audit_schedule_prune(void);
74c3cbe33   Al Viro   [PATCH] audit: wa...
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
  /* called with audit_filter_mutex */
  int audit_remove_tree_rule(struct audit_krule *rule)
  {
  	struct audit_tree *tree;
  	tree = rule->tree;
  	if (tree) {
  		spin_lock(&hash_lock);
  		list_del_init(&rule->rlist);
  		if (list_empty(&tree->rules) && !tree->goner) {
  			tree->root = NULL;
  			list_del_init(&tree->same_root);
  			tree->goner = 1;
  			list_move(&tree->list, &prune_list);
  			rule->tree = NULL;
  			spin_unlock(&hash_lock);
  			audit_schedule_prune();
  			return 1;
  		}
  		rule->tree = NULL;
  		spin_unlock(&hash_lock);
  		return 1;
  	}
  	return 0;
  }
1f707137b   Al Viro   new helper: itera...
547
548
549
550
  static int compare_root(struct vfsmount *mnt, void *arg)
  {
  	return mnt->mnt_root->d_inode == arg;
  }
74c3cbe33   Al Viro   [PATCH] audit: wa...
551
552
553
554
555
556
557
558
  void audit_trim_trees(void)
  {
  	struct list_head cursor;
  
  	mutex_lock(&audit_filter_mutex);
  	list_add(&cursor, &tree_list);
  	while (cursor.next != &tree_list) {
  		struct audit_tree *tree;
98bc993f9   Al Viro   [PATCH] get rid o...
559
  		struct path path;
74c3cbe33   Al Viro   [PATCH] audit: wa...
560
561
  		struct vfsmount *root_mnt;
  		struct node *node;
74c3cbe33   Al Viro   [PATCH] audit: wa...
562
563
564
565
566
567
568
  		int err;
  
  		tree = container_of(cursor.next, struct audit_tree, list);
  		get_tree(tree);
  		list_del(&cursor);
  		list_add(&cursor, &tree->list);
  		mutex_unlock(&audit_filter_mutex);
98bc993f9   Al Viro   [PATCH] get rid o...
569
  		err = kern_path(tree->pathname, 0, &path);
74c3cbe33   Al Viro   [PATCH] audit: wa...
570
571
  		if (err)
  			goto skip_it;
589ff870e   Al Viro   Switch collect_mo...
572
  		root_mnt = collect_mounts(&path);
98bc993f9   Al Viro   [PATCH] get rid o...
573
  		path_put(&path);
74c3cbe33   Al Viro   [PATCH] audit: wa...
574
575
  		if (!root_mnt)
  			goto skip_it;
74c3cbe33   Al Viro   [PATCH] audit: wa...
576
577
  		spin_lock(&hash_lock);
  		list_for_each_entry(node, &tree->chunks, list) {
28a3a7eb3   Eric Paris   audit: reimplemen...
578
579
  			struct audit_chunk *chunk = find_chunk(node);
  			/* this could be NULL if the watch is dieing else where... */
2823e04de   Eric Paris   fsnotify: put ino...
580
  			struct inode *inode = chunk->mark.i.inode;
74c3cbe33   Al Viro   [PATCH] audit: wa...
581
  			node->index |= 1U<<31;
1f707137b   Al Viro   new helper: itera...
582
583
  			if (iterate_mounts(compare_root, inode, root_mnt))
  				node->index &= ~(1U<<31);
74c3cbe33   Al Viro   [PATCH] audit: wa...
584
585
586
587
  		}
  		spin_unlock(&hash_lock);
  		trim_marked(tree);
  		put_tree(tree);
74c3cbe33   Al Viro   [PATCH] audit: wa...
588
589
590
591
592
593
594
  		drop_collected_mounts(root_mnt);
  skip_it:
  		mutex_lock(&audit_filter_mutex);
  	}
  	list_del(&cursor);
  	mutex_unlock(&audit_filter_mutex);
  }
74c3cbe33   Al Viro   [PATCH] audit: wa...
595
596
597
598
599
  int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op)
  {
  
  	if (pathname[0] != '/' ||
  	    rule->listnr != AUDIT_FILTER_EXIT ||
5af75d8d5   Al Viro   audit: validate c...
600
  	    op != Audit_equal ||
74c3cbe33   Al Viro   [PATCH] audit: wa...
601
602
603
604
605
606
607
608
609
610
611
612
  	    rule->inode_f || rule->watch || rule->tree)
  		return -EINVAL;
  	rule->tree = alloc_tree(pathname);
  	if (!rule->tree)
  		return -ENOMEM;
  	return 0;
  }
  
  void audit_put_tree(struct audit_tree *tree)
  {
  	put_tree(tree);
  }
1f707137b   Al Viro   new helper: itera...
613
614
615
616
  static int tag_mount(struct vfsmount *mnt, void *arg)
  {
  	return tag_chunk(mnt->mnt_root->d_inode, arg);
  }
74c3cbe33   Al Viro   [PATCH] audit: wa...
617
618
619
620
  /* called with audit_filter_mutex */
  int audit_add_tree_rule(struct audit_krule *rule)
  {
  	struct audit_tree *seed = rule->tree, *tree;
98bc993f9   Al Viro   [PATCH] get rid o...
621
  	struct path path;
1f707137b   Al Viro   new helper: itera...
622
  	struct vfsmount *mnt;
74c3cbe33   Al Viro   [PATCH] audit: wa...
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
  	int err;
  
  	list_for_each_entry(tree, &tree_list, list) {
  		if (!strcmp(seed->pathname, tree->pathname)) {
  			put_tree(seed);
  			rule->tree = tree;
  			list_add(&rule->rlist, &tree->rules);
  			return 0;
  		}
  	}
  	tree = seed;
  	list_add(&tree->list, &tree_list);
  	list_add(&rule->rlist, &tree->rules);
  	/* do not set rule->tree yet */
  	mutex_unlock(&audit_filter_mutex);
98bc993f9   Al Viro   [PATCH] get rid o...
638
  	err = kern_path(tree->pathname, 0, &path);
74c3cbe33   Al Viro   [PATCH] audit: wa...
639
640
  	if (err)
  		goto Err;
589ff870e   Al Viro   Switch collect_mo...
641
  	mnt = collect_mounts(&path);
98bc993f9   Al Viro   [PATCH] get rid o...
642
  	path_put(&path);
74c3cbe33   Al Viro   [PATCH] audit: wa...
643
644
645
646
  	if (!mnt) {
  		err = -ENOMEM;
  		goto Err;
  	}
74c3cbe33   Al Viro   [PATCH] audit: wa...
647
648
  
  	get_tree(tree);
1f707137b   Al Viro   new helper: itera...
649
  	err = iterate_mounts(tag_mount, tree, mnt);
74c3cbe33   Al Viro   [PATCH] audit: wa...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
  	drop_collected_mounts(mnt);
  
  	if (!err) {
  		struct node *node;
  		spin_lock(&hash_lock);
  		list_for_each_entry(node, &tree->chunks, list)
  			node->index &= ~(1U<<31);
  		spin_unlock(&hash_lock);
  	} else {
  		trim_marked(tree);
  		goto Err;
  	}
  
  	mutex_lock(&audit_filter_mutex);
  	if (list_empty(&rule->rlist)) {
  		put_tree(tree);
  		return -ENOENT;
  	}
  	rule->tree = tree;
  	put_tree(tree);
  
  	return 0;
  Err:
  	mutex_lock(&audit_filter_mutex);
  	list_del_init(&tree->list);
  	list_del_init(&tree->rules);
  	put_tree(tree);
  	return err;
  }
  
  int audit_tag_tree(char *old, char *new)
  {
  	struct list_head cursor, barrier;
  	int failed = 0;
2096f759a   Al Viro   New helper: path_...
684
  	struct path path1, path2;
74c3cbe33   Al Viro   [PATCH] audit: wa...
685
  	struct vfsmount *tagged;
74c3cbe33   Al Viro   [PATCH] audit: wa...
686
  	int err;
2096f759a   Al Viro   New helper: path_...
687
  	err = kern_path(new, 0, &path2);
74c3cbe33   Al Viro   [PATCH] audit: wa...
688
689
  	if (err)
  		return err;
2096f759a   Al Viro   New helper: path_...
690
691
  	tagged = collect_mounts(&path2);
  	path_put(&path2);
74c3cbe33   Al Viro   [PATCH] audit: wa...
692
693
  	if (!tagged)
  		return -ENOMEM;
2096f759a   Al Viro   New helper: path_...
694
  	err = kern_path(old, 0, &path1);
74c3cbe33   Al Viro   [PATCH] audit: wa...
695
696
697
698
  	if (err) {
  		drop_collected_mounts(tagged);
  		return err;
  	}
74c3cbe33   Al Viro   [PATCH] audit: wa...
699

74c3cbe33   Al Viro   [PATCH] audit: wa...
700
701
702
703
704
705
  	mutex_lock(&audit_filter_mutex);
  	list_add(&barrier, &tree_list);
  	list_add(&cursor, &barrier);
  
  	while (cursor.next != &tree_list) {
  		struct audit_tree *tree;
2096f759a   Al Viro   New helper: path_...
706
  		int good_one = 0;
74c3cbe33   Al Viro   [PATCH] audit: wa...
707
708
709
710
711
712
  
  		tree = container_of(cursor.next, struct audit_tree, list);
  		get_tree(tree);
  		list_del(&cursor);
  		list_add(&cursor, &tree->list);
  		mutex_unlock(&audit_filter_mutex);
2096f759a   Al Viro   New helper: path_...
713
714
715
716
  		err = kern_path(tree->pathname, 0, &path2);
  		if (!err) {
  			good_one = path_is_under(&path1, &path2);
  			path_put(&path2);
74c3cbe33   Al Viro   [PATCH] audit: wa...
717
  		}
2096f759a   Al Viro   New helper: path_...
718
  		if (!good_one) {
74c3cbe33   Al Viro   [PATCH] audit: wa...
719
720
721
722
  			put_tree(tree);
  			mutex_lock(&audit_filter_mutex);
  			continue;
  		}
74c3cbe33   Al Viro   [PATCH] audit: wa...
723

1f707137b   Al Viro   new helper: itera...
724
  		failed = iterate_mounts(tag_mount, tree, tagged);
74c3cbe33   Al Viro   [PATCH] audit: wa...
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
  		if (failed) {
  			put_tree(tree);
  			mutex_lock(&audit_filter_mutex);
  			break;
  		}
  
  		mutex_lock(&audit_filter_mutex);
  		spin_lock(&hash_lock);
  		if (!tree->goner) {
  			list_del(&tree->list);
  			list_add(&tree->list, &tree_list);
  		}
  		spin_unlock(&hash_lock);
  		put_tree(tree);
  	}
  
  	while (barrier.prev != &tree_list) {
  		struct audit_tree *tree;
  
  		tree = container_of(barrier.prev, struct audit_tree, list);
  		get_tree(tree);
  		list_del(&tree->list);
  		list_add(&tree->list, &barrier);
  		mutex_unlock(&audit_filter_mutex);
  
  		if (!failed) {
  			struct node *node;
  			spin_lock(&hash_lock);
  			list_for_each_entry(node, &tree->chunks, list)
  				node->index &= ~(1U<<31);
  			spin_unlock(&hash_lock);
  		} else {
  			trim_marked(tree);
  		}
  
  		put_tree(tree);
  		mutex_lock(&audit_filter_mutex);
  	}
  	list_del(&barrier);
  	list_del(&cursor);
74c3cbe33   Al Viro   [PATCH] audit: wa...
765
  	mutex_unlock(&audit_filter_mutex);
2096f759a   Al Viro   New helper: path_...
766
  	path_put(&path1);
74c3cbe33   Al Viro   [PATCH] audit: wa...
767
768
769
770
771
772
  	drop_collected_mounts(tagged);
  	return failed;
  }
  
  /*
   * That gets run when evict_chunk() ends up needing to kill audit_tree.
916d75761   Al Viro   Fix rule eviction...
773
   * Runs from a separate thread.
74c3cbe33   Al Viro   [PATCH] audit: wa...
774
   */
916d75761   Al Viro   Fix rule eviction...
775
  static int prune_tree_thread(void *unused)
74c3cbe33   Al Viro   [PATCH] audit: wa...
776
  {
916d75761   Al Viro   Fix rule eviction...
777
  	mutex_lock(&audit_cmd_mutex);
74c3cbe33   Al Viro   [PATCH] audit: wa...
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
  	mutex_lock(&audit_filter_mutex);
  
  	while (!list_empty(&prune_list)) {
  		struct audit_tree *victim;
  
  		victim = list_entry(prune_list.next, struct audit_tree, list);
  		list_del_init(&victim->list);
  
  		mutex_unlock(&audit_filter_mutex);
  
  		prune_one(victim);
  
  		mutex_lock(&audit_filter_mutex);
  	}
  
  	mutex_unlock(&audit_filter_mutex);
916d75761   Al Viro   Fix rule eviction...
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
  	mutex_unlock(&audit_cmd_mutex);
  	return 0;
  }
  
  static void audit_schedule_prune(void)
  {
  	kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
  }
  
  /*
   * ... and that one is done if evict_chunk() decides to delay until the end
   * of syscall.  Runs synchronously.
   */
  void audit_kill_trees(struct list_head *list)
  {
  	mutex_lock(&audit_cmd_mutex);
  	mutex_lock(&audit_filter_mutex);
  
  	while (!list_empty(list)) {
  		struct audit_tree *victim;
  
  		victim = list_entry(list->next, struct audit_tree, list);
  		kill_rules(victim);
  		list_del_init(&victim->list);
  
  		mutex_unlock(&audit_filter_mutex);
  
  		prune_one(victim);
  
  		mutex_lock(&audit_filter_mutex);
  	}
  
  	mutex_unlock(&audit_filter_mutex);
  	mutex_unlock(&audit_cmd_mutex);
74c3cbe33   Al Viro   [PATCH] audit: wa...
828
829
830
831
832
  }
  
  /*
   *  Here comes the stuff asynchronous to auditctl operations
   */
74c3cbe33   Al Viro   [PATCH] audit: wa...
833
834
835
  static void evict_chunk(struct audit_chunk *chunk)
  {
  	struct audit_tree *owner;
916d75761   Al Viro   Fix rule eviction...
836
837
  	struct list_head *postponed = audit_killed_trees();
  	int need_prune = 0;
74c3cbe33   Al Viro   [PATCH] audit: wa...
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
  	int n;
  
  	if (chunk->dead)
  		return;
  
  	chunk->dead = 1;
  	mutex_lock(&audit_filter_mutex);
  	spin_lock(&hash_lock);
  	while (!list_empty(&chunk->trees)) {
  		owner = list_entry(chunk->trees.next,
  				   struct audit_tree, same_root);
  		owner->goner = 1;
  		owner->root = NULL;
  		list_del_init(&owner->same_root);
  		spin_unlock(&hash_lock);
916d75761   Al Viro   Fix rule eviction...
853
854
855
856
857
858
859
  		if (!postponed) {
  			kill_rules(owner);
  			list_move(&owner->list, &prune_list);
  			need_prune = 1;
  		} else {
  			list_move(&owner->list, postponed);
  		}
74c3cbe33   Al Viro   [PATCH] audit: wa...
860
861
862
863
864
865
  		spin_lock(&hash_lock);
  	}
  	list_del_rcu(&chunk->hash);
  	for (n = 0; n < chunk->count; n++)
  		list_del_init(&chunk->owners[n].list);
  	spin_unlock(&hash_lock);
916d75761   Al Viro   Fix rule eviction...
866
867
  	if (need_prune)
  		audit_schedule_prune();
74c3cbe33   Al Viro   [PATCH] audit: wa...
868
869
  	mutex_unlock(&audit_filter_mutex);
  }
3a9b16b40   Eric Paris   fsnotify: send fs...
870
  static int audit_tree_handle_event(struct fsnotify_group *group,
ce8f76fb7   Eric Paris   fsnotify: pass bo...
871
872
  				   struct fsnotify_mark *inode_mark,
  				   struct fsnotify_mark *vfsmonut_mark,
3a9b16b40   Eric Paris   fsnotify: send fs...
873
  				   struct fsnotify_event *event)
74c3cbe33   Al Viro   [PATCH] audit: wa...
874
  {
28a3a7eb3   Eric Paris   audit: reimplemen...
875
876
877
  	BUG();
  	return -EOPNOTSUPP;
  }
74c3cbe33   Al Viro   [PATCH] audit: wa...
878

e61ce8673   Eric Paris   fsnotify: rename ...
879
  static void audit_tree_freeing_mark(struct fsnotify_mark *entry, struct fsnotify_group *group)
28a3a7eb3   Eric Paris   audit: reimplemen...
880
881
882
883
884
  {
  	struct audit_chunk *chunk = container_of(entry, struct audit_chunk, mark);
  
  	evict_chunk(chunk);
  	fsnotify_put_mark(entry);
74c3cbe33   Al Viro   [PATCH] audit: wa...
885
  }
7b0a04fbf   Eric Paris   fsnotify: provide...
886
  static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode,
1968f5eed   Eric Paris   fanotify: use bot...
887
  				  struct fsnotify_mark *inode_mark,
ce8f76fb7   Eric Paris   fsnotify: pass bo...
888
  				  struct fsnotify_mark *vfsmount_mark,
3a9b16b40   Eric Paris   fsnotify: send fs...
889
  				  __u32 mask, void *data, int data_type)
74c3cbe33   Al Viro   [PATCH] audit: wa...
890
  {
2612abb51   Eric Paris   fsnotify: cleanup...
891
  	return false;
74c3cbe33   Al Viro   [PATCH] audit: wa...
892
  }
28a3a7eb3   Eric Paris   audit: reimplemen...
893
894
895
896
897
898
  static const struct fsnotify_ops audit_tree_ops = {
  	.handle_event = audit_tree_handle_event,
  	.should_send_event = audit_tree_send_event,
  	.free_group_priv = NULL,
  	.free_event_priv = NULL,
  	.freeing_mark = audit_tree_freeing_mark,
74c3cbe33   Al Viro   [PATCH] audit: wa...
899
900
901
902
903
  };
  
  static int __init audit_tree_init(void)
  {
  	int i;
0d2e2a1d0   Eric Paris   fsnotify: drop ma...
904
  	audit_tree_group = fsnotify_alloc_group(&audit_tree_ops);
28a3a7eb3   Eric Paris   audit: reimplemen...
905
906
  	if (IS_ERR(audit_tree_group))
  		audit_panic("cannot initialize fsnotify group for rectree watches");
74c3cbe33   Al Viro   [PATCH] audit: wa...
907
908
909
910
911
912
913
  
  	for (i = 0; i < HASH_SIZE; i++)
  		INIT_LIST_HEAD(&chunk_hash_heads[i]);
  
  	return 0;
  }
  __initcall(audit_tree_init);