| // 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 "imageloader/helper_process_receiver.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <fcntl.h> |
| #include <libminijail.h> |
| #include <scoped_minijail.h> |
| #include <sys/capability.h> |
| #include <sys/socket.h> |
| #include <unistd.h> |
| |
| #include <base/bind.h> |
| #include <base/files/file_path.h> |
| #include <base/logging.h> |
| #include <base/posix/eintr_wrapper.h> |
| #include <base/time/time.h> |
| |
| #include "imageloader/imageloader.h" |
| #include "imageloader/verity_mounter.h" |
| |
| namespace imageloader { |
| |
| namespace { |
| constexpr char kSeccompFilterPath[] = |
| "/usr/share/policy/imageloader-helper-seccomp.policy"; |
| } // namespace |
| |
| HelperProcessReceiver::HelperProcessReceiver(base::ScopedFD control_fd) |
| : control_fd_(std::move(control_fd)), |
| pending_fd_(-1), |
| mounter_() {} |
| |
| int HelperProcessReceiver::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. |
| ScopedMinijail jail(minijail_new()); |
| minijail_no_new_privs(jail.get()); |
| minijail_use_seccomp_filter(jail.get()); |
| minijail_parse_seccomp_filters(jail.get(), kSeccompFilterPath); |
| minijail_reset_signal_mask(jail.get()); |
| minijail_namespace_net(jail.get()); |
| minijail_skip_remount_private(jail.get()); |
| minijail_enter(jail.get()); |
| |
| controller_ = base::FileDescriptorWatcher::WatchReadable( |
| control_fd_.get(), |
| base::BindRepeating(&HelperProcessReceiver::OnCommandReady, |
| base::Unretained(this))); |
| return Daemon::OnInit(); |
| } |
| |
| void HelperProcessReceiver::OnCommandReady() { |
| std::vector<char> buffer(4096 * 4); |
| |
| struct msghdr msg = {0}; |
| struct iovec iov[1]; |
| |
| iov[0].iov_base = buffer.data(); |
| iov[0].iov_len = buffer.size(); |
| |
| 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(control_fd_.get(), &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); |
| |
| ImageCommand command; |
| if (!command.ParseFromArray(buffer.data(), bytes)) |
| LOG(FATAL) << "error parsing protobuf"; |
| |
| // Handle the command to mount the image. |
| CommandResponse response = HandleCommand(command, cmsg); |
| // Reply to the parent process with the success or failure. |
| SendResponse(response); |
| } |
| |
| CommandResponse HelperProcessReceiver::HandleCommand( |
| const ImageCommand& image_command, struct cmsghdr* cmsg) { |
| CommandResponse response; |
| if (image_command.has_mount_command()) { |
| MountCommand command = image_command.mount_command(); |
| 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_)); |
| |
| // Convert the fs type to a string. |
| std::string fs_type; |
| switch (command.fs_type()) { |
| case MountCommand::EXT4: |
| fs_type = "ext4"; |
| break; |
| case MountCommand::SQUASH: |
| fs_type = "squashfs"; |
| break; |
| default: |
| LOG(FATAL) << "unknown filesystem type"; |
| } |
| |
| bool status = mounter_.Mount(base::ScopedFD(pending_fd_), |
| base::FilePath(command.mount_path()), fs_type, |
| command.table()); |
| if (!status) |
| LOG(ERROR) << "mount failed"; |
| |
| response.set_success(status); |
| } else if (image_command.has_unmount_all_command()) { |
| UnmountAllCommand command = image_command.unmount_all_command(); |
| std::vector<base::FilePath> paths; |
| const base::FilePath root_dir(command.unmount_rootpath()); |
| response.set_success( |
| mounter_.CleanupAll(command.dry_run(), root_dir, &paths)); |
| for (const auto& path : paths) { |
| const std::string path_(path.value()); |
| response.add_paths(path_); |
| } |
| } else if (image_command.has_unmount_command()) { |
| UnmountCommand command = image_command.unmount_command(); |
| const base::FilePath path(command.unmount_path()); |
| response.set_success(mounter_.Cleanup(path)); |
| } else { |
| LOG(FATAL) << "unknown operations"; |
| } |
| return response; |
| } |
| |
| void HelperProcessReceiver::SendResponse(const CommandResponse& response) { |
| std::string response_str; |
| if (!response.SerializeToString(&response_str)) |
| LOG(FATAL) << "failed to serialize protobuf"; |
| |
| if (HANDLE_EINTR( |
| write(control_fd_.get(), response_str.data(), response_str.size())) != |
| static_cast<ssize_t>(response_str.size())) { |
| PLOG(FATAL) << "short write on protobuf"; |
| } |
| } |
| |
| } // namespace imageloader |