tor-browser

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

SandboxBrokerClient.cpp (10056B)


      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 "SandboxBrokerClient.h"
      8 #include "SandboxInfo.h"
      9 #include "SandboxLogging.h"
     10 
     11 #include <errno.h>
     12 #include <fcntl.h>
     13 #include <stdio.h>
     14 #include <string.h>
     15 #include <sys/socket.h>
     16 #include <sys/stat.h>
     17 #include <sys/types.h>
     18 #include <sys/un.h>
     19 #include <unistd.h>
     20 
     21 #include "mozilla/Assertions.h"
     22 #include "mozilla/Atomics.h"
     23 #include "base/strings/safe_sprintf.h"
     24 
     25 namespace mozilla {
     26 
     27 SandboxBrokerClient::SandboxBrokerClient(int aFd) : mFileDesc(aFd) {}
     28 
     29 SandboxBrokerClient::~SandboxBrokerClient() { close(mFileDesc); }
     30 
     31 int SandboxBrokerClient::DoCall(const Request* aReq, const char* aPath,
     32                                const char* aPath2, void* aResponseBuff,
     33                                bool expectFd) {
     34  // Remap /proc/self to the actual pid, so that the broker can open
     35  // it.  This happens here instead of in the broker to follow the
     36  // principle of least privilege and keep the broker as simple as
     37  // possible.  (Note: when pid namespaces happen, this will also need
     38  // to remap the inner pid to the outer pid.)
     39  // We only remap the first path.
     40  static const char kProcSelf[] = "/proc/self/";
     41  static const size_t kProcSelfLen = sizeof(kProcSelf) - 1;
     42  const char* path = aPath;
     43  // This buffer just needs to be large enough for any such path that
     44  // the policy would actually allow.  sizeof("/proc/2147483647/") == 18.
     45  char rewrittenPath[64];
     46  if (strncmp(aPath, kProcSelf, kProcSelfLen) == 0) {
     47    ssize_t len = base::strings::SafeSPrintf(rewrittenPath, "/proc/%d/%s",
     48                                             getpid(), aPath + kProcSelfLen);
     49    if (static_cast<size_t>(len) < sizeof(rewrittenPath)) {
     50      if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
     51        SANDBOX_LOG("rewriting %s -> %s", aPath, rewrittenPath);
     52      }
     53      path = rewrittenPath;
     54    } else {
     55      SANDBOX_LOG("not rewriting unexpectedly long path %s", aPath);
     56    }
     57  }
     58 
     59  if (SandboxInfo::Get().Test(SandboxInfo::kVerboseTests)) {
     60    // Dont use SANDBOX_LOG directly to not be too spammy, just make sure the
     61    // ReportLog() works as expected
     62    SandboxProfiler::ReportLog(OperationDescription[aReq->mOp]);
     63  }
     64 
     65  const void* top = CallerPC();
     66  SandboxProfiler::ReportRequest(top, aReq->mId,
     67                                 OperationDescription[aReq->mOp], aReq->mFlags,
     68                                 aPath, aPath2, getpid());
     69 
     70  struct iovec ios[3];
     71  int respFds[2];
     72 
     73  // Set up iovecs for request + path.
     74  ios[0].iov_base = const_cast<Request*>(aReq);
     75  ios[0].iov_len = sizeof(*aReq);
     76  ios[1].iov_base = const_cast<char*>(path);
     77  ios[1].iov_len = strlen(path) + 1;
     78  if (aPath2 != nullptr) {
     79    ios[2].iov_base = const_cast<char*>(aPath2);
     80    ios[2].iov_len = strlen(aPath2) + 1;
     81  } else {
     82    ios[2].iov_base = nullptr;
     83    ios[2].iov_len = 0;
     84  }
     85  if (ios[1].iov_len > kMaxPathLen) {
     86    return -ENAMETOOLONG;
     87  }
     88  if (ios[2].iov_len > kMaxPathLen) {
     89    return -ENAMETOOLONG;
     90  }
     91 
     92  // Create response socket and send request.
     93  if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, respFds) < 0) {
     94    return -errno;
     95  }
     96  const ssize_t sent = SendWithFd(mFileDesc, ios, 3, respFds[1]);
     97  const int sendErrno = errno;
     98  MOZ_ASSERT(sent < 0 || static_cast<size_t>(sent) ==
     99                             ios[0].iov_len + ios[1].iov_len + ios[2].iov_len);
    100  close(respFds[1]);
    101  if (sent < 0) {
    102    close(respFds[0]);
    103    return -sendErrno;
    104  }
    105 
    106  // Set up iovecs for response.
    107  Response resp;
    108  ios[0].iov_base = &resp;
    109  ios[0].iov_len = sizeof(resp);
    110  if (aResponseBuff) {
    111    ios[1].iov_base = aResponseBuff;
    112    ios[1].iov_len = aReq->mBufSize;
    113  } else {
    114    ios[1].iov_base = nullptr;
    115    ios[1].iov_len = 0;
    116  }
    117 
    118  // Wait for response and return appropriately.
    119  int openedFd = -1;
    120  const ssize_t recvd = RecvWithFd(respFds[0], ios, aResponseBuff ? 2 : 1,
    121                                   expectFd ? &openedFd : nullptr);
    122  const int recvErrno = errno;
    123  close(respFds[0]);
    124  if (recvd < 0) {
    125    return -recvErrno;
    126  }
    127  if (recvd == 0) {
    128    SANDBOX_LOG("Unexpected EOF, op %d flags 0%o path %s", aReq->mOp,
    129                aReq->mFlags, path);
    130    return -EIO;
    131  }
    132  MOZ_ASSERT(static_cast<size_t>(recvd) <= ios[0].iov_len + ios[1].iov_len);
    133  // Some calls such as readlink return a size if successful
    134  if (resp.mError >= 0) {
    135    // Success!
    136    if (expectFd) {
    137      MOZ_ASSERT(openedFd >= 0);
    138      return openedFd;
    139    }
    140    return resp.mError;
    141  }
    142  if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
    143    // Keep in mind that "rejected" files can include ones that don't
    144    // actually exist, if it's something that's optional or part of a
    145    // search path (e.g., shared libraries).  In those cases, this
    146    // error message is expected.
    147    SANDBOX_LOG("Failed errno %d op %s flags 0%o path %s", resp.mError,
    148                OperationDescription[aReq->mOp], aReq->mFlags, path);
    149  }
    150  if (openedFd >= 0) {
    151    close(openedFd);
    152  }
    153  return resp.mError;
    154 }
    155 
    156 SandboxBrokerCommon::Request MakeRequest(
    157    const SandboxBrokerCommon::Operation aOp, const int aFlags,
    158    const size_t aBufSize) {
    159  static Atomic<uint64_t> reqId{0};
    160  SandboxBrokerCommon::Request req = {aOp, aFlags, reqId, aBufSize};
    161  reqId++;
    162  return req;
    163 }
    164 
    165 int SandboxBrokerClient::Open(const char* aPath, int aFlags) {
    166  Request req = MakeRequest(SANDBOX_FILE_OPEN, aFlags, 0);
    167  int maybeFd = DoCall(&req, aPath, nullptr, nullptr, true);
    168  if (maybeFd >= 0) {
    169    // NSPR has opinions about file flags.  Fix O_CLOEXEC.
    170    if ((aFlags & O_CLOEXEC) == 0) {
    171      fcntl(maybeFd, F_SETFD, 0);
    172    }
    173  }
    174  return maybeFd;
    175 }
    176 
    177 int SandboxBrokerClient::Access(const char* aPath, int aMode) {
    178  Request req = MakeRequest(SANDBOX_FILE_ACCESS, aMode, 0);
    179  return DoCall(&req, aPath, nullptr, nullptr, false);
    180 }
    181 
    182 int SandboxBrokerClient::Stat(const char* aPath, statstruct* aStat) {
    183  if (!aPath || !aStat) {
    184    return -EFAULT;
    185  }
    186 
    187  Request req = MakeRequest(SANDBOX_FILE_STAT, 0, sizeof(statstruct));
    188  return DoCall(&req, aPath, nullptr, (void*)aStat, false);
    189 }
    190 
    191 int SandboxBrokerClient::LStat(const char* aPath, statstruct* aStat) {
    192  if (!aPath || !aStat) {
    193    return -EFAULT;
    194  }
    195 
    196  Request req = MakeRequest(SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(statstruct));
    197  return DoCall(&req, aPath, nullptr, (void*)aStat, false);
    198 }
    199 
    200 int SandboxBrokerClient::Chmod(const char* aPath, int aMode) {
    201  Request req = MakeRequest(SANDBOX_FILE_CHMOD, aMode, 0);
    202  return DoCall(&req, aPath, nullptr, nullptr, false);
    203 }
    204 
    205 int SandboxBrokerClient::Link(const char* aOldPath, const char* aNewPath) {
    206  Request req = MakeRequest(SANDBOX_FILE_LINK, 0, 0);
    207  return DoCall(&req, aOldPath, aNewPath, nullptr, false);
    208 }
    209 
    210 int SandboxBrokerClient::Symlink(const char* aOldPath, const char* aNewPath) {
    211  Request req = MakeRequest(SANDBOX_FILE_SYMLINK, 0, 0);
    212  return DoCall(&req, aOldPath, aNewPath, nullptr, false);
    213 }
    214 
    215 int SandboxBrokerClient::Rename(const char* aOldPath, const char* aNewPath) {
    216  Request req = MakeRequest(SANDBOX_FILE_RENAME, 0, 0);
    217  return DoCall(&req, aOldPath, aNewPath, nullptr, false);
    218 }
    219 
    220 int SandboxBrokerClient::Mkdir(const char* aPath, int aMode) {
    221  Request req = MakeRequest(SANDBOX_FILE_MKDIR, aMode, 0);
    222  return DoCall(&req, aPath, nullptr, nullptr, false);
    223 }
    224 
    225 int SandboxBrokerClient::Unlink(const char* aPath) {
    226  Request req = MakeRequest(SANDBOX_FILE_UNLINK, 0, 0);
    227  return DoCall(&req, aPath, nullptr, nullptr, false);
    228 }
    229 
    230 int SandboxBrokerClient::Rmdir(const char* aPath) {
    231  Request req = MakeRequest(SANDBOX_FILE_RMDIR, 0, 0);
    232  return DoCall(&req, aPath, nullptr, nullptr, false);
    233 }
    234 
    235 int SandboxBrokerClient::Readlink(const char* aPath, void* aBuff,
    236                                  size_t aSize) {
    237  Request req = MakeRequest(SANDBOX_FILE_READLINK, 0, aSize);
    238  return DoCall(&req, aPath, nullptr, aBuff, false);
    239 }
    240 
    241 int SandboxBrokerClient::Connect(const sockaddr_un* aAddr, size_t aLen,
    242                                 int aType) {
    243  static constexpr size_t maxLen = sizeof(aAddr->sun_path);
    244  const char* path = aAddr->sun_path;
    245  const auto addrEnd = reinterpret_cast<const char*>(aAddr) + aLen;
    246  // Ensure that the length isn't impossibly small.
    247  if (addrEnd <= path) {
    248    return -EINVAL;
    249  }
    250  // Unix domain only
    251  if (aAddr->sun_family != AF_UNIX) {
    252    return -EAFNOSUPPORT;
    253  }
    254  // How much of sun_path may be accessed?
    255  auto bufLen = static_cast<size_t>(addrEnd - path);
    256  if (bufLen > maxLen) {
    257    bufLen = maxLen;
    258  }
    259 
    260  // Try to handle abstract addresses where the address (the part
    261  // after the leading null byte) resembles a pathname: a leading
    262  // slash and no embedded nulls.
    263  //
    264  // `DoCall` expects null-terminated strings, but in this case the
    265  // "path" is terminated by the sockaddr length (without a null), so
    266  // we need to make a copy.
    267  if (bufLen >= 2 && path[0] == '\0' && path[1] == '/' &&
    268      !memchr(path + 1, '\0', bufLen - 1)) {
    269    char tmpBuf[maxLen];
    270    MOZ_RELEASE_ASSERT(bufLen - 1 < maxLen);
    271    memcpy(tmpBuf, path + 1, bufLen - 1);
    272    tmpBuf[bufLen - 1] = '\0';
    273 
    274    const Request req = MakeRequest(SANDBOX_SOCKET_CONNECT_ABSTRACT, aType, 0);
    275    return DoCall(&req, tmpBuf, nullptr, nullptr, true);
    276  }
    277 
    278  // Require null-termination.  (Linux doesn't require it, but
    279  // applications usually null-terminate for portability, and not
    280  // handling unterminated strings means we don't have to copy the path.)
    281  const size_t pathLen = strnlen(path, bufLen);
    282  if (pathLen == bufLen) {
    283    return -ENAMETOOLONG;
    284  }
    285 
    286  // Abstract addresses are handled only in some specific case, error in others
    287  if (pathLen == 0) {
    288    return -ENETUNREACH;
    289  }
    290 
    291  const Request req = MakeRequest(SANDBOX_SOCKET_CONNECT, aType, 0);
    292  return DoCall(&req, path, nullptr, nullptr, true);
    293 }
    294 
    295 }  // namespace mozilla