Skip to content

Commit 59f5cf4

Browse files
tyhickskees
authored andcommitted
seccomp: Action to log before allowing
Add a new action, SECCOMP_RET_LOG, that logs a syscall before allowing the syscall. At the implementation level, this action is identical to the existing SECCOMP_RET_ALLOW action. However, it can be very useful when initially developing a seccomp filter for an application. The developer can set the default action to be SECCOMP_RET_LOG, maybe mark any obviously needed syscalls with SECCOMP_RET_ALLOW, and then put the application through its paces. A list of syscalls that triggered the default action (SECCOMP_RET_LOG) can be easily gleaned from the logs and that list can be used to build the syscall whitelist. Finally, the developer can change the default action to the desired value. This provides a more friendly experience than seeing the application get killed, then updating the filter and rebuilding the app, seeing the application get killed due to a different syscall, then updating the filter and rebuilding the app, etc. The functionality is similar to what's supported by the various LSMs. SELinux has permissive mode, AppArmor has complain mode, SMACK has bring-up mode, etc. SECCOMP_RET_LOG is given a lower value than SECCOMP_RET_ALLOW as allow while logging is slightly more restrictive than quietly allowing. Unfortunately, the tests added for SECCOMP_RET_LOG are not capable of inspecting the audit log to verify that the syscall was 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 action == RET_LOG && RET_LOG 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 <[email protected]> Signed-off-by: Kees Cook <[email protected]>
1 parent e66a399 commit 59f5cf4

File tree

4 files changed

+125
-6
lines changed

4 files changed

+125
-6
lines changed

Documentation/userspace-api/seccomp_filter.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ In precedence order, they are:
141141
allow use of ptrace, even of other sandboxed processes, without
142142
extreme care; ptracers can use this mechanism to escape.)
143143

144+
``SECCOMP_RET_LOG``:
145+
Results in the system call being executed after it is logged. This
146+
should be used by application developers to learn which syscalls their
147+
application needs without having to iterate through multiple test and
148+
development cycles to build the list.
149+
150+
This action will only be logged if "log" is present in the
151+
actions_logged sysctl string.
152+
144153
``SECCOMP_RET_ALLOW``:
145154
Results in the system call being executed.
146155

include/uapi/linux/seccomp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */
3232
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
3333
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */
34+
#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
3435
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */
3536

3637
/* Masks for the return value sections. */

kernel/seccomp.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -533,10 +533,12 @@ static void seccomp_send_sigsys(int syscall, int reason)
533533
#define SECCOMP_LOG_TRAP (1 << 2)
534534
#define SECCOMP_LOG_ERRNO (1 << 3)
535535
#define SECCOMP_LOG_TRACE (1 << 4)
536-
#define SECCOMP_LOG_ALLOW (1 << 5)
536+
#define SECCOMP_LOG_LOG (1 << 5)
537+
#define SECCOMP_LOG_ALLOW (1 << 6)
537538

538539
static u32 seccomp_actions_logged = SECCOMP_LOG_KILL | SECCOMP_LOG_TRAP |
539-
SECCOMP_LOG_ERRNO | SECCOMP_LOG_TRACE;
540+
SECCOMP_LOG_ERRNO | SECCOMP_LOG_TRACE |
541+
SECCOMP_LOG_LOG;
540542

541543
static inline void seccomp_log(unsigned long syscall, long signr, u32 action,
542544
bool requested)
@@ -555,15 +557,18 @@ static inline void seccomp_log(unsigned long syscall, long signr, u32 action,
555557
case SECCOMP_RET_TRACE:
556558
log = requested && seccomp_actions_logged & SECCOMP_LOG_TRACE;
557559
break;
560+
case SECCOMP_RET_LOG:
561+
log = seccomp_actions_logged & SECCOMP_LOG_LOG;
562+
break;
558563
case SECCOMP_RET_KILL:
559564
default:
560565
log = seccomp_actions_logged & SECCOMP_LOG_KILL;
561566
}
562567

563568
/*
564-
* Force an audit message to be emitted when the action is RET_KILL or
565-
* the FILTER_FLAG_LOG bit was set and the action is allowed to be
566-
* logged by the admin.
569+
* Force an audit message to be emitted when the action is RET_KILL,
570+
* RET_LOG, or the FILTER_FLAG_LOG bit was set and the action is
571+
* allowed to be logged by the admin.
567572
*/
568573
if (log)
569574
return __audit_seccomp(syscall, signr, action);
@@ -699,6 +704,10 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
699704

700705
return 0;
701706

707+
case SECCOMP_RET_LOG:
708+
seccomp_log(this_syscall, 0, action, true);
709+
return 0;
710+
702711
case SECCOMP_RET_ALLOW:
703712
/*
704713
* Note that the "match" filter will always be NULL for
@@ -873,6 +882,7 @@ static long seccomp_get_action_avail(const char __user *uaction)
873882
case SECCOMP_RET_TRAP:
874883
case SECCOMP_RET_ERRNO:
875884
case SECCOMP_RET_TRACE:
885+
case SECCOMP_RET_LOG:
876886
case SECCOMP_RET_ALLOW:
877887
break;
878888
default:
@@ -1023,12 +1033,14 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
10231033
#define SECCOMP_RET_TRAP_NAME "trap"
10241034
#define SECCOMP_RET_ERRNO_NAME "errno"
10251035
#define SECCOMP_RET_TRACE_NAME "trace"
1036+
#define SECCOMP_RET_LOG_NAME "log"
10261037
#define SECCOMP_RET_ALLOW_NAME "allow"
10271038

10281039
static const char seccomp_actions_avail[] = SECCOMP_RET_KILL_NAME " "
10291040
SECCOMP_RET_TRAP_NAME " "
10301041
SECCOMP_RET_ERRNO_NAME " "
10311042
SECCOMP_RET_TRACE_NAME " "
1043+
SECCOMP_RET_LOG_NAME " "
10321044
SECCOMP_RET_ALLOW_NAME;
10331045

10341046
struct seccomp_log_name {
@@ -1041,6 +1053,7 @@ static const struct seccomp_log_name seccomp_log_names[] = {
10411053
{ SECCOMP_LOG_TRAP, SECCOMP_RET_TRAP_NAME },
10421054
{ SECCOMP_LOG_ERRNO, SECCOMP_RET_ERRNO_NAME },
10431055
{ SECCOMP_LOG_TRACE, SECCOMP_RET_TRACE_NAME },
1056+
{ SECCOMP_LOG_LOG, SECCOMP_RET_LOG_NAME },
10441057
{ SECCOMP_LOG_ALLOW, SECCOMP_RET_ALLOW_NAME },
10451058
{ }
10461059
};

tools/testing/selftests/seccomp/seccomp_bpf.c

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,12 @@
7474
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
7575
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */
7676
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */
77+
#endif
78+
#ifndef SECCOMP_RET_LOG
79+
#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
80+
#endif
7781

82+
#ifndef SECCOMP_RET_ACTION
7883
/* Masks for the return value sections. */
7984
#define SECCOMP_RET_ACTION 0x7fff0000U
8085
#define SECCOMP_RET_DATA 0x0000ffffU
@@ -342,6 +347,28 @@ TEST(empty_prog)
342347
EXPECT_EQ(EINVAL, errno);
343348
}
344349

350+
TEST(log_all)
351+
{
352+
struct sock_filter filter[] = {
353+
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_LOG),
354+
};
355+
struct sock_fprog prog = {
356+
.len = (unsigned short)ARRAY_SIZE(filter),
357+
.filter = filter,
358+
};
359+
long ret;
360+
pid_t parent = getppid();
361+
362+
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
363+
ASSERT_EQ(0, ret);
364+
365+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
366+
ASSERT_EQ(0, ret);
367+
368+
/* getppid() should succeed and be logged (no check for logging) */
369+
EXPECT_EQ(parent, syscall(__NR_getppid));
370+
}
371+
345372
TEST_SIGNAL(unknown_ret_is_kill_inside, SIGSYS)
346373
{
347374
struct sock_filter filter[] = {
@@ -756,6 +783,7 @@ TEST_F(TRAP, handler)
756783

757784
FIXTURE_DATA(precedence) {
758785
struct sock_fprog allow;
786+
struct sock_fprog log;
759787
struct sock_fprog trace;
760788
struct sock_fprog error;
761789
struct sock_fprog trap;
@@ -767,6 +795,13 @@ FIXTURE_SETUP(precedence)
767795
struct sock_filter allow_insns[] = {
768796
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
769797
};
798+
struct sock_filter log_insns[] = {
799+
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
800+
offsetof(struct seccomp_data, nr)),
801+
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
802+
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
803+
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_LOG),
804+
};
770805
struct sock_filter trace_insns[] = {
771806
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
772807
offsetof(struct seccomp_data, nr)),
@@ -803,6 +838,7 @@ FIXTURE_SETUP(precedence)
803838
memcpy(self->_x.filter, &_x##_insns, sizeof(_x##_insns)); \
804839
self->_x.len = (unsigned short)ARRAY_SIZE(_x##_insns)
805840
FILTER_ALLOC(allow);
841+
FILTER_ALLOC(log);
806842
FILTER_ALLOC(trace);
807843
FILTER_ALLOC(error);
808844
FILTER_ALLOC(trap);
@@ -813,6 +849,7 @@ FIXTURE_TEARDOWN(precedence)
813849
{
814850
#define FILTER_FREE(_x) if (self->_x.filter) free(self->_x.filter)
815851
FILTER_FREE(allow);
852+
FILTER_FREE(log);
816853
FILTER_FREE(trace);
817854
FILTER_FREE(error);
818855
FILTER_FREE(trap);
@@ -830,6 +867,8 @@ TEST_F(precedence, allow_ok)
830867

831868
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
832869
ASSERT_EQ(0, ret);
870+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
871+
ASSERT_EQ(0, ret);
833872
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
834873
ASSERT_EQ(0, ret);
835874
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
@@ -854,6 +893,8 @@ TEST_F_SIGNAL(precedence, kill_is_highest, SIGSYS)
854893

855894
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
856895
ASSERT_EQ(0, ret);
896+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
897+
ASSERT_EQ(0, ret);
857898
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
858899
ASSERT_EQ(0, ret);
859900
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
@@ -885,6 +926,8 @@ TEST_F_SIGNAL(precedence, kill_is_highest_in_any_order, SIGSYS)
885926
ASSERT_EQ(0, ret);
886927
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
887928
ASSERT_EQ(0, ret);
929+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
930+
ASSERT_EQ(0, ret);
888931
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
889932
ASSERT_EQ(0, ret);
890933
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
@@ -906,6 +949,8 @@ TEST_F_SIGNAL(precedence, trap_is_second, SIGSYS)
906949

907950
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
908951
ASSERT_EQ(0, ret);
952+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
953+
ASSERT_EQ(0, ret);
909954
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
910955
ASSERT_EQ(0, ret);
911956
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
@@ -931,6 +976,8 @@ TEST_F_SIGNAL(precedence, trap_is_second_in_any_order, SIGSYS)
931976
ASSERT_EQ(0, ret);
932977
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
933978
ASSERT_EQ(0, ret);
979+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
980+
ASSERT_EQ(0, ret);
934981
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
935982
ASSERT_EQ(0, ret);
936983
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
@@ -952,6 +999,8 @@ TEST_F(precedence, errno_is_third)
952999

9531000
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
9541001
ASSERT_EQ(0, ret);
1002+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
1003+
ASSERT_EQ(0, ret);
9551004
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
9561005
ASSERT_EQ(0, ret);
9571006
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
@@ -970,6 +1019,8 @@ TEST_F(precedence, errno_is_third_in_any_order)
9701019
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
9711020
ASSERT_EQ(0, ret);
9721021

1022+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
1023+
ASSERT_EQ(0, ret);
9731024
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
9741025
ASSERT_EQ(0, ret);
9751026
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
@@ -992,6 +1043,8 @@ TEST_F(precedence, trace_is_fourth)
9921043

9931044
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
9941045
ASSERT_EQ(0, ret);
1046+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
1047+
ASSERT_EQ(0, ret);
9951048
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
9961049
ASSERT_EQ(0, ret);
9971050
/* Should work just fine. */
@@ -1013,12 +1066,54 @@ TEST_F(precedence, trace_is_fourth_in_any_order)
10131066
ASSERT_EQ(0, ret);
10141067
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
10151068
ASSERT_EQ(0, ret);
1069+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
1070+
ASSERT_EQ(0, ret);
10161071
/* Should work just fine. */
10171072
EXPECT_EQ(parent, syscall(__NR_getppid));
10181073
/* No ptracer */
10191074
EXPECT_EQ(-1, syscall(__NR_getpid));
10201075
}
10211076

1077+
TEST_F(precedence, log_is_fifth)
1078+
{
1079+
pid_t mypid, parent;
1080+
long ret;
1081+
1082+
mypid = getpid();
1083+
parent = getppid();
1084+
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1085+
ASSERT_EQ(0, ret);
1086+
1087+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
1088+
ASSERT_EQ(0, ret);
1089+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
1090+
ASSERT_EQ(0, ret);
1091+
/* Should work just fine. */
1092+
EXPECT_EQ(parent, syscall(__NR_getppid));
1093+
/* Should also work just fine */
1094+
EXPECT_EQ(mypid, syscall(__NR_getpid));
1095+
}
1096+
1097+
TEST_F(precedence, log_is_fifth_in_any_order)
1098+
{
1099+
pid_t mypid, parent;
1100+
long ret;
1101+
1102+
mypid = getpid();
1103+
parent = getppid();
1104+
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1105+
ASSERT_EQ(0, ret);
1106+
1107+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
1108+
ASSERT_EQ(0, ret);
1109+
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
1110+
ASSERT_EQ(0, ret);
1111+
/* Should work just fine. */
1112+
EXPECT_EQ(parent, syscall(__NR_getppid));
1113+
/* Should also work just fine */
1114+
EXPECT_EQ(mypid, syscall(__NR_getpid));
1115+
}
1116+
10221117
#ifndef PTRACE_O_TRACESECCOMP
10231118
#define PTRACE_O_TRACESECCOMP 0x00000080
10241119
#endif
@@ -2603,7 +2698,7 @@ TEST(get_action_avail)
26032698
{
26042699
__u32 actions[] = { SECCOMP_RET_KILL, SECCOMP_RET_TRAP,
26052700
SECCOMP_RET_ERRNO, SECCOMP_RET_TRACE,
2606-
SECCOMP_RET_ALLOW };
2701+
SECCOMP_RET_LOG, SECCOMP_RET_ALLOW };
26072702
__u32 unknown_action = 0x10000000U;
26082703
int i;
26092704
long ret;
@@ -2640,6 +2735,7 @@ TEST(get_action_avail)
26402735
* - 64-bit arg prodding
26412736
* - arch value testing (x86 modes especially)
26422737
* - verify that FILTER_FLAG_LOG filters generate log messages
2738+
* - verify that RET_LOG generates log messages
26432739
* - ...
26442740
*/
26452741

0 commit comments

Comments
 (0)