Home | History | Annotate | Download | only in syscall_broker
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "sandbox/linux/syscall_broker/broker_host.h"
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <stddef.h>
     10 #include <sys/socket.h>
     11 #include <sys/stat.h>
     12 #include <sys/syscall.h>
     13 #include <sys/types.h>
     14 #include <unistd.h>
     15 
     16 #include <string>
     17 #include <utility>
     18 #include <vector>
     19 
     20 #include "base/files/scoped_file.h"
     21 #include "base/logging.h"
     22 #include "base/pickle.h"
     23 #include "base/posix/eintr_wrapper.h"
     24 #include "base/posix/unix_domain_socket_linux.h"
     25 #include "base/third_party/valgrind/valgrind.h"
     26 #include "sandbox/linux/syscall_broker/broker_common.h"
     27 #include "sandbox/linux/syscall_broker/broker_policy.h"
     28 #include "sandbox/linux/system_headers/linux_syscalls.h"
     29 
     30 namespace sandbox {
     31 
     32 namespace syscall_broker {
     33 
     34 namespace {
     35 
     36 bool IsRunningOnValgrind() {
     37   return RUNNING_ON_VALGRIND;
     38 }
     39 
     40 // A little open(2) wrapper to handle some oddities for us. In the general case
     41 // make a direct system call since we want to keep in control of the broker
     42 // process' system calls profile to be able to loosely sandbox it.
     43 int sys_open(const char* pathname, int flags) {
     44   // Hardcode mode to rw------- when creating files.
     45   int mode;
     46   if (flags & O_CREAT) {
     47     mode = 0600;
     48   } else {
     49     mode = 0;
     50   }
     51   if (IsRunningOnValgrind()) {
     52     // Valgrind does not support AT_FDCWD, just use libc's open() in this case.
     53     return open(pathname, flags, mode);
     54   } else {
     55     return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
     56   }
     57 }
     58 
     59 // Open |requested_filename| with |flags| if allowed by our policy.
     60 // Write the syscall return value (-errno) to |write_pickle| and append
     61 // a file descriptor to |opened_files| if relevant.
     62 void OpenFileForIPC(const BrokerPolicy& policy,
     63                     const std::string& requested_filename,
     64                     int flags,
     65                     base::Pickle* write_pickle,
     66                     std::vector<int>* opened_files) {
     67   DCHECK(write_pickle);
     68   DCHECK(opened_files);
     69   const char* file_to_open = NULL;
     70   bool unlink_after_open = false;
     71   const bool safe_to_open_file = policy.GetFileNameIfAllowedToOpen(
     72       requested_filename.c_str(), flags, &file_to_open, &unlink_after_open);
     73 
     74   if (safe_to_open_file) {
     75     CHECK(file_to_open);
     76     int opened_fd = sys_open(file_to_open, flags);
     77     if (opened_fd < 0) {
     78       write_pickle->WriteInt(-errno);
     79     } else {
     80       // Success.
     81       if (unlink_after_open) {
     82         unlink(file_to_open);
     83       }
     84       opened_files->push_back(opened_fd);
     85       write_pickle->WriteInt(0);
     86     }
     87   } else {
     88     write_pickle->WriteInt(-policy.denied_errno());
     89   }
     90 }
     91 
     92 // Perform access(2) on |requested_filename| with mode |mode| if allowed by our
     93 // policy. Write the syscall return value (-errno) to |write_pickle|.
     94 void AccessFileForIPC(const BrokerPolicy& policy,
     95                       const std::string& requested_filename,
     96                       int mode,
     97                       base::Pickle* write_pickle) {
     98   DCHECK(write_pickle);
     99   const char* file_to_access = NULL;
    100   const bool safe_to_access_file = policy.GetFileNameIfAllowedToAccess(
    101       requested_filename.c_str(), mode, &file_to_access);
    102 
    103   if (safe_to_access_file) {
    104     CHECK(file_to_access);
    105     int access_ret = access(file_to_access, mode);
    106     int access_errno = errno;
    107     if (!access_ret)
    108       write_pickle->WriteInt(0);
    109     else
    110       write_pickle->WriteInt(-access_errno);
    111   } else {
    112     write_pickle->WriteInt(-policy.denied_errno());
    113   }
    114 }
    115 
    116 // Handle a |command_type| request contained in |iter| and send the reply
    117 // on |reply_ipc|.
    118 // Currently COMMAND_OPEN and COMMAND_ACCESS are supported.
    119 bool HandleRemoteCommand(const BrokerPolicy& policy,
    120                          IPCCommand command_type,
    121                          int reply_ipc,
    122                          base::PickleIterator iter) {
    123   // Currently all commands have two arguments: filename and flags.
    124   std::string requested_filename;
    125   int flags = 0;
    126   if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
    127     return false;
    128 
    129   base::Pickle write_pickle;
    130   std::vector<int> opened_files;
    131 
    132   switch (command_type) {
    133     case COMMAND_ACCESS:
    134       AccessFileForIPC(policy, requested_filename, flags, &write_pickle);
    135       break;
    136     case COMMAND_OPEN:
    137       OpenFileForIPC(
    138           policy, requested_filename, flags, &write_pickle, &opened_files);
    139       break;
    140     default:
    141       LOG(ERROR) << "Invalid IPC command";
    142       break;
    143   }
    144 
    145   CHECK_LE(write_pickle.size(), kMaxMessageLength);
    146   ssize_t sent = base::UnixDomainSocket::SendMsg(
    147       reply_ipc, write_pickle.data(), write_pickle.size(), opened_files);
    148 
    149   // Close anything we have opened in this process.
    150   for (std::vector<int>::iterator it = opened_files.begin();
    151        it != opened_files.end();
    152        ++it) {
    153     int ret = IGNORE_EINTR(close(*it));
    154     DCHECK(!ret) << "Could not close file descriptor";
    155   }
    156 
    157   if (sent <= 0) {
    158     LOG(ERROR) << "Could not send IPC reply";
    159     return false;
    160   }
    161   return true;
    162 }
    163 
    164 }  // namespace
    165 
    166 BrokerHost::BrokerHost(const BrokerPolicy& broker_policy,
    167                        BrokerChannel::EndPoint ipc_channel)
    168     : broker_policy_(broker_policy), ipc_channel_(std::move(ipc_channel)) {}
    169 
    170 BrokerHost::~BrokerHost() {
    171 }
    172 
    173 // Handle a request on the IPC channel ipc_channel_.
    174 // A request should have a file descriptor attached on which we will reply and
    175 // that we will then close.
    176 // A request should start with an int that will be used as the command type.
    177 BrokerHost::RequestStatus BrokerHost::HandleRequest() const {
    178   std::vector<base::ScopedFD> fds;
    179   char buf[kMaxMessageLength];
    180   errno = 0;
    181   const ssize_t msg_len = base::UnixDomainSocket::RecvMsg(
    182       ipc_channel_.get(), buf, sizeof(buf), &fds);
    183 
    184   if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
    185     // EOF from the client, or the client died, we should die.
    186     return RequestStatus::LOST_CLIENT;
    187   }
    188 
    189   // The client should send exactly one file descriptor, on which we
    190   // will write the reply.
    191   if (msg_len < 0 || fds.size() != 1 || fds[0].get() < 0) {
    192     PLOG(ERROR) << "Error reading message from the client";
    193     return RequestStatus::FAILURE;
    194   }
    195 
    196   base::ScopedFD temporary_ipc(std::move(fds[0]));
    197 
    198   base::Pickle pickle(buf, msg_len);
    199   base::PickleIterator iter(pickle);
    200   int command_type;
    201   if (iter.ReadInt(&command_type)) {
    202     bool command_handled = false;
    203     // Go through all the possible IPC messages.
    204     switch (command_type) {
    205       case COMMAND_ACCESS:
    206       case COMMAND_OPEN:
    207         // We reply on the file descriptor sent to us via the IPC channel.
    208         command_handled = HandleRemoteCommand(
    209             broker_policy_, static_cast<IPCCommand>(command_type),
    210             temporary_ipc.get(), iter);
    211         break;
    212       default:
    213         NOTREACHED();
    214         break;
    215     }
    216 
    217     if (command_handled) {
    218       return RequestStatus::SUCCESS;
    219     } else {
    220       return RequestStatus::FAILURE;
    221     }
    222 
    223     NOTREACHED();
    224   }
    225 
    226   LOG(ERROR) << "Error parsing IPC request";
    227   return RequestStatus::FAILURE;
    228 }
    229 
    230 }  // namespace syscall_broker
    231 
    232 }  // namespace sandbox
    233