|
Message-Id: <1458788042-26173-9-git-send-email-mic@digikod.net> Date: Thu, 24 Mar 2016 03:54:02 +0100 From: Mickaël Salaün <mic@...ikod.net> To: linux-security-module@...r.kernel.org Cc: Mickaël Salaün <mic@...ikod.net>, Andreas Gruenbacher <agruenba@...hat.com>, Andy Lutomirski <luto@...capital.net>, Andy Lutomirski <luto@...nel.org>, Arnd Bergmann <arnd@...db.de>, Casey Schaufler <casey@...aufler-ca.com>, Daniel Borkmann <daniel@...earbox.net>, David Drysdale <drysdale@...gle.com>, Eric Paris <eparis@...hat.com>, James Morris <james.l.morris@...cle.com>, Jeff Dike <jdike@...toit.com>, Julien Tinnes <jln@...gle.com>, Kees Cook <keescook@...omium.org>, Michael Kerrisk <mtk@...7.org>, Paul Moore <pmoore@...hat.com>, Richard Weinberger <richard@....at>, "Serge E . Hallyn" <serge@...lyn.com>, Stephen Smalley <sds@...ho.nsa.gov>, Tetsuo Handa <penguin-kernel@...ove.SAKURA.ne.jp>, Will Drewry <wad@...omium.org>, linux-api@...r.kernel.org, kernel-hardening@...ts.openwall.com Subject: [RFC v1 17/17] selftest/seccomp: Add argeval_toctou_filesystem test Detect a TOCTOU race condition attack on the filesystem by renaming a file after the seccomp filter evaluation but before the effective syscall. Signed-off-by: Mickaël Salaün <mic@...ikod.net> Cc: Andy Lutomirski <luto@...nel.org> Cc: Kees Cook <keescook@...omium.org> Cc: Paul Moore <pmoore@...hat.com> Cc: Will Drewry <wad@...omium.org> --- tools/testing/selftests/seccomp/seccomp_bpf.c | 180 +++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 64b4d758b007..1558e0079fe9 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -10,6 +10,7 @@ #define __have_sigval_t 1 #define __have_sigevent_t 1 +#define _GNU_SOURCE #include <errno.h> #include <linux/filter.h> #include <sys/prctl.h> @@ -32,8 +33,6 @@ #include <sys/fcntl.h> #include <sys/mman.h> #include <sys/times.h> - -#define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> @@ -2520,6 +2519,183 @@ TEST_F(TRACE_poke_arg_path, argeval_toctou_argument) EXPECT_EQ(E2BIG, errno); close(fd); } + +char *new_file(struct __test_metadata *_metadata, const char *name, char buf) +{ + int ret, fd, path_len; + char *path; + const char tmpl[] = "/tmp/seccomp-test_%s.XXXXXX"; + + path_len = sizeof(tmpl) - 2 + strlen(name); + path = malloc(path_len); + ASSERT_NE(path, NULL); + ret = snprintf(path, path_len, tmpl, name); + ASSERT_EQ(ret, path_len - 1); + fd = mkostemp(path, O_CLOEXEC); + ASSERT_NE(fd, -1); + ret = write(fd, &buf, sizeof(buf)); + ASSERT_EQ(ret, sizeof(buf)); + close(fd); + return path; +} + +struct tracer_args_files { + char *path_orig, *path_hijack, *path_swap; +}; + +/* Move a file after the filter evaluation but before the effective syscall. */ +void tracer_swap_file(struct __test_metadata *_metadata, pid_t tracee, + int status, void *args) +{ + int ret; + unsigned long msg; + struct tracer_args_files *info = (struct tracer_args_files *)args; + + ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg); + EXPECT_EQ(0, ret); + /* If this fails, don't try to recover. */ + ASSERT_EQ(0x1002, msg) { + kill(tracee, SIGKILL); + } + /* Let's start the bonneteau! */ + ret = rename(info->path_orig, info->path_swap); + EXPECT_EQ(0, ret); + ret = rename(info->path_hijack, info->path_orig); + EXPECT_EQ(0, ret); + ret = rename(info->path_swap, info->path_hijack); + EXPECT_EQ(0, ret); +} + +FIXTURE_DATA(TRACE_swap_file) { + struct sock_fprog prog; + pid_t tracer; + struct tracer_args_files tracer_args; + char *path_orig, *path_hijack, *path_swap; +}; + +FIXTURE_SETUP(TRACE_swap_file) +{ + int fd; + unsigned long orig_delta, orig_size, hijack_delta, hijack_size; + struct sock_filter filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_open, 0, 1), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1002), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + + memset(&self->prog, 0, sizeof(self->prog)); + self->prog.filter = malloc(sizeof(filter)); + ASSERT_NE(NULL, self->prog.filter); + memcpy(self->prog.filter, filter, sizeof(filter)); + self->prog.len = (unsigned short)ARRAY_SIZE(filter); + + /* Create all the files */ + self->path_orig = new_file(_metadata, "orig", 'O'); + self->tracer_args.path_orig = self->path_orig; + self->path_hijack = new_file(_metadata, "hijack", 'H'); + self->tracer_args.path_hijack = self->path_hijack; + self->path_swap = new_file(_metadata, "swap", 'S'); + self->tracer_args.path_swap = self->path_swap; + + /* Remove the temporary swap file */ + unlink(self->path_swap); + + /* Launch tracer */ + self->tracer = setup_trace_fixture(_metadata, tracer_swap_file, + &self->tracer_args); +} + +FIXTURE_TEARDOWN(TRACE_swap_file) +{ + teardown_trace_fixture(_metadata, self->tracer); + if (self->prog.filter) + free(self->prog.filter); + if (self->path_orig) { + unlink(self->path_orig); + free(self->path_orig); + } + if (self->path_hijack) { + unlink(self->path_hijack); + free(self->path_hijack); + } + if (self->path_swap) { + unlink(self->path_swap); + free(self->path_swap); + } +} + +TEST_F(TRACE_swap_file, argeval_toctou_filesystem) +{ + int fd; + char buf; + ssize_t len; + + /* Validate the first test file */ + fd = open(self->path_orig, O_RDONLY); + EXPECT_NE(-1, fd) { + TH_LOG("Failed to open %s", self->path_orig); + } + len = read(fd, &buf, sizeof(buf)); + EXPECT_EQ(1, len) { + TH_LOG("Failed to read from %s", self->path_orig); + } + EXPECT_EQ('O', buf) { + TH_LOG("Got unexpected value from %s", self->path_orig); + } + close(fd); + + /* Validate the second test file */ + fd = open(self->path_hijack, O_RDONLY); + EXPECT_NE(-1, fd) { + TH_LOG("Failed to open %s", self->path_hijack); + } + len = read(fd, &buf, sizeof(buf)); + EXPECT_EQ(1, len) { + TH_LOG("Failed to read from %s", self->path_hijack); + } + EXPECT_EQ('H', buf) { + TH_LOG("Got unexpected value from %s", self->path_hijack); + } + close(fd); + + apply_sandbox0(_metadata, self->path_orig); + + /* Setup the hijack for every open */ + EXPECT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, + SECCOMP_FILTER_FLAG_TSYNC, &self->prog)) { + TH_LOG("Failed to install filter!"); + } + + /* Hijacked file */ + fd = open(self->path_orig, O_RDONLY); + EXPECT_EQ(-1, fd) { + TH_LOG("Could open %s", self->path_hijack); + } + EXPECT_EQ(EPERM, errno); + close(fd); + + /* Denied file */ + fd = open(self->path_orig, O_RDONLY); + EXPECT_EQ(-1, fd) { + TH_LOG("Could open %s", self->path_hijack); + } + EXPECT_EQ(E2BIG, errno); + close(fd); +} + +/* + * TODO: tests to add + * - symlink following + * - dentry/inode/device/mount checkers + * - PATH_BENEATH + * - object creation with nonexistent file + * - validate that ptrace's SETREGS is still working on a process using seccomp-objects + * - TOCTOU with a hard link (should pass) + * - limits + */ + #endif /* SECCOMP_DATA_ARGEVAL_PRESENT */ /* -- 2.8.0.rc3
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.