diff options
Diffstat (limited to 'sandbox/linux/syscall_broker/broker_client.cc')
-rw-r--r-- | sandbox/linux/syscall_broker/broker_client.cc | 144 |
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 |