aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTyler Hicks <tyhicks@canonical.com>2017-08-11 00:33:56 -0400
committerKees Cook <keescook@chromium.org>2017-08-14 16:46:46 -0400
commite66a39977985b1e69e17c4042cb290768eca9b02 (patch)
tree1247786fccc6a57144930cfe4fd8fbf6f0a44a24
parent2b7ea5b5b5799f2878ed454bb48032bed6d101d3 (diff)
seccomp: Filter flag to log all actions except SECCOMP_RET_ALLOW
Add a new filter flag, SECCOMP_FILTER_FLAG_LOG, that enables logging for all actions except for SECCOMP_RET_ALLOW for the given filter. SECCOMP_RET_KILL actions are always logged, when "kill" is in the actions_logged sysctl, and SECCOMP_RET_ALLOW actions are never logged, regardless of this flag. This flag can be used to create noisy filters that result in all non-allowed actions to be logged. A process may have one noisy filter, which is loaded with this flag, as well as a quiet filter that's not loaded with this flag. This allows for the actions in a set of filters to be selectively conveyed to the admin. Since a system could have a large number of allocated seccomp_filter structs, struct packing was taken in consideration. On 64 bit x86, the new log member takes up one byte of an existing four byte hole in the struct. On 32 bit x86, the new log member creates a new four byte hole (unavoidable) and consumes one of those bytes. Unfortunately, the tests added for SECCOMP_FILTER_FLAG_LOG are not capable of inspecting the audit log to verify that the actions taken in the filter were logged. With this patch, the logic for deciding if an action will be logged is: if action == RET_ALLOW: do not log else if action == RET_KILL && RET_KILL in actions_logged: log else if filter-requests-logging && action in actions_logged: log else if audit_enabled && process-is-being-audited: log else: do not log Signed-off-by: Tyler Hicks <tyhicks@canonical.com> Signed-off-by: Kees Cook <keescook@chromium.org>
-rw-r--r--include/linux/seccomp.h3
-rw-r--r--include/uapi/linux/seccomp.h1
-rw-r--r--kernel/seccomp.c26
-rw-r--r--tools/testing/selftests/seccomp/seccomp_bpf.c69
4 files changed, 91 insertions, 8 deletions
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index ecc296c137cd..c8bef436b61d 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -3,7 +3,8 @@
3 3
4#include <uapi/linux/seccomp.h> 4#include <uapi/linux/seccomp.h>
5 5
6#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC) 6#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC | \
7 SECCOMP_FILTER_FLAG_LOG)
7 8
8#ifdef CONFIG_SECCOMP 9#ifdef CONFIG_SECCOMP
9 10
diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h
index aaad61cc46bc..19a611d0712e 100644
--- a/include/uapi/linux/seccomp.h
+++ b/include/uapi/linux/seccomp.h
@@ -17,6 +17,7 @@
17 17
18/* Valid flags for SECCOMP_SET_MODE_FILTER */ 18/* Valid flags for SECCOMP_SET_MODE_FILTER */
19#define SECCOMP_FILTER_FLAG_TSYNC 1 19#define SECCOMP_FILTER_FLAG_TSYNC 1
20#define SECCOMP_FILTER_FLAG_LOG 2
20 21
21/* 22/*
22 * All BPF programs must return a 32-bit value. 23 * All BPF programs must return a 32-bit value.
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 54357e361aea..ed9fde418fc4 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -44,6 +44,7 @@
44 * get/put helpers should be used when accessing an instance 44 * get/put helpers should be used when accessing an instance
45 * outside of a lifetime-guarded section. In general, this 45 * outside of a lifetime-guarded section. In general, this
46 * is only needed for handling filters shared across tasks. 46 * is only needed for handling filters shared across tasks.
47 * @log: true if all actions except for SECCOMP_RET_ALLOW should be logged
47 * @prev: points to a previously installed, or inherited, filter 48 * @prev: points to a previously installed, or inherited, filter
48 * @prog: the BPF program to evaluate 49 * @prog: the BPF program to evaluate
49 * 50 *
@@ -59,6 +60,7 @@
59 */ 60 */
60struct seccomp_filter { 61struct seccomp_filter {
61 refcount_t usage; 62 refcount_t usage;
63 bool log;
62 struct seccomp_filter *prev; 64 struct seccomp_filter *prev;
63 struct bpf_prog *prog; 65 struct bpf_prog *prog;
64}; 66};
@@ -452,6 +454,10 @@ static long seccomp_attach_filter(unsigned int flags,
452 return ret; 454 return ret;
453 } 455 }
454 456
457 /* Set log flag, if present. */
458 if (flags & SECCOMP_FILTER_FLAG_LOG)
459 filter->log = true;
460
455 /* 461 /*
456 * If there is an existing filter, make it the prev and don't drop its 462 * If there is an existing filter, make it the prev and don't drop its
457 * task reference. 463 * task reference.
@@ -532,15 +538,22 @@ static void seccomp_send_sigsys(int syscall, int reason)
532static u32 seccomp_actions_logged = SECCOMP_LOG_KILL | SECCOMP_LOG_TRAP | 538static u32 seccomp_actions_logged = SECCOMP_LOG_KILL | SECCOMP_LOG_TRAP |
533 SECCOMP_LOG_ERRNO | SECCOMP_LOG_TRACE; 539 SECCOMP_LOG_ERRNO | SECCOMP_LOG_TRACE;
534 540
535static inline void seccomp_log(unsigned long syscall, long signr, u32 action) 541static inline void seccomp_log(unsigned long syscall, long signr, u32 action,
542 bool requested)
536{ 543{
537 bool log = false; 544 bool log = false;
538 545
539 switch (action) { 546 switch (action) {
540 case SECCOMP_RET_ALLOW: 547 case SECCOMP_RET_ALLOW:
548 break;
541 case SECCOMP_RET_TRAP: 549 case SECCOMP_RET_TRAP:
550 log = requested && seccomp_actions_logged & SECCOMP_LOG_TRAP;
551 break;
542 case SECCOMP_RET_ERRNO: 552 case SECCOMP_RET_ERRNO:
553 log = requested && seccomp_actions_logged & SECCOMP_LOG_ERRNO;
554 break;
543 case SECCOMP_RET_TRACE: 555 case SECCOMP_RET_TRACE:
556 log = requested && seccomp_actions_logged & SECCOMP_LOG_TRACE;
544 break; 557 break;
545 case SECCOMP_RET_KILL: 558 case SECCOMP_RET_KILL:
546 default: 559 default:
@@ -548,8 +561,9 @@ static inline void seccomp_log(unsigned long syscall, long signr, u32 action)
548 } 561 }
549 562
550 /* 563 /*
551 * Force an audit message to be emitted when the action is RET_KILL and 564 * Force an audit message to be emitted when the action is RET_KILL or
552 * the action is allowed to be logged by the admin. 565 * the FILTER_FLAG_LOG bit was set and the action is allowed to be
566 * logged by the admin.
553 */ 567 */
554 if (log) 568 if (log)
555 return __audit_seccomp(syscall, signr, action); 569 return __audit_seccomp(syscall, signr, action);
@@ -586,7 +600,7 @@ static void __secure_computing_strict(int this_syscall)
586#ifdef SECCOMP_DEBUG 600#ifdef SECCOMP_DEBUG
587 dump_stack(); 601 dump_stack();
588#endif 602#endif
589 seccomp_log(this_syscall, SIGKILL, SECCOMP_RET_KILL); 603 seccomp_log(this_syscall, SIGKILL, SECCOMP_RET_KILL, true);
590 do_exit(SIGKILL); 604 do_exit(SIGKILL);
591} 605}
592 606
@@ -695,7 +709,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
695 709
696 case SECCOMP_RET_KILL: 710 case SECCOMP_RET_KILL:
697 default: 711 default:
698 seccomp_log(this_syscall, SIGSYS, action); 712 seccomp_log(this_syscall, SIGSYS, action, true);
699 /* Dump core only if this is the last remaining thread. */ 713 /* Dump core only if this is the last remaining thread. */
700 if (get_nr_threads(current) == 1) { 714 if (get_nr_threads(current) == 1) {
701 siginfo_t info; 715 siginfo_t info;
@@ -712,7 +726,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
712 unreachable(); 726 unreachable();
713 727
714skip: 728skip:
715 seccomp_log(this_syscall, 0, action); 729 seccomp_log(this_syscall, 0, action, match ? match->log : false);
716 return -1; 730 return -1;
717} 731}
718#else 732#else
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index abf708e09892..1c8c22ce7740 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -1739,6 +1739,10 @@ TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS)
1739#define SECCOMP_FILTER_FLAG_TSYNC 1 1739#define SECCOMP_FILTER_FLAG_TSYNC 1
1740#endif 1740#endif
1741 1741
1742#ifndef SECCOMP_FILTER_FLAG_LOG
1743#define SECCOMP_FILTER_FLAG_LOG 2
1744#endif
1745
1742#ifndef seccomp 1746#ifndef seccomp
1743int seccomp(unsigned int op, unsigned int flags, void *args) 1747int seccomp(unsigned int op, unsigned int flags, void *args)
1744{ 1748{
@@ -1844,7 +1848,8 @@ TEST(seccomp_syscall_mode_lock)
1844 */ 1848 */
1845TEST(detect_seccomp_filter_flags) 1849TEST(detect_seccomp_filter_flags)
1846{ 1850{
1847 unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC }; 1851 unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC,
1852 SECCOMP_FILTER_FLAG_LOG };
1848 unsigned int flag, all_flags; 1853 unsigned int flag, all_flags;
1849 int i; 1854 int i;
1850 long ret; 1855 long ret;
@@ -2533,6 +2538,67 @@ TEST(syscall_restart)
2533 _metadata->passed = 0; 2538 _metadata->passed = 0;
2534} 2539}
2535 2540
2541TEST_SIGNAL(filter_flag_log, SIGSYS)
2542{
2543 struct sock_filter allow_filter[] = {
2544 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
2545 };
2546 struct sock_filter kill_filter[] = {
2547 BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
2548 offsetof(struct seccomp_data, nr)),
2549 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
2550 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
2551 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
2552 };
2553 struct sock_fprog allow_prog = {
2554 .len = (unsigned short)ARRAY_SIZE(allow_filter),
2555 .filter = allow_filter,
2556 };
2557 struct sock_fprog kill_prog = {
2558 .len = (unsigned short)ARRAY_SIZE(kill_filter),
2559 .filter = kill_filter,
2560 };
2561 long ret;
2562 pid_t parent = getppid();
2563
2564 ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
2565 ASSERT_EQ(0, ret);
2566
2567 /* Verify that the FILTER_FLAG_LOG flag isn't accepted in strict mode */
2568 ret = seccomp(SECCOMP_SET_MODE_STRICT, SECCOMP_FILTER_FLAG_LOG,
2569 &allow_prog);
2570 ASSERT_NE(ENOSYS, errno) {
2571 TH_LOG("Kernel does not support seccomp syscall!");
2572 }
2573 EXPECT_NE(0, ret) {
2574 TH_LOG("Kernel accepted FILTER_FLAG_LOG flag in strict mode!");
2575 }
2576 EXPECT_EQ(EINVAL, errno) {
2577 TH_LOG("Kernel returned unexpected errno for FILTER_FLAG_LOG flag in strict mode!");
2578 }
2579
2580 /* Verify that a simple, permissive filter can be added with no flags */
2581 ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &allow_prog);
2582 EXPECT_EQ(0, ret);
2583
2584 /* See if the same filter can be added with the FILTER_FLAG_LOG flag */
2585 ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG,
2586 &allow_prog);
2587 ASSERT_NE(EINVAL, errno) {
2588 TH_LOG("Kernel does not support the FILTER_FLAG_LOG flag!");
2589 }
2590 EXPECT_EQ(0, ret);
2591
2592 /* Ensure that the kill filter works with the FILTER_FLAG_LOG flag */
2593 ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG,
2594 &kill_prog);
2595 EXPECT_EQ(0, ret);
2596
2597 EXPECT_EQ(parent, syscall(__NR_getppid));
2598 /* getpid() should never return. */
2599 EXPECT_EQ(0, syscall(__NR_getpid));
2600}
2601
2536TEST(get_action_avail) 2602TEST(get_action_avail)
2537{ 2603{
2538 __u32 actions[] = { SECCOMP_RET_KILL, SECCOMP_RET_TRAP, 2604 __u32 actions[] = { SECCOMP_RET_KILL, SECCOMP_RET_TRAP,
@@ -2573,6 +2639,7 @@ TEST(get_action_avail)
2573 * - endianness checking when appropriate 2639 * - endianness checking when appropriate
2574 * - 64-bit arg prodding 2640 * - 64-bit arg prodding
2575 * - arch value testing (x86 modes especially) 2641 * - arch value testing (x86 modes especially)
2642 * - verify that FILTER_FLAG_LOG filters generate log messages
2576 * - ... 2643 * - ...
2577 */ 2644 */
2578 2645