tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SandboxInfo.cpp (8181B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "SandboxInfo.h"
      8 #include "SandboxLogging.h"
      9 #include "LinuxSched.h"
     10 
     11 #include <errno.h>
     12 #include <stdlib.h>
     13 #include <sys/prctl.h>
     14 #include <sys/stat.h>
     15 #include <sys/syscall.h>
     16 #include <sys/wait.h>
     17 #include <unistd.h>
     18 
     19 #include "base/posix/eintr_wrapper.h"
     20 #include "mozilla/Assertions.h"
     21 #include "mozilla/SandboxSettings.h"
     22 #include "sandbox/linux/system_headers/linux_seccomp.h"
     23 #include "sandbox/linux/system_headers/linux_syscalls.h"
     24 
     25 #ifdef MOZ_VALGRIND
     26 #  include <valgrind/valgrind.h>
     27 #endif
     28 
     29 // A note about assertions: in general, the worst thing this module
     30 // should be able to do is disable sandboxing features, so release
     31 // asserts or MOZ_CRASH should be avoided, even for seeming
     32 // impossibilities like an unimplemented syscall returning success
     33 // (which has happened: https://crbug.com/439795 ).
     34 //
     35 // MOZ_DIAGNOSTIC_ASSERT (debug builds, plus Nightly/Aurora non-debug)
     36 // is probably the best choice for conditions that shouldn't be able
     37 // to fail without the help of bugs in the kernel or system libraries.
     38 //
     39 // Regardless of assertion type, whatever condition caused it to fail
     40 // should generally also disable the corresponding feature on builds
     41 // that omit the assertion.
     42 
     43 namespace mozilla {
     44 
     45 static bool HasSeccompBPF() {
     46  // Allow simulating the absence of seccomp-bpf support, for testing.
     47  if (getenv("MOZ_FAKE_NO_SANDBOX")) {
     48    return false;
     49  }
     50 
     51  // Valgrind and the sandbox don't interact well, probably because Valgrind
     52  // does various system calls which aren't allowed, even if Firefox itself
     53  // is playing by the rules.
     54 #if defined(MOZ_VALGRIND)
     55  if (RUNNING_ON_VALGRIND) {
     56    return false;
     57  }
     58 #endif
     59 
     60  // Determine whether seccomp-bpf is supported by trying to
     61  // enable it with an invalid pointer for the filter.  This will
     62  // fail with EFAULT if supported and EINVAL if not, without
     63  // changing the process's state.
     64 
     65  int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr);
     66  MOZ_DIAGNOSTIC_ASSERT(rv == -1,
     67                        "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER,"
     68                        " nullptr) didn't fail");
     69  MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL);
     70  return rv == -1 && errno == EFAULT;
     71 }
     72 
     73 static bool HasSeccompTSync() {
     74  // Similar to above, but for thread-sync mode.  See also Chromium's
     75  // sandbox::SandboxBPF::SupportsSeccompThreadFilterSynchronization
     76  if (getenv("MOZ_FAKE_NO_SECCOMP_TSYNC")) {
     77    return false;
     78  }
     79  int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
     80                   SECCOMP_FILTER_FLAG_TSYNC, nullptr);
     81  MOZ_DIAGNOSTIC_ASSERT(rv == -1,
     82                        "seccomp(..., SECCOMP_FILTER_FLAG_TSYNC,"
     83                        " nullptr) didn't fail");
     84  MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL || errno == ENOSYS);
     85  return rv == -1 && errno == EFAULT;
     86 }
     87 
     88 static bool HasUserNamespaceSupport() {
     89  // Note: the /proc/<pid>/ns/* files track setns(2) support, which in
     90  // some cases (e.g., pid) significantly postdates kernel support for
     91  // the namespace type, so in general this type of check could be a
     92  // false negative.  However, for user namespaces, any kernel new
     93  // enough for the feature to be usable for us has setns support
     94  // (v3.8), so this is okay.
     95  //
     96  // The non-user namespaces all default to "y" in init/Kconfig, but
     97  // check them explicitly in case someone has a weird custom config.
     98  static const char* const paths[] = {
     99      "/proc/self/ns/user",
    100      "/proc/self/ns/pid",
    101      "/proc/self/ns/net",
    102      "/proc/self/ns/ipc",
    103  };
    104  for (size_t i = 0; i < std::size(paths); ++i) {
    105    if (access(paths[i], F_OK) == -1) {
    106      MOZ_ASSERT(errno == ENOENT);
    107      return false;
    108    }
    109  }
    110  return true;
    111 }
    112 
    113 static bool CanCreateUserNamespace() {
    114  // Unfortunately, the only way to verify that this process can
    115  // create a new user namespace is to actually create one; because
    116  // this process's namespaces shouldn't be side-effected (yet), it's
    117  // necessary to clone (and collect) a child process.  See also
    118  // Chromium's sandbox::Credentials::SupportsNewUserNS.
    119  //
    120  // This is somewhat more expensive than the other tests, so it's
    121  // cached in the environment to prevent child processes from having
    122  // to re-run the test.
    123  //
    124  // This is run at static initializer time, while single-threaded, so
    125  // locking isn't needed to access the environment.
    126  static const char kCacheEnvName[] = "MOZ_ASSUME_USER_NS";
    127  const char* cached = getenv(kCacheEnvName);
    128  if (cached) {
    129    return cached[0] > '0';
    130  }
    131 
    132  // Bug 1434528: In addition to CLONE_NEWUSER, do something that uses
    133  // the new capabilities (in this case, cloning another namespace) to
    134  // detect AppArmor policies that allow CLONE_NEWUSER but don't allow
    135  // doing anything useful with it.
    136  //
    137  // Bug 1884347: There's a new AppArmor feature which can result in
    138  // unsharing NEWUSER and NEWPID (or NEWNET etc.) in one syscall
    139  // being allowed, but further use of capabilities will be blocked
    140  // afterwards.  That may be a bug, but we need to handle it.
    141  pid_t pid = syscall(__NR_clone, SIGCHLD | CLONE_NEWUSER, nullptr, nullptr,
    142                      nullptr, nullptr);
    143  if (pid == 0) {
    144    // The exact meaning of `unshare(CLONE_NEWPID)` is slightly
    145    // counterintuitive but in this case it doesn't matter.  This just
    146    // needs to be some operation that attempts to use capabilities,
    147    // to check if it's blocked by an LSM.
    148    int rv = unshare(CLONE_NEWPID);
    149    if (rv < 0) {
    150      SANDBOX_LOG_ERRNO("CanCreateUserNamespace() unshare(CLONE_NEWPID)");
    151    }
    152 
    153    // Exit with status 0 on success, 1 on failure.
    154    _exit(rv == 0 ? 0 : 1);
    155  }
    156  if (pid == -1) {
    157    SANDBOX_LOG_ERRNO("CanCreateUserNamespace() clone() failure");
    158    // Failure.
    159    MOZ_ASSERT(errno == EINVAL ||  // unsupported
    160               errno == EPERM ||   // root-only, or we're already chrooted
    161               errno == EUSERS);   // already at user namespace nesting limit
    162    setenv(kCacheEnvName, "0", 1);
    163    return false;
    164  }
    165  // Otherwise, in the parent and successful.
    166  int wstatus;
    167  bool waitpid_ok = HANDLE_EINTR(waitpid(pid, &wstatus, 0)) == pid;
    168  MOZ_ASSERT(waitpid_ok);
    169  if (!waitpid_ok) {
    170    SANDBOX_LOG_ERRNO("CanCreateUserNamespace() waitpid(%d) failure", pid);
    171    return false;
    172  }
    173  // Check for failures reported by the child process.
    174  if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
    175    if (!(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1)) {
    176      SANDBOX_LOG(
    177          "CanCreateUserNamespace() waitpid(%d) child process failure %08x",
    178          pid, wstatus);
    179    }
    180    setenv(kCacheEnvName, "0", 1);
    181    return false;
    182  }
    183  setenv(kCacheEnvName, "1", 1);
    184  return true;
    185 }
    186 
    187 /* static */
    188 MOZ_RUNINIT const SandboxInfo SandboxInfo::sSingleton = SandboxInfo();
    189 
    190 SandboxInfo::SandboxInfo() {
    191  int flags = 0;
    192  static_assert(sizeof(flags) >= sizeof(Flags), "enum Flags fits in an int");
    193 
    194  if (HasSeccompBPF()) {
    195    flags |= kHasSeccompBPF;
    196    if (HasSeccompTSync()) {
    197      flags |= kHasSeccompTSync;
    198    }
    199  }
    200 
    201  if (HasUserNamespaceSupport()) {
    202    flags |= kHasPrivilegedUserNamespaces;
    203    if (CanCreateUserNamespace()) {
    204      flags |= kHasUserNamespaces;
    205    }
    206  }
    207 
    208  // We can't use mozilla::IsContentSandboxEnabled() here because a)
    209  // libmozsandbox can't depend on libxul, and b) this is called in a static
    210  // initializer before the prefences service is ready.
    211  if (!getenv("MOZ_DISABLE_CONTENT_SANDBOX")) {
    212    flags |= kEnabledForContent;
    213  }
    214  if (getenv("MOZ_PERMISSIVE_CONTENT_SANDBOX")) {
    215    flags |= kPermissive;
    216  }
    217  if (!getenv("MOZ_DISABLE_GMP_SANDBOX")) {
    218    flags |= kEnabledForMedia;
    219  }
    220  if (getenv("MOZ_SANDBOX_LOGGING")) {
    221    flags |= kVerbose;
    222  }
    223  if (getenv("MOZ_SANDBOX_LOGGING_FOR_TESTS")) {
    224    flags |= kVerboseTests;
    225  }
    226 
    227  mFlags = static_cast<Flags>(flags);
    228 }
    229 
    230 }  // namespace mozilla