blob: 4f948006c9bb3bf08ec3c44f417a3d1744bdd590 [file] [log] [blame]
// Copyright 2017 The Chromium OS 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 "mount_helper.h"
#include <fcntl.h>
#include <libminijail.h>
#include <sys/capability.h>
#include <sys/socket.h>
#include <unistd.h>
#include <base/files/file_path.h>
#include <base/bind.h>
#include <base/logging.h>
#include <base/time/time.h>
#include "imageloader.h"
#include "ipc.pb.h"
#include "verity_mounter.h"
namespace imageloader {
namespace {
constexpr char kSeccompFilterPath[] =
"/opt/google/imageloader/imageloader-helper-seccomp.policy";
} // namespace
MountHelper::MountHelper(base::ScopedFD control_fd)
: control_fd_(std::move(control_fd)),
control_watcher_(),
pending_fd_(-1),
mounter_() {}
int MountHelper::OnInit() {
// Prevent the main process from sending us any signals.
// errno can be EPERM if the process is already the group leader.
if (setsid() < 0 && errno != EPERM) PLOG(FATAL) << "setsid failed";
// Run with minimal privileges.
struct minijail* jail = minijail_new();
minijail_no_new_privs(jail);
minijail_use_seccomp_filter(jail);
minijail_parse_seccomp_filters(jail, kSeccompFilterPath);
minijail_reset_signal_mask(jail);
minijail_namespace_net(jail);
minijail_skip_remount_private(jail);
minijail_enter(jail);
MessageLoopForIO::current()->WatchFileDescriptor(control_fd_.get(), true,
MessageLoopForIO::WATCH_READ,
&control_watcher_, this);
return Daemon::OnInit();
}
void MountHelper::OnFileCanReadWithoutBlocking(int fd) {
CHECK_EQ(fd, control_fd_.get());
char buffer[4096 * 4];
memset(buffer, '\0', sizeof(buffer));
struct msghdr msg = {0};
struct iovec iov[1];
iov[0].iov_base = buffer;
iov[0].iov_len = sizeof(buffer);
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
char c_buffer[256];
msg.msg_control = c_buffer;
msg.msg_controllen = sizeof(c_buffer);
ssize_t bytes = recvmsg(fd, &msg, 0);
if (bytes < 0) PLOG(FATAL) << "recvmsg failed";
// Per recvmsg(2), the return value will be 0 when the peer has performed an
// orderly shutdown.
if (bytes == 0) _exit(0);
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg == nullptr) LOG(FATAL) << "no cmsg";
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
LOG(FATAL) << "cmsg is wrong type";
memmove(&pending_fd_, CMSG_DATA(cmsg), sizeof(pending_fd_));
MountImage command;
if (!command.ParseFromArray(buffer, strlen(buffer)))
LOG(FATAL) << "error parsing protobuf";
// Handle the command to mount the image.
MountResponse response = HandleCommand(command);
// Reply to the parent process with the success or failure.
SendResponse(response);
}
MountResponse MountHelper::HandleCommand(MountImage& command) {
bool status = mounter_.Mount(base::ScopedFD(pending_fd_),
base::FilePath(command.mount_path()),
command.table());
if (!status) LOG(ERROR) << "mount failed";
MountResponse response;
response.set_success(status);
return response;
}
void MountHelper::SendResponse(MountResponse& response) {
std::string response_str;
if (!response.SerializeToString(&response_str))
LOG(FATAL) << "failed to serialize protobuf";
if (write(control_fd_.get(), response_str.data(), response_str.size()) !=
static_cast<ssize_t>(response_str.size())) {
LOG(FATAL) << "short write on protobuf";
}
}
} // namespace imageloader