diff options
Diffstat (limited to 'kernel/auditsc.c')
| -rw-r--r-- | kernel/auditsc.c | 225 |
1 files changed, 223 insertions, 2 deletions
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index e19b5a33aede..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 | ||
| 183 | struct 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. */ |
| 183 | struct audit_context { | 189 | struct 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 | ||
| 288 | static 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 | |||
| 309 | static 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 | |||
| 326 | static 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 | |||
| 356 | static 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 | |||
| 365 | static 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 | ||
| 1411 | static 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 | |||
| 1441 | static 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; | ||
| 1454 | retry: | ||
| 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 |
| @@ -1403,10 +1619,11 @@ static void audit_copy_inode(struct audit_names *name, const struct inode *inode | |||
| 1403 | * | 1619 | * |
| 1404 | * Called from fs/namei.c:path_lookup(). | 1620 | * Called from fs/namei.c:path_lookup(). |
| 1405 | */ | 1621 | */ |
| 1406 | void __audit_inode(const char *name, const struct inode *inode) | 1622 | 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; |
| 1626 | const struct inode *inode = dentry->d_inode; | ||
| 1410 | 1627 | ||
| 1411 | if (!context->in_syscall) | 1628 | if (!context->in_syscall) |
| 1412 | return; | 1629 | return; |
| @@ -1426,6 +1643,7 @@ void __audit_inode(const char *name, const struct inode *inode) | |||
| 1426 | idx = context->name_count - 1; | 1643 | idx = context->name_count - 1; |
| 1427 | context->names[idx].name = NULL; | 1644 | context->names[idx].name = NULL; |
| 1428 | } | 1645 | } |
| 1646 | handle_path(dentry); | ||
| 1429 | audit_copy_inode(&context->names[idx], inode); | 1647 | audit_copy_inode(&context->names[idx], inode); |
| 1430 | } | 1648 | } |
| 1431 | 1649 | ||
| @@ -1443,17 +1661,20 @@ void __audit_inode(const char *name, const struct inode *inode) | |||
| 1443 | * must be hooked prior, in order to capture the target inode during | 1661 | * must be hooked prior, in order to capture the target inode during |
| 1444 | * unsuccessful attempts. | 1662 | * unsuccessful attempts. |
| 1445 | */ | 1663 | */ |
| 1446 | void __audit_inode_child(const char *dname, const struct inode *inode, | 1664 | void __audit_inode_child(const char *dname, const struct dentry *dentry, |
| 1447 | const struct inode *parent) | 1665 | const struct inode *parent) |
| 1448 | { | 1666 | { |
| 1449 | int idx; | 1667 | int idx; |
| 1450 | struct audit_context *context = current->audit_context; | 1668 | struct audit_context *context = current->audit_context; |
| 1451 | const char *found_parent = NULL, *found_child = NULL; | 1669 | const char *found_parent = NULL, *found_child = NULL; |
| 1670 | const struct inode *inode = dentry->d_inode; | ||
| 1452 | int dirlen = 0; | 1671 | int dirlen = 0; |
| 1453 | 1672 | ||
| 1454 | if (!context->in_syscall) | 1673 | if (!context->in_syscall) |
| 1455 | return; | 1674 | return; |
| 1456 | 1675 | ||
| 1676 | if (inode) | ||
| 1677 | handle_one(inode); | ||
| 1457 | /* determine matching parent */ | 1678 | /* determine matching parent */ |
| 1458 | if (!dname) | 1679 | if (!dname) |
| 1459 | goto add_names; | 1680 | goto add_names; |
