aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing
diff options
context:
space:
mode:
authorTycho Andersen <tycho@tycho.ws>2018-12-09 13:24:13 -0500
committerKees Cook <keescook@chromium.org>2018-12-11 19:28:41 -0500
commit6a21cc50f0c7f87dae5259f6cfefe024412313f6 (patch)
tree0312987667dc2b05e9f9cc33586fac101b542a9a /tools/testing
parenta5662e4d81c4d4b08140c625d0f3c50b15786252 (diff)
seccomp: add a return code to trap to userspace
This patch introduces a means for syscalls matched in seccomp to notify some other task that a particular filter has been triggered. The motivation for this is primarily for use with containers. For example, if a container does an init_module(), we obviously don't want to load this untrusted code, which may be compiled for the wrong version of the kernel anyway. Instead, we could parse the module image, figure out which module the container is trying to load and load it on the host. As another example, containers cannot mount() in general since various filesystems assume a trusted image. However, if an orchestrator knows that e.g. a particular block device has not been exposed to a container for writing, it want to allow the container to mount that block device (that is, handle the mount for it). This patch adds functionality that is already possible via at least two other means that I know about, both of which involve ptrace(): first, one could ptrace attach, and then iterate through syscalls via PTRACE_SYSCALL. Unfortunately this is slow, so a faster version would be to install a filter that does SECCOMP_RET_TRACE, which triggers a PTRACE_EVENT_SECCOMP. Since ptrace allows only one tracer, if the container runtime is that tracer, users inside the container (or outside) trying to debug it will not be able to use ptrace, which is annoying. It also means that older distributions based on Upstart cannot boot inside containers using ptrace, since upstart itself uses ptrace to monitor services while starting. The actual implementation of this is fairly small, although getting the synchronization right was/is slightly complex. Finally, it's worth noting that the classic seccomp TOCTOU of reading memory data from the task still applies here, but can be avoided with careful design of the userspace handler: if the userspace handler reads all of the task memory that is necessary before applying its security policy, the tracee's subsequent memory edits will not be read by the tracer. Signed-off-by: Tycho Andersen <tycho@tycho.ws> CC: Kees Cook <keescook@chromium.org> CC: Andy Lutomirski <luto@amacapital.net> CC: Oleg Nesterov <oleg@redhat.com> CC: Eric W. Biederman <ebiederm@xmission.com> CC: "Serge E. Hallyn" <serge@hallyn.com> Acked-by: Serge Hallyn <serge@hallyn.com> CC: Christian Brauner <christian@brauner.io> CC: Tyler Hicks <tyhicks@canonical.com> CC: Akihiro Suda <suda.akihiro@lab.ntt.co.jp> Signed-off-by: Kees Cook <keescook@chromium.org>
Diffstat (limited to 'tools/testing')
-rw-r--r--tools/testing/selftests/seccomp/seccomp_bpf.c447
1 files changed, 445 insertions, 2 deletions
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index e1473234968d..5c9768a1b8cd 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -5,6 +5,7 @@
5 * Test code for seccomp bpf. 5 * Test code for seccomp bpf.
6 */ 6 */
7 7
8#define _GNU_SOURCE
8#include <sys/types.h> 9#include <sys/types.h>
9 10
10/* 11/*
@@ -40,10 +41,12 @@
40#include <sys/fcntl.h> 41#include <sys/fcntl.h>
41#include <sys/mman.h> 42#include <sys/mman.h>
42#include <sys/times.h> 43#include <sys/times.h>
44#include <sys/socket.h>
45#include <sys/ioctl.h>
43 46
44#define _GNU_SOURCE
45#include <unistd.h> 47#include <unistd.h>
46#include <sys/syscall.h> 48#include <sys/syscall.h>
49#include <poll.h>
47 50
48#include "../kselftest_harness.h" 51#include "../kselftest_harness.h"
49 52
@@ -133,6 +136,10 @@ struct seccomp_data {
133#define SECCOMP_GET_ACTION_AVAIL 2 136#define SECCOMP_GET_ACTION_AVAIL 2
134#endif 137#endif
135 138
139#ifndef SECCOMP_GET_NOTIF_SIZES
140#define SECCOMP_GET_NOTIF_SIZES 3
141#endif
142
136#ifndef SECCOMP_FILTER_FLAG_TSYNC 143#ifndef SECCOMP_FILTER_FLAG_TSYNC
137#define SECCOMP_FILTER_FLAG_TSYNC (1UL << 0) 144#define SECCOMP_FILTER_FLAG_TSYNC (1UL << 0)
138#endif 145#endif
@@ -154,6 +161,44 @@ struct seccomp_metadata {
154}; 161};
155#endif 162#endif
156 163
164#ifndef SECCOMP_FILTER_FLAG_NEW_LISTENER
165#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
166
167#define SECCOMP_RET_USER_NOTIF 0x7fc00000U
168
169#define SECCOMP_IOC_MAGIC '!'
170#define SECCOMP_IO(nr) _IO(SECCOMP_IOC_MAGIC, nr)
171#define SECCOMP_IOR(nr, type) _IOR(SECCOMP_IOC_MAGIC, nr, type)
172#define SECCOMP_IOW(nr, type) _IOW(SECCOMP_IOC_MAGIC, nr, type)
173#define SECCOMP_IOWR(nr, type) _IOWR(SECCOMP_IOC_MAGIC, nr, type)
174
175/* Flags for seccomp notification fd ioctl. */
176#define SECCOMP_IOCTL_NOTIF_RECV SECCOMP_IOWR(0, struct seccomp_notif)
177#define SECCOMP_IOCTL_NOTIF_SEND SECCOMP_IOWR(1, \
178 struct seccomp_notif_resp)
179#define SECCOMP_IOCTL_NOTIF_ID_VALID SECCOMP_IOR(2, __u64)
180
181struct seccomp_notif {
182 __u64 id;
183 __u32 pid;
184 __u32 flags;
185 struct seccomp_data data;
186};
187
188struct seccomp_notif_resp {
189 __u64 id;
190 __s64 val;
191 __s32 error;
192 __u32 flags;
193};
194
195struct seccomp_notif_sizes {
196 __u16 seccomp_notif;
197 __u16 seccomp_notif_resp;
198 __u16 seccomp_data;
199};
200#endif
201
157#ifndef seccomp 202#ifndef seccomp
158int seccomp(unsigned int op, unsigned int flags, void *args) 203int seccomp(unsigned int op, unsigned int flags, void *args)
159{ 204{
@@ -2077,7 +2122,8 @@ TEST(detect_seccomp_filter_flags)
2077{ 2122{
2078 unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC, 2123 unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC,
2079 SECCOMP_FILTER_FLAG_LOG, 2124 SECCOMP_FILTER_FLAG_LOG,
2080 SECCOMP_FILTER_FLAG_SPEC_ALLOW }; 2125 SECCOMP_FILTER_FLAG_SPEC_ALLOW,
2126 SECCOMP_FILTER_FLAG_NEW_LISTENER };
2081 unsigned int flag, all_flags; 2127 unsigned int flag, all_flags;
2082 int i; 2128 int i;
2083 long ret; 2129 long ret;
@@ -2933,6 +2979,403 @@ skip:
2933 ASSERT_EQ(0, kill(pid, SIGKILL)); 2979 ASSERT_EQ(0, kill(pid, SIGKILL));
2934} 2980}
2935 2981
2982static int user_trap_syscall(int nr, unsigned int flags)
2983{
2984 struct sock_filter filter[] = {
2985 BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
2986 offsetof(struct seccomp_data, nr)),
2987 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, nr, 0, 1),
2988 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_USER_NOTIF),
2989 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
2990 };
2991
2992 struct sock_fprog prog = {
2993 .len = (unsigned short)ARRAY_SIZE(filter),
2994 .filter = filter,
2995 };
2996
2997 return seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog);
2998}
2999
3000#define USER_NOTIF_MAGIC 116983961184613L
3001TEST(user_notification_basic)
3002{
3003 pid_t pid;
3004 long ret;
3005 int status, listener;
3006 struct seccomp_notif req = {};
3007 struct seccomp_notif_resp resp = {};
3008 struct pollfd pollfd;
3009
3010 struct sock_filter filter[] = {
3011 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
3012 };
3013 struct sock_fprog prog = {
3014 .len = (unsigned short)ARRAY_SIZE(filter),
3015 .filter = filter,
3016 };
3017
3018 pid = fork();
3019 ASSERT_GE(pid, 0);
3020
3021 /* Check that we get -ENOSYS with no listener attached */
3022 if (pid == 0) {
3023 if (user_trap_syscall(__NR_getpid, 0) < 0)
3024 exit(1);
3025 ret = syscall(__NR_getpid);
3026 exit(ret >= 0 || errno != ENOSYS);
3027 }
3028
3029 EXPECT_EQ(waitpid(pid, &status, 0), pid);
3030 EXPECT_EQ(true, WIFEXITED(status));
3031 EXPECT_EQ(0, WEXITSTATUS(status));
3032
3033 /* Add some no-op filters so for grins. */
3034 EXPECT_EQ(seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog), 0);
3035 EXPECT_EQ(seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog), 0);
3036 EXPECT_EQ(seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog), 0);
3037 EXPECT_EQ(seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog), 0);
3038
3039 /* Check that the basic notification machinery works */
3040 listener = user_trap_syscall(__NR_getpid,
3041 SECCOMP_FILTER_FLAG_NEW_LISTENER);
3042 EXPECT_GE(listener, 0);
3043
3044 /* Installing a second listener in the chain should EBUSY */
3045 EXPECT_EQ(user_trap_syscall(__NR_getpid,
3046 SECCOMP_FILTER_FLAG_NEW_LISTENER),
3047 -1);
3048 EXPECT_EQ(errno, EBUSY);
3049
3050 pid = fork();
3051 ASSERT_GE(pid, 0);
3052
3053 if (pid == 0) {
3054 ret = syscall(__NR_getpid);
3055 exit(ret != USER_NOTIF_MAGIC);
3056 }
3057
3058 pollfd.fd = listener;
3059 pollfd.events = POLLIN | POLLOUT;
3060
3061 EXPECT_GT(poll(&pollfd, 1, -1), 0);
3062 EXPECT_EQ(pollfd.revents, POLLIN);