// 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 <stddef.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <utility>
#include "base/logging.h"
#include "base/pickle.h"
#include "base/posix/unix_domain_socket_linux.h"
#include "build/build_config.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_(std::move(ipc_channel)),
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