summaryrefslogtreecommitdiff
path: root/sandbox/linux/syscall_broker/broker_client.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/linux/syscall_broker/broker_client.cc')
-rw-r--r--sandbox/linux/syscall_broker/broker_client.cc144
1 files changed, 144 insertions, 0 deletions
diff --git a/sandbox/linux/syscall_broker/broker_client.cc b/sandbox/linux/syscall_broker/broker_client.cc
new file mode 100644
index 0000000000..760cf59b3c
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_client.cc
@@ -0,0 +1,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