diff options
-rw-r--r-- | include/linux/audit.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/audit.h | 5 | ||||
-rw-r--r-- | kernel/audit.h | 4 | ||||
-rw-r--r-- | kernel/audit_tree.c | 2 | ||||
-rw-r--r-- | kernel/audit_watch.c | 31 | ||||
-rw-r--r-- | kernel/auditfilter.c | 53 | ||||
-rw-r--r-- | kernel/auditsc.c | 3 |
7 files changed, 97 insertions, 2 deletions
diff --git a/include/linux/audit.h b/include/linux/audit.h index 759feb0e9d13..b2abc996c25d 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h | |||
@@ -62,6 +62,7 @@ struct audit_krule { | |||
62 | struct audit_field *inode_f; /* quick access to an inode field */ | 62 | struct audit_field *inode_f; /* quick access to an inode field */ |
63 | struct audit_watch *watch; /* associated watch */ | 63 | struct audit_watch *watch; /* associated watch */ |
64 | struct audit_tree *tree; /* associated watched tree */ | 64 | struct audit_tree *tree; /* associated watched tree */ |
65 | struct audit_fsnotify_mark *exe; | ||
65 | struct list_head rlist; /* entry in audit_{watch,tree}.rules list */ | 66 | struct list_head rlist; /* entry in audit_{watch,tree}.rules list */ |
66 | struct list_head list; /* for AUDIT_LIST* purposes only */ | 67 | struct list_head list; /* for AUDIT_LIST* purposes only */ |
67 | u64 prio; | 68 | u64 prio; |
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index d3475e1f15ec..f6ff62c24aba 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h | |||
@@ -266,6 +266,7 @@ | |||
266 | #define AUDIT_OBJ_UID 109 | 266 | #define AUDIT_OBJ_UID 109 |
267 | #define AUDIT_OBJ_GID 110 | 267 | #define AUDIT_OBJ_GID 110 |
268 | #define AUDIT_FIELD_COMPARE 111 | 268 | #define AUDIT_FIELD_COMPARE 111 |
269 | #define AUDIT_EXE 112 | ||
269 | 270 | ||
270 | #define AUDIT_ARG0 200 | 271 | #define AUDIT_ARG0 200 |
271 | #define AUDIT_ARG1 (AUDIT_ARG0+1) | 272 | #define AUDIT_ARG1 (AUDIT_ARG0+1) |
@@ -324,8 +325,10 @@ enum { | |||
324 | 325 | ||
325 | #define AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT 0x00000001 | 326 | #define AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT 0x00000001 |
326 | #define AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME 0x00000002 | 327 | #define AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME 0x00000002 |
328 | #define AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH 0x00000004 | ||
327 | #define AUDIT_FEATURE_BITMAP_ALL (AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT | \ | 329 | #define AUDIT_FEATURE_BITMAP_ALL (AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT | \ |
328 | AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME) | 330 | AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME | \ |
331 | AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH) | ||
329 | 332 | ||
330 | /* deprecated: AUDIT_VERSION_* */ | 333 | /* deprecated: AUDIT_VERSION_* */ |
331 | #define AUDIT_VERSION_LATEST AUDIT_FEATURE_BITMAP_ALL | 334 | #define AUDIT_VERSION_LATEST AUDIT_FEATURE_BITMAP_ALL |
diff --git a/kernel/audit.h b/kernel/audit.h index 7102d538737b..24ec86145667 100644 --- a/kernel/audit.h +++ b/kernel/audit.h | |||
@@ -274,6 +274,8 @@ extern char *audit_mark_path(struct audit_fsnotify_mark *mark); | |||
274 | extern void audit_remove_mark(struct audit_fsnotify_mark *audit_mark); | 274 | extern void audit_remove_mark(struct audit_fsnotify_mark *audit_mark); |
275 | extern void audit_remove_mark_rule(struct audit_krule *krule); | 275 | extern void audit_remove_mark_rule(struct audit_krule *krule); |
276 | extern int audit_mark_compare(struct audit_fsnotify_mark *mark, unsigned long ino, dev_t dev); | 276 | extern int audit_mark_compare(struct audit_fsnotify_mark *mark, unsigned long ino, dev_t dev); |
277 | extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old); | ||
278 | extern int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark); | ||
277 | 279 | ||
278 | #else | 280 | #else |
279 | #define audit_put_watch(w) {} | 281 | #define audit_put_watch(w) {} |
@@ -289,6 +291,8 @@ extern int audit_mark_compare(struct audit_fsnotify_mark *mark, unsigned long in | |||
289 | #define audit_remove_mark(m) | 291 | #define audit_remove_mark(m) |
290 | #define audit_remove_mark_rule(k) | 292 | #define audit_remove_mark_rule(k) |
291 | #define audit_mark_compare(m, i, d) 0 | 293 | #define audit_mark_compare(m, i, d) 0 |
294 | #define audit_exe_compare(t, m) (-EINVAL) | ||
295 | #define audit_dupe_exe(n, o) (-EINVAL) | ||
292 | #endif /* CONFIG_AUDIT_WATCH */ | 296 | #endif /* CONFIG_AUDIT_WATCH */ |
293 | 297 | ||
294 | #ifdef CONFIG_AUDIT_TREE | 298 | #ifdef CONFIG_AUDIT_TREE |
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 2e0c97427b33..f41722506808 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c | |||
@@ -478,6 +478,8 @@ static void kill_rules(struct audit_tree *tree) | |||
478 | if (rule->tree) { | 478 | if (rule->tree) { |
479 | /* not a half-baked one */ | 479 | /* not a half-baked one */ |
480 | audit_tree_log_remove_rule(rule); | 480 | audit_tree_log_remove_rule(rule); |
481 | if (entry->rule.exe) | ||
482 | audit_remove_mark(entry->rule.exe); | ||
481 | rule->tree = NULL; | 483 | rule->tree = NULL; |
482 | list_del_rcu(&entry->list); | 484 | list_del_rcu(&entry->list); |
483 | list_del(&entry->rule.list); | 485 | list_del(&entry->rule.list); |
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 645c6884cee5..27ef8dcf7cd8 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c | |||
@@ -312,6 +312,8 @@ static void audit_update_watch(struct audit_parent *parent, | |||
312 | list_replace(&oentry->rule.list, | 312 | list_replace(&oentry->rule.list, |
313 | &nentry->rule.list); | 313 | &nentry->rule.list); |
314 | } | 314 | } |
315 | if (oentry->rule.exe) | ||
316 | audit_remove_mark(oentry->rule.exe); | ||
315 | 317 | ||
316 | audit_watch_log_rule_change(r, owatch, "updated_rules"); | 318 | audit_watch_log_rule_change(r, owatch, "updated_rules"); |
317 | 319 | ||
@@ -342,6 +344,8 @@ static void audit_remove_parent_watches(struct audit_parent *parent) | |||
342 | list_for_each_entry_safe(r, nextr, &w->rules, rlist) { | 344 | list_for_each_entry_safe(r, nextr, &w->rules, rlist) { |
343 | e = container_of(r, struct audit_entry, rule); | 345 | e = container_of(r, struct audit_entry, rule); |
344 | audit_watch_log_rule_change(r, w, "remove_rule"); | 346 | audit_watch_log_rule_change(r, w, "remove_rule"); |
347 | if (e->rule.exe) | ||
348 | audit_remove_mark(e->rule.exe); | ||
345 | list_del(&r->rlist); | 349 | list_del(&r->rlist); |
346 | list_del(&r->list); | 350 | list_del(&r->list); |
347 | list_del_rcu(&e->list); | 351 | list_del_rcu(&e->list); |
@@ -514,3 +518,30 @@ static int __init audit_watch_init(void) | |||
514 | return 0; | 518 | return 0; |
515 | } | 519 | } |
516 | device_initcall(audit_watch_init); | 520 | device_initcall(audit_watch_init); |
521 | |||
522 | int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old) | ||
523 | { | ||
524 | struct audit_fsnotify_mark *audit_mark; | ||
525 | char *pathname; | ||
526 | |||
527 | pathname = kstrdup(audit_mark_path(old->exe), GFP_KERNEL); | ||
528 | if (!pathname) | ||
529 | return -ENOMEM; | ||
530 | |||
531 | audit_mark = audit_alloc_mark(new, pathname, strlen(pathname)); | ||
532 | if (IS_ERR(audit_mark)) { | ||
533 | kfree(pathname); | ||
534 | return PTR_ERR(audit_mark); | ||
535 | } | ||
536 | new->exe = audit_mark; | ||
537 | |||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark) | ||
542 | { | ||
543 | unsigned long ino = tsk->mm->exe_file->f_inode->i_ino; | ||
544 | dev_t dev = tsk->mm->exe_file->f_inode->i_sb->s_dev; | ||
545 | |||
546 | return audit_mark_compare(mark, ino, dev); | ||
547 | } | ||
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index b4d8c366ec30..7714d93edb85 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
@@ -405,6 +405,12 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f) | |||
405 | if (f->val > AUDIT_MAX_FIELD_COMPARE) | 405 | if (f->val > AUDIT_MAX_FIELD_COMPARE) |
406 | return -EINVAL; | 406 | return -EINVAL; |
407 | break; | 407 | break; |
408 | case AUDIT_EXE: | ||
409 | if (f->op != Audit_equal) | ||
410 | return -EINVAL; | ||
411 | if (entry->rule.listnr != AUDIT_FILTER_EXIT) | ||
412 | return -EINVAL; | ||
413 | break; | ||
408 | }; | 414 | }; |
409 | return 0; | 415 | return 0; |
410 | } | 416 | } |
@@ -419,6 +425,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, | |||
419 | size_t remain = datasz - sizeof(struct audit_rule_data); | 425 | size_t remain = datasz - sizeof(struct audit_rule_data); |
420 | int i; | 426 | int i; |
421 | char *str; | 427 | char *str; |
428 | struct audit_fsnotify_mark *audit_mark; | ||
422 | 429 | ||
423 | entry = audit_to_entry_common(data); | 430 | entry = audit_to_entry_common(data); |
424 | if (IS_ERR(entry)) | 431 | if (IS_ERR(entry)) |
@@ -539,6 +546,24 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, | |||
539 | entry->rule.buflen += f->val; | 546 | entry->rule.buflen += f->val; |
540 | entry->rule.filterkey = str; | 547 | entry->rule.filterkey = str; |
541 | break; | 548 | break; |
549 | case AUDIT_EXE: | ||
550 | if (entry->rule.exe || f->val > PATH_MAX) | ||
551 | goto exit_free; | ||
552 | str = audit_unpack_string(&bufp, &remain, f->val); | ||
553 | if (IS_ERR(str)) { | ||
554 | err = PTR_ERR(str); | ||
555 | goto exit_free; | ||
556 | } | ||
557 | entry->rule.buflen += f->val; | ||
558 | |||
559 | audit_mark = audit_alloc_mark(&entry->rule, str, f->val); | ||
560 | if (IS_ERR(audit_mark)) { | ||
561 | kfree(str); | ||
562 | err = PTR_ERR(audit_mark); | ||
563 | goto exit_free; | ||
564 | } | ||
565 | entry->rule.exe = audit_mark; | ||
566 | break; | ||
542 | } | 567 | } |
543 | } | 568 | } |
544 | 569 | ||
@@ -551,6 +576,8 @@ exit_nofree: | |||
551 | exit_free: | 576 | exit_free: |
552 | if (entry->rule.tree) | 577 | if (entry->rule.tree) |
553 | audit_put_tree(entry->rule.tree); /* that's the temporary one */ | 578 | audit_put_tree(entry->rule.tree); /* that's the temporary one */ |
579 | if (entry->rule.exe) | ||
580 | audit_remove_mark(entry->rule.exe); /* that's the template one */ | ||
554 | audit_free_rule(entry); | 581 | audit_free_rule(entry); |
555 | return ERR_PTR(err); | 582 | return ERR_PTR(err); |
556 | } | 583 | } |
@@ -615,6 +642,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) | |||
615 | data->buflen += data->values[i] = | 642 | data->buflen += data->values[i] = |
616 | audit_pack_string(&bufp, krule->filterkey); | 643 | audit_pack_string(&bufp, krule->filterkey); |
617 | break; | 644 | break; |
645 | case AUDIT_EXE: | ||
646 | data->buflen += data->values[i] = | ||
647 | audit_pack_string(&bufp, audit_mark_path(krule->exe)); | ||
648 | break; | ||
618 | case AUDIT_LOGINUID_SET: | 649 | case AUDIT_LOGINUID_SET: |
619 | if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) { | 650 | if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) { |
620 | data->fields[i] = AUDIT_LOGINUID; | 651 | data->fields[i] = AUDIT_LOGINUID; |
@@ -678,6 +709,12 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) | |||
678 | if (strcmp(a->filterkey, b->filterkey)) | 709 | if (strcmp(a->filterkey, b->filterkey)) |
679 | return 1; | 710 | return 1; |
680 | break; | 711 | break; |
712 | case AUDIT_EXE: | ||
713 | /* both paths exist based on above type compare */ | ||
714 | if (strcmp(audit_mark_path(a->exe), | ||
715 | audit_mark_path(b->exe))) | ||
716 | return 1; | ||
717 | break; | ||
681 | case AUDIT_UID: | 718 | case AUDIT_UID: |
682 | case AUDIT_EUID: | 719 | case AUDIT_EUID: |
683 | case AUDIT_SUID: | 720 | case AUDIT_SUID: |
@@ -799,8 +836,14 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old) | |||
799 | err = -ENOMEM; | 836 | err = -ENOMEM; |
800 | else | 837 | else |
801 | new->filterkey = fk; | 838 | new->filterkey = fk; |
839 | break; | ||
840 | case AUDIT_EXE: | ||
841 | err = audit_dupe_exe(new, old); | ||
842 | break; | ||
802 | } | 843 | } |
803 | if (err) { | 844 | if (err) { |
845 | if (new->exe) | ||
846 | audit_remove_mark(new->exe); | ||
804 | audit_free_rule(entry); | 847 | audit_free_rule(entry); |
805 | return ERR_PTR(err); | 848 | return ERR_PTR(err); |
806 | } | 849 | } |
@@ -963,6 +1006,9 @@ int audit_del_rule(struct audit_entry *entry) | |||
963 | if (e->rule.tree) | 1006 | if (e->rule.tree) |
964 | audit_remove_tree_rule(&e->rule); | 1007 | audit_remove_tree_rule(&e->rule); |
965 | 1008 | ||
1009 | if (e->rule.exe) | ||
1010 | audit_remove_mark_rule(&e->rule); | ||
1011 | |||
966 | #ifdef CONFIG_AUDITSYSCALL | 1012 | #ifdef CONFIG_AUDITSYSCALL |
967 | if (!dont_count) | 1013 | if (!dont_count) |
968 | audit_n_rules--; | 1014 | audit_n_rules--; |
@@ -1067,8 +1113,11 @@ int audit_rule_change(int type, __u32 portid, int seq, void *data, | |||
1067 | WARN_ON(1); | 1113 | WARN_ON(1); |
1068 | } | 1114 | } |
1069 | 1115 | ||
1070 | if (err || type == AUDIT_DEL_RULE) | 1116 | if (err || type == AUDIT_DEL_RULE) { |
1117 | if (entry->rule.exe) | ||
1118 | audit_remove_mark(entry->rule.exe); | ||
1071 | audit_free_rule(entry); | 1119 | audit_free_rule(entry); |
1120 | } | ||
1072 | 1121 | ||
1073 | return err; | 1122 | return err; |
1074 | } | 1123 | } |
@@ -1360,6 +1409,8 @@ static int update_lsm_rule(struct audit_krule *r) | |||
1360 | return 0; | 1409 | return 0; |
1361 | 1410 | ||
1362 | nentry = audit_dupe_rule(r); | 1411 | nentry = audit_dupe_rule(r); |
1412 | if (entry->rule.exe) | ||
1413 | audit_remove_mark(entry->rule.exe); | ||
1363 | if (IS_ERR(nentry)) { | 1414 | if (IS_ERR(nentry)) { |
1364 | /* save the first error encountered for the | 1415 | /* save the first error encountered for the |
1365 | * return value */ | 1416 | * return value */ |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index ea3fe2b748a8..9b56b7ae053f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -466,6 +466,9 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
466 | result = audit_comparator(ctx->ppid, f->op, f->val); | 466 | result = audit_comparator(ctx->ppid, f->op, f->val); |
467 | } | 467 | } |
468 | break; | 468 | break; |
469 | case AUDIT_EXE: | ||
470 | result = audit_exe_compare(tsk, rule->exe); | ||
471 | break; | ||
469 | case AUDIT_UID: | 472 | case AUDIT_UID: |
470 | result = audit_uid_comparator(cred->uid, f->op, f->uid); | 473 | result = audit_uid_comparator(cred->uid, f->op, f->uid); |
471 | break; | 474 | break; |