aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/auditsc.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2007-07-22 08:04:18 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2007-10-21 02:37:45 -0400
commit74c3cbe33bc077ac1159cadfea608b501e100344 (patch)
tree4c4023caa4e15d19780255fa5880df3d36eb292c /kernel/auditsc.c
parent455434d450a358ac5bcf3fc58f8913d13c544622 (diff)
[PATCH] audit: watching subtrees
New kind of audit rule predicates: "object is visible in given subtree". The part that can be sanely implemented, that is. Limitations: * if you have hardlink from outside of tree, you'd better watch it too (or just watch the object itself, obviously) * if you mount something under a watched tree, tell audit that new chunk should be added to watched subtrees * if you umount something in a watched tree and it's still mounted elsewhere, you will get matches on events happening there. New command tells audit to recalculate the trees, trimming such sources of false positives. Note that it's _not_ about path - if something mounted in several places (multiple mount, bindings, different namespaces, etc.), the match does _not_ depend on which one we are using for access. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'kernel/auditsc.c')
-rw-r--r--kernel/auditsc.c221
1 files changed, 220 insertions, 1 deletions
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 8a85c203be12..80ecab0942ef 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -65,6 +65,7 @@
65#include <linux/binfmts.h> 65#include <linux/binfmts.h>
66#include <linux/highmem.h> 66#include <linux/highmem.h>
67#include <linux/syscalls.h> 67#include <linux/syscalls.h>
68#include <linux/inotify.h>
68 69
69#include "audit.h" 70#include "audit.h"
70 71
@@ -179,6 +180,11 @@ struct audit_aux_data_pids {
179 int pid_count; 180 int pid_count;
180}; 181};
181 182
183struct audit_tree_refs {
184 struct audit_tree_refs *next;
185 struct audit_chunk *c[31];
186};
187
182/* The per-task audit context. */ 188/* The per-task audit context. */
183struct audit_context { 189struct audit_context {
184 int dummy; /* must be the first element */ 190 int dummy; /* must be the first element */
@@ -211,6 +217,9 @@ struct audit_context {
211 pid_t target_pid; 217 pid_t target_pid;
212 u32 target_sid; 218 u32 target_sid;
213 219
220 struct audit_tree_refs *trees, *first_trees;
221 int tree_count;
222
214#if AUDIT_DEBUG 223#if AUDIT_DEBUG
215 int put_count; 224 int put_count;
216 int ino_count; 225 int ino_count;
@@ -265,6 +274,117 @@ static int audit_match_perm(struct audit_context *ctx, int mask)
265 } 274 }
266} 275}
267 276
277/*
278 * We keep a linked list of fixed-sized (31 pointer) arrays of audit_chunk *;
279 * ->first_trees points to its beginning, ->trees - to the current end of data.
280 * ->tree_count is the number of free entries in array pointed to by ->trees.
281 * Original condition is (NULL, NULL, 0); as soon as it grows we never revert to NULL,
282 * "empty" becomes (p, p, 31) afterwards. We don't shrink the list (and seriously,
283 * it's going to remain 1-element for almost any setup) until we free context itself.
284 * References in it _are_ dropped - at the same time we free/drop aux stuff.
285 */
286
287#ifdef CONFIG_AUDIT_TREE
288static int put_tree_ref(struct audit_context *ctx, struct audit_chunk *chunk)
289{
290 struct audit_tree_refs *p = ctx->trees;
291 int left = ctx->tree_count;
292 if (likely(left)) {
293 p->c[--left] = chunk;
294 ctx->tree_count = left;
295 return 1;
296 }
297 if (!p)
298 return 0;
299 p = p->next;
300 if (p) {
301 p->c[30] = chunk;
302 ctx->trees = p;
303 ctx->tree_count = 30;
304 return 1;
305 }
306 return 0;
307}
308
309static int grow_tree_refs(struct audit_context *ctx)
310{
311 struct audit_tree_refs *p = ctx->trees;
312 ctx->trees = kzalloc(sizeof(struct audit_tree_refs), GFP_KERNEL);
313 if (!ctx->trees) {
314 ctx->trees = p;
315 return 0;
316 }
317 if (p)
318 p->next = ctx->trees;
319 else
320 ctx->first_trees = ctx->trees;
321 ctx->tree_count = 31;
322 return 1;
323}
324#endif
325
326static void unroll_tree_refs(struct audit_context *ctx,
327 struct audit_tree_refs *p, int count)
328{
329#ifdef CONFIG_AUDIT_TREE
330 struct audit_tree_refs *q;
331 int n;
332 if (!p) {
333 /* we started with empty chain */
334 p = ctx->first_trees;
335 count = 31;
336 /* if the very first allocation has failed, nothing to do */
337 if (!p)
338 return;
339 }
340 n = count;
341 for (q = p; q != ctx->trees; q = q->next, n = 31) {
342 while (n--) {
343 audit_put_chunk(q->c[n]);
344 q->c[n] = NULL;
345 }
346 }
347 while (n-- > ctx->tree_count) {
348 audit_put_chunk(q->c[n]);
349 q->c[n] = NULL;
350 }
351 ctx->trees = p;
352 ctx->tree_count = count;
353#endif
354}
355
356static void free_tree_refs(struct audit_context *ctx)
357{
358 struct audit_tree_refs *p, *q;
359 for (p = ctx->first_trees; p; p = q) {
360 q = p->next;
361 kfree(p);
362 }
363}
364
365static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree)
366{
367#ifdef CONFIG_AUDIT_TREE
368 struct audit_tree_refs *p;
369 int n;
370 if (!tree)
371 return 0;
372 /* full ones */
373 for (p = ctx->first_trees; p != ctx->trees; p = p->next) {
374 for (n = 0; n < 31; n++)
375 if (audit_tree_match(p->c[n], tree))
376 return 1;
377 }
378 /* partial */
379 if (p) {
380 for (n = ctx->tree_count; n < 31; n++)
381 if (audit_tree_match(p->c[n], tree))
382 return 1;
383 }
384#endif
385 return 0;
386}
387
268/* Determine if any context name data matches a rule's watch data */ 388/* Determine if any context name data matches a rule's watch data */
269/* Compare a task_struct with an audit_rule. Return 1 on match, 0 389/* Compare a task_struct with an audit_rule. Return 1 on match, 0
270 * otherwise. */ 390 * otherwise. */
@@ -379,6 +499,10 @@ static int audit_filter_rules(struct task_struct *tsk,
379 result = (name->dev == rule->watch->dev && 499 result = (name->dev == rule->watch->dev &&
380 name->ino == rule->watch->ino); 500 name->ino == rule->watch->ino);
381 break; 501 break;
502 case AUDIT_DIR:
503 if (ctx)
504 result = match_tree_refs(ctx, rule->tree);
505 break;
382 case AUDIT_LOGINUID: 506 case AUDIT_LOGINUID:
383 result = 0; 507 result = 0;
384 if (ctx) 508 if (ctx)
@@ -727,6 +851,8 @@ static inline void audit_free_context(struct audit_context *context)
727 context->name_count, count); 851 context->name_count, count);
728 } 852 }
729 audit_free_names(context); 853 audit_free_names(context);
854 unroll_tree_refs(context, NULL, 0);
855 free_tree_refs(context);
730 audit_free_aux(context); 856 audit_free_aux(context);
731 kfree(context->filterkey); 857 kfree(context->filterkey);
732 kfree(context); 858 kfree(context);
@@ -1270,6 +1396,7 @@ void audit_syscall_exit(int valid, long return_code)
1270 tsk->audit_context = new_context; 1396 tsk->audit_context = new_context;
1271 } else { 1397 } else {
1272 audit_free_names(context); 1398 audit_free_names(context);
1399 unroll_tree_refs(context, NULL, 0);
1273 audit_free_aux(context); 1400 audit_free_aux(context);
1274 context->aux = NULL; 1401 context->aux = NULL;
1275 context->aux_pids = NULL; 1402 context->aux_pids = NULL;
@@ -1281,6 +1408,95 @@ void audit_syscall_exit(int valid, long return_code)
1281 } 1408 }
1282} 1409}
1283 1410
1411static inline void handle_one(const struct inode *inode)
1412{
1413#ifdef CONFIG_AUDIT_TREE
1414 struct audit_context *context;
1415 struct audit_tree_refs *p;
1416 struct audit_chunk *chunk;
1417 int count;
1418 if (likely(list_empty(&inode->inotify_watches)))
1419 return;
1420 context = current->audit_context;
1421 p = context->trees;
1422 count = context->tree_count;
1423 rcu_read_lock();
1424 chunk = audit_tree_lookup(inode);
1425 rcu_read_unlock();
1426 if (!chunk)
1427 return;
1428 if (likely(put_tree_ref(context, chunk)))
1429 return;
1430 if (unlikely(!grow_tree_refs(context))) {
1431 printk(KERN_WARNING "out of memory, audit has lost a tree reference");
1432 audit_set_auditable(context);
1433 audit_put_chunk(chunk);
1434 unroll_tree_refs(context, p, count);
1435 return;
1436 }
1437 put_tree_ref(context, chunk);
1438#endif
1439}
1440
1441static void handle_path(const struct dentry *dentry)
1442{
1443#ifdef CONFIG_AUDIT_TREE
1444 struct audit_context *context;
1445 struct audit_tree_refs *p;
1446 const struct dentry *d, *parent;
1447 struct audit_chunk *drop;
1448 unsigned long seq;
1449 int count;
1450
1451 context = current->audit_context;
1452 p = context->trees;
1453 count = context->tree_count;
1454retry:
1455 drop = NULL;
1456 d = dentry;
1457 rcu_read_lock();
1458 seq = read_seqbegin(&rename_lock);
1459 for(;;) {
1460 struct inode *inode = d->d_inode;
1461 if (inode && unlikely(!list_empty(&inode->inotify_watches))) {
1462 struct audit_chunk *chunk;
1463 chunk = audit_tree_lookup(inode);
1464 if (chunk) {
1465 if (unlikely(!put_tree_ref(context, chunk))) {
1466 drop = chunk;
1467 break;
1468 }
1469 }
1470 }
1471 parent = d->d_parent;
1472 if (parent == d)
1473 break;
1474 d = parent;
1475 }
1476 if (unlikely(read_seqretry(&rename_lock, seq) || drop)) { /* in this order */
1477 rcu_read_unlock();
1478 if (!drop) {
1479 /* just a race with rename */
1480 unroll_tree_refs(context, p, count);
1481 goto retry;
1482 }
1483 audit_put_chunk(drop);
1484 if (grow_tree_refs(context)) {
1485 /* OK, got more space */
1486 unroll_tree_refs(context, p, count);
1487 goto retry;
1488 }
1489 /* too bad */
1490 printk(KERN_WARNING
1491 "out of memory, audit has lost a tree reference");
1492 unroll_tree_refs(context, p, count);
1493 audit_set_auditable(context);
1494 return;
1495 }
1496 rcu_read_unlock();
1497#endif
1498}
1499
1284/** 1500/**
1285 * audit_getname - add a name to the list 1501 * audit_getname - add a name to the list
1286 * @name: name to add 1502 * @name: name to add
@@ -1407,7 +1623,7 @@ void __audit_inode(const char *name, const struct dentry *dentry)
1407{ 1623{
1408 int idx; 1624 int idx;
1409 struct audit_context *context = current->audit_context; 1625 struct audit_context *context = current->audit_context;
1410 const struct inode *inode = inode = dentry->d_inode; 1626 const struct inode *inode = dentry->d_inode;
1411 1627
1412 if (!context->in_syscall) 1628 if (!context->in_syscall)
1413 return; 1629 return;
@@ -1427,6 +1643,7 @@ void __audit_inode(const char *name, const struct dentry *dentry)
1427 idx = context->name_count - 1; 1643 idx = context->name_count - 1;
1428 context->names[idx].name = NULL; 1644 context->names[idx].name = NULL;
1429 } 1645 }
1646 handle_path(dentry);
1430 audit_copy_inode(&context->names[idx], inode); 1647 audit_copy_inode(&context->names[idx], inode);
1431} 1648}
1432 1649
@@ -1456,6 +1673,8 @@ void __audit_inode_child(const char *dname, const struct dentry *dentry,
1456 if (!context->in_syscall) 1673 if (!context->in_syscall)
1457 return; 1674 return;
1458 1675
1676 if (inode)
1677 handle_one(inode);
1459 /* determine matching parent */ 1678 /* determine matching parent */
1460 if (!dname) 1679 if (!dname)
1461 goto add_names; 1680 goto add_names;