summaryrefslogtreecommitdiff
path: root/sandbox/linux/syscall_broker/broker_client.cc
blob: 760cf59b3c14340ff1c09ec4cd62e0f3dee6eaa3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright 2014 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/syscall_broker/broker_client.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>

#include "build/build_config.h"
#include "base/logging.h"
#include "base/pickle.h"
#include "base/posix/unix_domain_socket_linux.h"
#include "sandbox/linux/syscall_broker/broker_channel.h"
#include "sandbox/linux/syscall_broker/broker_common.h"
#include "sandbox/linux/syscall_broker/broker_policy.h"

#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
#define MSG_CMSG_CLOEXEC 0x40000000
#endif

namespace sandbox {

namespace syscall_broker {

// Make a remote system call over IPC for syscalls that take a path and flags
// as arguments, currently open() and access().
// Will return -errno like a real system call.
// This function needs to be async signal safe.
int BrokerClient::PathAndFlagsSyscall(IPCCommand syscall_type,
                                      const char* pathname,
                                      int flags) const {
  int recvmsg_flags = 0;
  RAW_CHECK(syscall_type == COMMAND_OPEN || syscall_type == COMMAND_ACCESS);
  if (!pathname)
    return -EFAULT;

  // For this "remote system call" to work, we need to handle any flag that
  // cannot be sent over a Unix socket in a special way.
  // See the comments around kCurrentProcessOpenFlagsMask.
  if (syscall_type == COMMAND_OPEN && (flags & kCurrentProcessOpenFlagsMask)) {
    // This implementation only knows about O_CLOEXEC, someone needs to look at
    // this code if other flags are added.
    RAW_CHECK(kCurrentProcessOpenFlagsMask == O_CLOEXEC);
    recvmsg_flags |= MSG_CMSG_CLOEXEC;
    flags &= ~O_CLOEXEC;
  }

  // There is no point in forwarding a request that we know will be denied.
  // Of course, the real security check needs to be on the other side of the
  // IPC.
  if (fast_check_in_client_) {
    if (syscall_type == COMMAND_OPEN &&
        !broker_policy_.GetFileNameIfAllowedToOpen(
            pathname, flags, NULL /* file_to_open */,
            NULL /* unlink_after_open */)) {
      return -broker_policy_.denied_errno();
    }
    if (syscall_type == COMMAND_ACCESS &&
        !broker_policy_.GetFileNameIfAllowedToAccess(pathname, flags, NULL)) {
      return -broker_policy_.denied_errno();
    }
  }

  base::Pickle write_pickle;
  write_pickle.WriteInt(syscall_type);
  write_pickle.WriteString(pathname);
  write_pickle.WriteInt(flags);
  RAW_CHECK(write_pickle.size() <= kMaxMessageLength);

  int returned_fd = -1;
  uint8_t reply_buf[kMaxMessageLength];

  // Send a request (in write_pickle) as well that will include a new
  // temporary socketpair (created internally by SendRecvMsg()).
  // Then read the reply on this new socketpair in reply_buf and put an
  // eventual attached file descriptor in |returned_fd|.
  ssize_t msg_len = base::UnixDomainSocket::SendRecvMsgWithFlags(
      ipc_channel_.get(), reply_buf, sizeof(reply_buf), recvmsg_flags,
      &returned_fd, write_pickle);
  if (msg_len <= 0) {
    if (!quiet_failures_for_tests_)
      RAW_LOG(ERROR, "Could not make request to broker process");
    return -ENOMEM;
  }

  base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
  base::PickleIterator iter(read_pickle);
  int return_value = -1;
  // Now deserialize the return value and eventually return the file
  // descriptor.
  if (iter.ReadInt(&return_value)) {
    switch (syscall_type) {
      case COMMAND_ACCESS:
        // We should never have a fd to return.
        RAW_CHECK(returned_fd == -1);
        return return_value;
      case COMMAND_OPEN:
        if (return_value < 0) {
          RAW_CHECK(returned_fd == -1);
          return return_value;
        } else {
          // We have a real file descriptor to return.
          RAW_CHECK(returned_fd >= 0);
          return returned_fd;
        }
      default:
        RAW_LOG(ERROR, "Unsupported command");
        return -ENOSYS;
    }
  } else {
    RAW_LOG(ERROR, "Could not read pickle");
    NOTREACHED();
    return -ENOMEM;
  }
}

BrokerClient::BrokerClient(const BrokerPolicy& broker_policy,
                           BrokerChannel::EndPoint ipc_channel,
                           bool fast_check_in_client,
                           bool quiet_failures_for_tests)
    : broker_policy_(broker_policy),
      ipc_channel_(ipc_channel.Pass()),
      fast_check_in_client_(fast_check_in_client),
      quiet_failures_for_tests_(quiet_failures_for_tests) {
}

BrokerClient::~BrokerClient() {
}

int BrokerClient::Access(const char* pathname, int mode) const {
  return PathAndFlagsSyscall(COMMAND_ACCESS, pathname, mode);
}

int BrokerClient::Open(const char* pathname, int flags) const {
  return PathAndFlagsSyscall(COMMAND_OPEN, pathname, flags);
}

}  // namespace syscall_broker

}  // namespace sandbox