blob: 16c37a069753508d89011f77a0dc9c6f4d3f672d [file] [log] [blame]
// 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 <errno.h>
#include <fcntl.h>
#include <fcntl.h>
#include <linux/futex.h>
#include <linux/net.h>
#include <sched.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#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.