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_client.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <stddef.h>
10#include <stdint.h>
11#include <sys/socket.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <utility>
15
16#include "base/logging.h"
17#include "base/pickle.h"
18#include "base/posix/unix_domain_socket_linux.h"
19#include "build/build_config.h"
20#include "sandbox/linux/syscall_broker/broker_channel.h"
21#include "sandbox/linux/syscall_broker/broker_common.h"
22#include "sandbox/linux/syscall_broker/broker_policy.h"
23
24#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
25#define MSG_CMSG_CLOEXEC 0x40000000
26#endif
27
28namespace sandbox {
29
30namespace syscall_broker {
31
32// Make a remote system call over IPC for syscalls that take a path and flags
33// as arguments, currently open() and access().
34// Will return -errno like a real system call.
35// This function needs to be async signal safe.
36int BrokerClient::PathAndFlagsSyscall(IPCCommand syscall_type,
37                                      const char* pathname,
38                                      int flags) const {
39  int recvmsg_flags = 0;
40  RAW_CHECK(syscall_type == COMMAND_OPEN || syscall_type == COMMAND_ACCESS);
41  if (!pathname)
42    return -EFAULT;
43
44  // For this "remote system call" to work, we need to handle any flag that
45  // cannot be sent over a Unix socket in a special way.
46  // See the comments around kCurrentProcessOpenFlagsMask.
47  if (syscall_type == COMMAND_OPEN && (flags & kCurrentProcessOpenFlagsMask)) {
48    // This implementation only knows about O_CLOEXEC, someone needs to look at
49    // this code if other flags are added.
50    RAW_CHECK(kCurrentProcessOpenFlagsMask == O_CLOEXEC);
51    recvmsg_flags |= MSG_CMSG_CLOEXEC;
52    flags &= ~O_CLOEXEC;
53  }
54
55  // There is no point in forwarding a request that we know will be denied.
56  // Of course, the real security check needs to be on the other side of the
57  // IPC.
58  if (fast_check_in_client_) {
59    if (syscall_type == COMMAND_OPEN &&
60        !broker_policy_.GetFileNameIfAllowedToOpen(
61            pathname, flags, NULL /* file_to_open */,
62            NULL /* unlink_after_open */)) {
63      return -broker_policy_.denied_errno();
64    }
65    if (syscall_type == COMMAND_ACCESS &&
66        !broker_policy_.GetFileNameIfAllowedToAccess(pathname, flags, NULL)) {
67      return -broker_policy_.denied_errno();
68    }
69  }
70
71  base::Pickle write_pickle;
72  write_pickle.WriteInt(syscall_type);
73  write_pickle.WriteString(pathname);
74  write_pickle.WriteInt(flags);
75  RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
76
77  int returned_fd = -1;
78  uint8_t reply_buf[kMaxMessageLength];
79
80  // Send a request (in write_pickle) as well that will include a new
81  // temporary socketpair (created internally by SendRecvMsg()).
82  // Then read the reply on this new socketpair in reply_buf and put an
83  // eventual attached file descriptor in |returned_fd|.
84  ssize_t msg_len = base::UnixDomainSocket::SendRecvMsgWithFlags(
85      ipc_channel_.get(), reply_buf, sizeof(reply_buf), recvmsg_flags,
86      &returned_fd, write_pickle);
87  if (msg_len <= 0) {
88    if (!quiet_failures_for_tests_)
89      RAW_LOG(ERROR, "Could not make request to broker process");
90    return -ENOMEM;
91  }
92
93  base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
94  base::PickleIterator iter(read_pickle);
95  int return_value = -1;
96  // Now deserialize the return value and eventually return the file
97  // descriptor.
98  if (iter.ReadInt(&return_value)) {
99    switch (syscall_type) {
100      case COMMAND_ACCESS:
101        // We should never have a fd to return.
102        RAW_CHECK(returned_fd == -1);
103        return return_value;
104      case COMMAND_OPEN:
105        if (return_value < 0) {
106          RAW_CHECK(returned_fd == -1);
107          return return_value;
108        } else {
109          // We have a real file descriptor to return.
110          RAW_CHECK(returned_fd >= 0);
111          return returned_fd;
112        }
113      default:
114        RAW_LOG(ERROR, "Unsupported command");
115        return -ENOSYS;
116    }
117  } else {
118    RAW_LOG(ERROR, "Could not read pickle");
119    NOTREACHED();
120    return -ENOMEM;
121  }
122}
123
124BrokerClient::BrokerClient(const BrokerPolicy& broker_policy,
125                           BrokerChannel::EndPoint ipc_channel,
126                           bool fast_check_in_client,
127                           bool quiet_failures_for_tests)
128    : broker_policy_(broker_policy),
129      ipc_channel_(std::move(ipc_channel)),
130      fast_check_in_client_(fast_check_in_client),
131      quiet_failures_for_tests_(quiet_failures_for_tests) {}
132
133BrokerClient::~BrokerClient() {
134}
135
136int BrokerClient::Access(const char* pathname, int mode) const {
137  return PathAndFlagsSyscall(COMMAND_ACCESS, pathname, mode);
138}
139
140int BrokerClient::Open(const char* pathname, int flags) const {
141  return PathAndFlagsSyscall(COMMAND_OPEN, pathname, flags);
142}
143
144}  // namespace syscall_broker
145
146}  // namespace sandbox
147