Commit e66a3997 authored by Tyler Hicks's avatar Tyler Hicks Committed by Kees Cook

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: default avatarTyler Hicks <tyhicks@canonical.com>
Signed-off-by: default avatarKees Cook <keescook@chromium.org>
parent 2b7ea5b5
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
#include <uapi/linux/seccomp.h> #include <uapi/linux/seccomp.h>
#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC) #define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC | \
SECCOMP_FILTER_FLAG_LOG)
#ifdef CONFIG_SECCOMP #ifdef CONFIG_SECCOMP
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
/* Valid flags for SECCOMP_SET_MODE_FILTER */ /* Valid flags for SECCOMP_SET_MODE_FILTER */
#define SECCOMP_FILTER_FLAG_TSYNC 1 #define SECCOMP_FILTER_FLAG_TSYNC 1
#define SECCOMP_FILTER_FLAG_LOG 2
/* /*
* All BPF programs must return a 32-bit value. * All BPF programs must return a 32-bit value.
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
* get/put helpers should be used when accessing an instance * get/put helpers should be used when accessing an instance
* outside of a lifetime-guarded section. In general, this * outside of a lifetime-guarded section. In general, this
* is only needed for handling filters shared across tasks. * is only needed for handling filters shared across tasks.
* @log: true if all actions except for SECCOMP_RET_ALLOW should be logged
* @prev: points to a previously installed, or inherited, filter * @prev: points to a previously installed, or inherited, filter
* @prog: the BPF program to evaluate * @prog: the BPF program to evaluate
* *
...@@ -59,6 +60,7 @@ ...@@ -59,6 +60,7 @@
*/ */
struct seccomp_filter { struct seccomp_filter {
refcount_t usage; refcount_t usage;
bool log;
struct seccomp_filter *prev; struct seccomp_filter *prev;
struct bpf_prog *prog; struct bpf_prog *prog;
}; };
...@@ -452,6 +454,10 @@ static long seccomp_attach_filter(unsigned int flags, ...@@ -452,6 +454,10 @@ static long seccomp_attach_filter(unsigned int flags,
return ret; return ret;
} }
/* Set log flag, if present. */
if (flags & SECCOMP_FILTER_FLAG_LOG)
filter->log = true;
/* /*
* If there is an existing filter, make it the prev and don't drop its * If there is an existing filter, make it the prev and don't drop its
* task reference. * task reference.
...@@ -532,15 +538,22 @@ static void seccomp_send_sigsys(int syscall, int reason) ...@@ -532,15 +538,22 @@ static void seccomp_send_sigsys(int syscall, int reason)
static u32 seccomp_actions_logged = SECCOMP_LOG_KILL | SECCOMP_LOG_TRAP | static u32 seccomp_actions_logged = SECCOMP_LOG_KILL | SECCOMP_LOG_TRAP |
SECCOMP_LOG_ERRNO | SECCOMP_LOG_TRACE; SECCOMP_LOG_ERRNO | SECCOMP_LOG_TRACE;
static inline void seccomp_log(unsigned long syscall, long signr, u32 action) static inline void seccomp_log(unsigned long syscall, long signr, u32 action,
bool requested)
{ {
bool log = false; bool log = false;
switch (action) { switch (action) {
case SECCOMP_RET_ALLOW: case SECCOMP_RET_ALLOW:
break;
case SECCOMP_RET_TRAP: case SECCOMP_RET_TRAP:
log = requested && seccomp_actions_logged & SECCOMP_LOG_TRAP;
break;
case SECCOMP_RET_ERRNO: case SECCOMP_RET_ERRNO:
log = requested && seccomp_actions_logged & SECCOMP_LOG_ERRNO;
break;
case SECCOMP_RET_TRACE: case SECCOMP_RET_TRACE:
log = requested && seccomp_actions_logged & SECCOMP_LOG_TRACE;
break; break;
case SECCOMP_RET_KILL: case SECCOMP_RET_KILL:
default: default:
...@@ -548,8 +561,9 @@ static inline void seccomp_log(unsigned long syscall, long signr, u32 action) ...@@ -548,8 +561,9 @@ static inline void seccomp_log(unsigned long syscall, long signr, u32 action)
} }
/* /*
* Force an audit message to be emitted when the action is RET_KILL and * Force an audit message to be emitted when the action is RET_KILL or
* the action is allowed to be logged by the admin. * the FILTER_FLAG_LOG bit was set and the action is allowed to be
* logged by the admin.
*/ */
if (log) if (log)
return __audit_seccomp(syscall, signr, action); return __audit_seccomp(syscall, signr, action);
...@@ -586,7 +600,7 @@ static void __secure_computing_strict(int this_syscall) ...@@ -586,7 +600,7 @@ static void __secure_computing_strict(int this_syscall)
#ifdef SECCOMP_DEBUG #ifdef SECCOMP_DEBUG
dump_stack(); dump_stack();
#endif #endif
seccomp_log(this_syscall, SIGKILL, SECCOMP_RET_KILL); seccomp_log(this_syscall, SIGKILL, SECCOMP_RET_KILL, true);
do_exit(SIGKILL); do_exit(SIGKILL);
} }
...@@ -695,7 +709,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, ...@@ -695,7 +709,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
case SECCOMP_RET_KILL: case SECCOMP_RET_KILL:
default: default:
seccomp_log(this_syscall, SIGSYS, action); seccomp_log(this_syscall, SIGSYS, action, true);
/* Dump core only if this is the last remaining thread. */ /* Dump core only if this is the last remaining thread. */
if (get_nr_threads(current) == 1) { if (get_nr_threads(current) == 1) {
siginfo_t info; siginfo_t info;
...@@ -712,7 +726,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, ...@@ -712,7 +726,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
unreachable(); unreachable();
skip: skip:
seccomp_log(this_syscall, 0, action); seccomp_log(this_syscall, 0, action, match ? match->log : false);
return -1; return -1;
} }
#else #else
......
...@@ -1739,6 +1739,10 @@ TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS) ...@@ -1739,6 +1739,10 @@ TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS)
#define SECCOMP_FILTER_FLAG_TSYNC 1 #define SECCOMP_FILTER_FLAG_TSYNC 1
#endif #endif
#ifndef SECCOMP_FILTER_FLAG_LOG
#define SECCOMP_FILTER_FLAG_LOG 2
#endif
#ifndef seccomp #ifndef seccomp
int seccomp(unsigned int op, unsigned int flags, void *args) int seccomp(unsigned int op, unsigned int flags, void *args)
{ {
...@@ -1844,7 +1848,8 @@ TEST(seccomp_syscall_mode_lock) ...@@ -1844,7 +1848,8 @@ TEST(seccomp_syscall_mode_lock)
*/ */
TEST(detect_seccomp_filter_flags) TEST(detect_seccomp_filter_flags)
{ {
unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC }; unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC,
SECCOMP_FILTER_FLAG_LOG };
unsigned int flag, all_flags; unsigned int flag, all_flags;
int i; int i;
long ret; long ret;
...@@ -2533,6 +2538,67 @@ TEST(syscall_restart) ...@@ -2533,6 +2538,67 @@ TEST(syscall_restart)
_metadata->passed = 0; _metadata->passed = 0;
} }
TEST_SIGNAL(filter_flag_log, SIGSYS)
{
struct sock_filter allow_filter[] = {
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_filter kill_filter[] = {
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog allow_prog = {
.len = (unsigned short)ARRAY_SIZE(allow_filter),
.filter = allow_filter,
};
struct sock_fprog kill_prog = {
.len = (unsigned short)ARRAY_SIZE(kill_filter),
.filter = kill_filter,
};
long ret;
pid_t parent = getppid();
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
ASSERT_EQ(0, ret);
/* Verify that the FILTER_FLAG_LOG flag isn't accepted in strict mode */
ret = seccomp(SECCOMP_SET_MODE_STRICT, SECCOMP_FILTER_FLAG_LOG,
&allow_prog);
ASSERT_NE(ENOSYS, errno) {
TH_LOG("Kernel does not support seccomp syscall!");
}
EXPECT_NE(0, ret) {
TH_LOG("Kernel accepted FILTER_FLAG_LOG flag in strict mode!");
}
EXPECT_EQ(EINVAL, errno) {
TH_LOG("Kernel returned unexpected errno for FILTER_FLAG_LOG flag in strict mode!");
}
/* Verify that a simple, permissive filter can be added with no flags */
ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &allow_prog);
EXPECT_EQ(0, ret);
/* See if the same filter can be added with the FILTER_FLAG_LOG flag */
ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG,
&allow_prog);
ASSERT_NE(EINVAL, errno) {
TH_LOG("Kernel does not support the FILTER_FLAG_LOG flag!");
}
EXPECT_EQ(0, ret);
/* Ensure that the kill filter works with the FILTER_FLAG_LOG flag */
ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG,
&kill_prog);
EXPECT_EQ(0, ret);
EXPECT_EQ(parent, syscall(__NR_getppid));
/* getpid() should never return. */
EXPECT_EQ(0, syscall(__NR_getpid));
}
TEST(get_action_avail) TEST(get_action_avail)
{ {
__u32 actions[] = { SECCOMP_RET_KILL, SECCOMP_RET_TRAP, __u32 actions[] = { SECCOMP_RET_KILL, SECCOMP_RET_TRAP,
...@@ -2573,6 +2639,7 @@ TEST(get_action_avail) ...@@ -2573,6 +2639,7 @@ TEST(get_action_avail)
* - endianness checking when appropriate * - endianness checking when appropriate
* - 64-bit arg prodding * - 64-bit arg prodding
* - arch value testing (x86 modes especially) * - arch value testing (x86 modes especially)
* - verify that FILTER_FLAG_LOG filters generate log messages
* - ... * - ...
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment