普通文本  |  290行  |  8.61 KB

// 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/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include "base/basictypes.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/time/time.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/linux_syscalls.h"

#if defined(OS_ANDROID)

#include "sandbox/linux/services/android_futex.h"

#if !defined(F_DUPFD_CLOEXEC)
#define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
#endif

#endif  // defined(OS_ANDROID)

#if defined(__arm__) && !defined(MAP_STACK)
#define MAP_STACK 0x20000  // Daisy build environment has old headers.
#endif

#if defined(__mips__) && !defined(MAP_STACK)
#define MAP_STACK 0x40000
#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
}

inline bool IsArchitectureMips() {
#if defined(__mips__)
  return true;
#else
  return false;
#endif
}

}  // namespace.

#define CASES SANDBOX_BPF_DSL_CASES

using sandbox::bpf_dsl::Allow;
using sandbox::bpf_dsl::Arg;
using sandbox::bpf_dsl::BoolExpr;
using sandbox::bpf_dsl::Error;
using sandbox::bpf_dsl::If;
using sandbox::bpf_dsl::ResultExpr;

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.
ResultExpr RestrictCloneToThreadsAndEPERMFork() {
  const Arg<unsigned long> flags(0);

  // TODO(mdempsky): Extend DSL to support (flags & ~mask1) == mask2.
  const uint64_t kAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES |
                                     CLONE_SIGHAND | CLONE_THREAD |
                                     CLONE_SYSVSEM;
  const uint64_t kObsoleteAndroidCloneMask = kAndroidCloneMask | CLONE_DETACHED;

  const uint64_t kGlibcPthreadFlags =
      CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD |
      CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
  const BoolExpr glibc_test = flags == kGlibcPthreadFlags;

  const BoolExpr android_test = flags == kAndroidCloneMask ||
                                flags == kObsoleteAndroidCloneMask ||
                                flags == kGlibcPthreadFlags;

  return If(IsAndroid() ? android_test : glibc_test, Allow())
      .ElseIf((flags & (CLONE_VM | CLONE_THREAD)) == 0, Error(EPERM))
      .Else(CrashSIGSYSClone());
}

ResultExpr RestrictPrctl() {
  // Will need to add seccomp compositing in the future. PR_SET_PTRACER is
  // used by breakpad but not needed anymore.
  const Arg<int> option(0);
  return Switch(option)
      .CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE),
             Allow())
      .Default(CrashSIGSYSPrctl());
}

ResultExpr RestrictIoctl() {
  const Arg<int> request(1);
  return Switch(request).CASES((TCGETS, FIONREAD), Allow()).Default(
      CrashSIGSYSIoctl());
}

ResultExpr RestrictMmapFlags() {
  // 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.
  const uint64_t kAllowedMask = MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS |
                                MAP_STACK | MAP_NORESERVE | MAP_FIXED |
                                MAP_DENYWRITE;
  const Arg<int> flags(3);
  return If((flags & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
}

ResultExpr RestrictMprotectFlags() {
  // 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.
  const uint64_t kAllowedMask = PROT_READ | PROT_WRITE | PROT_EXEC;
  const Arg<int> prot(2);
  return If((prot & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
}

ResultExpr RestrictFcntlCommands() {
  // 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.
  uint64_t kOLargeFileFlag = O_LARGEFILE;
  if (IsArchitectureX86_64() || IsArchitectureI386() || IsArchitectureMips())
    kOLargeFileFlag = 0100000;

  const Arg<int> cmd(1);
  const Arg<long> long_arg(2);

  const uint64_t kAllowedMask = O_ACCMODE | O_APPEND | O_NONBLOCK | O_SYNC |
                                kOLargeFileFlag | O_CLOEXEC | O_NOATIME;
  return Switch(cmd)
      .CASES((F_GETFL,
              F_GETFD,
              F_SETFD,
              F_SETLK,
              F_SETLKW,
              F_GETLK,
              F_DUPFD,
              F_DUPFD_CLOEXEC),
             Allow())
      .Case(F_SETFL,
            If((long_arg & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()))
      .Default(CrashSIGSYS());
}

#if defined(__i386__) || defined(__mips__)
ResultExpr RestrictSocketcallCommand() {
  // 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.
  const Arg<int> call(0);
  return Switch(call)
      .CASES((SYS_SOCKETPAIR,
              SYS_SHUTDOWN,
              SYS_RECV,
              SYS_SEND,
              SYS_RECVFROM,
              SYS_SENDTO,
              SYS_RECVMSG,
              SYS_SENDMSG),
             Allow())
      .Default(Error(EPERM));
}
#endif

ResultExpr RestrictKillTarget(pid_t target_pid, int sysno) {
  switch (sysno) {
    case __NR_kill:
    case __NR_tgkill: {
      const Arg<pid_t> pid(0);
      return If(pid == target_pid, Allow()).Else(CrashSIGSYSKill());
    }
    case __NR_tkill:
      return CrashSIGSYSKill();
    default:
      NOTREACHED();
      return CrashSIGSYS();
  }
}

ResultExpr RestrictFutex() {
  const uint64_t kAllowedFutexFlags = FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME;
  const Arg<int> op(1);
  return Switch(op & ~kAllowedFutexFlags)
      .CASES((FUTEX_WAIT,
              FUTEX_WAKE,
              FUTEX_REQUEUE,
              FUTEX_CMP_REQUEUE,
              FUTEX_WAKE_OP,
              FUTEX_WAIT_BITSET,
              FUTEX_WAKE_BITSET),
             Allow())
      .Default(CrashSIGSYSFutex());
}

ResultExpr RestrictGetSetpriority(pid_t target_pid) {
  const Arg<int> which(0);
  const Arg<int> who(1);
  return If(which == PRIO_PROCESS,
            If(who == 0 || who == target_pid, Allow()).Else(Error(EPERM)))
      .Else(CrashSIGSYS());
}

ResultExpr RestrictClockID() {
  COMPILE_ASSERT(4 == sizeof(clockid_t), clockid_is_not_32bit);
  const Arg<clockid_t> clockid(0);
  return If(
#if defined(OS_CHROMEOS)
             // Allow the special clock for Chrome OS used by Chrome tracing.
             clockid == base::TimeTicks::kClockSystemTrace ||
#endif
                 clockid == CLOCK_MONOTONIC ||
                 clockid == CLOCK_PROCESS_CPUTIME_ID ||
                 clockid == CLOCK_REALTIME ||
                 clockid == CLOCK_THREAD_CPUTIME_ID,
             Allow()).Else(CrashSIGSYS());
}

ResultExpr RestrictSchedTarget(pid_t target_pid, int sysno) {
  switch (sysno) {
    case __NR_sched_getaffinity:
    case __NR_sched_getattr:
    case __NR_sched_getparam:
    case __NR_sched_getscheduler:
    case __NR_sched_rr_get_interval:
    case __NR_sched_setaffinity:
    case __NR_sched_setattr:
    case __NR_sched_setparam:
    case __NR_sched_setscheduler: {
      const Arg<pid_t> pid(0);
      return If(pid == 0 || pid == target_pid, Allow())
          .Else(RewriteSchedSIGSYS());
    }
    default:
      NOTREACHED();
      return CrashSIGSYS();
  }
}


}  // namespace sandbox.