// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/basictypes.h" #include "base/logging.h" #include "base/macros.h" #include "build/build_config.h" #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" #include "sandbox/linux/services/android_futex.h" #if defined(OS_ANDROID) #if !defined(F_DUPFD_CLOEXEC) #define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6) #endif #endif #if defined(__arm__) && !defined(MAP_STACK) #define MAP_STACK 0x20000 // Daisy build environment has old headers. #endif namespace { inline bool IsArchitectureX86_64() { #if defined(__x86_64__) return true; #else return false; #endif } inline bool IsArchitectureI386() { #if defined(__i386__) return true; #else return false; #endif } inline bool IsAndroid() { #if defined(OS_ANDROID) return true; #else return false; #endif } } // namespace. namespace sandbox { // Allow Glibc's and Android pthread creation flags, crash on any other // thread creation attempts and EPERM attempts to use neither // CLONE_VM, nor CLONE_THREAD, which includes all fork() implementations. ErrorCode RestrictCloneToThreadsAndEPERMFork(SandboxBPF* sandbox) { if (!IsAndroid()) { const uint64_t kGlibcPthreadFlags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, kGlibcPthreadFlags, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS, CLONE_VM | CLONE_THREAD, sandbox->Trap(SIGSYSCloneFailure, NULL), ErrorCode(EPERM))); } else { const uint64_t kAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM; const uint64_t kObsoleteAndroidCloneMask = kAndroidCloneMask | CLONE_DETACHED; return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, kAndroidCloneMask, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, kObsoleteAndroidCloneMask, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS, CLONE_VM | CLONE_THREAD, sandbox->Trap(SIGSYSCloneFailure, NULL), ErrorCode(EPERM)))); } } ErrorCode RestrictPrctl(SandboxBPF* sandbox) { // Will need to add seccomp compositing in the future. PR_SET_PTRACER is // used by breakpad but not needed anymore. return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, PR_SET_NAME, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, PR_SET_DUMPABLE, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, PR_GET_DUMPABLE, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Trap(SIGSYSPrctlFailure, NULL)))); } ErrorCode RestrictIoctl(SandboxBPF* sandbox) { return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, TCGETS, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, FIONREAD, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Trap(SIGSYSIoctlFailure, NULL))); } ErrorCode RestrictMmapFlags(SandboxBPF* sandbox) { // The flags you see are actually the allowed ones, and the variable is a // "denied" mask because of the negation operator. // Significantly, we don't permit MAP_HUGETLB, or the newer flags such as // MAP_POPULATE. // TODO(davidung), remove MAP_DENYWRITE with updated Tegra libraries. uint32_t denied_mask = ~(MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK | MAP_NORESERVE | MAP_FIXED | MAP_DENYWRITE); return sandbox->Cond(3, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS, denied_mask, sandbox->Trap(CrashSIGSYS_Handler, NULL), ErrorCode(ErrorCode::ERR_ALLOWED)); } ErrorCode RestrictMprotectFlags(SandboxBPF* sandbox) { // The flags you see are actually the allowed ones, and the variable is a // "denied" mask because of the negation operator. // Significantly, we don't permit weird undocumented flags such as // PROT_GROWSDOWN. uint32_t denied_mask = ~(PROT_READ | PROT_WRITE | PROT_EXEC); return sandbox->Cond(2, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS, denied_mask, sandbox->Trap(CrashSIGSYS_Handler, NULL), ErrorCode(ErrorCode::ERR_ALLOWED)); } ErrorCode RestrictFcntlCommands(SandboxBPF* sandbox) { // We also restrict the flags in F_SETFL. We don't want to permit flags with // a history of trouble such as O_DIRECT. The flags you see are actually the // allowed ones, and the variable is a "denied" mask because of the negation // operator. // Glibc overrides the kernel's O_LARGEFILE value. Account for this. int kOLargeFileFlag = O_LARGEFILE; if (IsArchitectureX86_64() || IsArchitectureI386()) kOLargeFileFlag = 0100000; // TODO(jln): add TP_LONG/TP_SIZET types. ErrorCode::ArgType mask_long_type; if (sizeof(long) == 8) mask_long_type = ErrorCode::TP_64BIT; else if (sizeof(long) == 4) mask_long_type = ErrorCode::TP_32BIT; else NOTREACHED(); unsigned long denied_mask = ~(O_ACCMODE | O_APPEND | O_NONBLOCK | O_SYNC | kOLargeFileFlag | O_CLOEXEC | O_NOATIME); return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, F_GETFL, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, F_SETFL, sandbox->Cond(2, mask_long_type, ErrorCode::OP_HAS_ANY_BITS, denied_mask, sandbox->Trap(CrashSIGSYS_Handler, NULL), ErrorCode(ErrorCode::ERR_ALLOWED)), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, F_GETFD, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, F_SETFD, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, F_DUPFD, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, F_SETLK, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, F_SETLKW, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, F_GETLK, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, F_DUPFD_CLOEXEC, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Trap(CrashSIGSYS_Handler, NULL)))))))))); } #if defined(__i386__) ErrorCode RestrictSocketcallCommand(SandboxBPF* sandbox) { // Unfortunately, we are unable to restrict the first parameter to // socketpair(2). Whilst initially sounding bad, it's noteworthy that very // few protocols actually support socketpair(2). The scary call that we're // worried about, socket(2), remains blocked. return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, SYS_SOCKETPAIR, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, SYS_SEND, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, SYS_RECV, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, SYS_SENDTO, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, SYS_RECVFROM, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, SYS_SHUTDOWN, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, SYS_SENDMSG, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, SYS_RECVMSG, ErrorCode(ErrorCode::ERR_ALLOWED), ErrorCode(EPERM))))))))); } #endif ErrorCode RestrictKillTarget(pid_t target_pid, SandboxBPF* sandbox, int sysno) { switch (sysno) { case __NR_kill: case __NR_tgkill: return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, target_pid, ErrorCode(ErrorCode::ERR_ALLOWED), sandbox->Trap(SIGSYSKillFailure, NULL)); case __NR_tkill: return sandbox->Trap(SIGSYSKillFailure, NULL); default: NOTREACHED(); return sandbox->Trap(CrashSIGSYS_Handler, NULL); } } ErrorCode RestrictFutex(SandboxBPF* sandbox) { // In futex.c, the kernel does "int cmd = op & FUTEX_CMD_MASK;". We need to // make sure that the combination below will cover every way to get // FUTEX_CMP_REQUEUE_PI. const int kBannedFutexBits = ~(FUTEX_CMD_MASK | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME); COMPILE_ASSERT(0 == kBannedFutexBits, need_to_explicitly_blacklist_more_bits); return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, FUTEX_CMP_REQUEUE_PI, sandbox->Trap(SIGSYSFutexFailure, NULL), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, FUTEX_CMP_REQUEUE_PI_PRIVATE, sandbox->Trap(SIGSYSFutexFailure, NULL), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, FUTEX_CMP_REQUEUE_PI | FUTEX_CLOCK_REALTIME, sandbox->Trap(SIGSYSFutexFailure, NULL), sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, FUTEX_CMP_REQUEUE_PI_PRIVATE | FUTEX_CLOCK_REALTIME, sandbox->Trap(SIGSYSFutexFailure, NULL), ErrorCode(ErrorCode::ERR_ALLOWED))))); } } // namespace sandbox.