| // Copyright (c) 2012 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 "cros-disks/sandboxed_process.h" |
| |
| #include <utility> |
| |
| #include <stdlib.h> |
| |
| #include <sys/mount.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| |
| #include <base/bind.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_file.h> |
| #include <base/logging.h> |
| #include <base/posix/safe_strerror.h> |
| #include <chromeos/libminijail.h> |
| |
| #include "cros-disks/mount_options.h" |
| #include "cros-disks/quote.h" |
| #include "cros-disks/sandboxed_init.h" |
| |
| namespace cros_disks { |
| namespace { |
| |
| int Exec(char* const args[]) { |
| const char* const path = args[0]; |
| execv(path, args); |
| const int ret = |
| (errno == ENOENT ? MINIJAIL_ERR_NO_COMMAND : MINIJAIL_ERR_NO_ACCESS); |
| PLOG(ERROR) << "Cannot exec " << quote(path); |
| return ret; |
| } |
| |
| } // namespace |
| |
| SandboxedProcess::SandboxedProcess() : jail_(minijail_new()) { |
| CHECK(jail_) << "Failed to create a process jail"; |
| } |
| |
| SandboxedProcess::~SandboxedProcess() { |
| minijail_destroy(jail_); |
| } |
| |
| void SandboxedProcess::LoadSeccompFilterPolicy(const std::string& policy_file) { |
| minijail_parse_seccomp_filters(jail_, policy_file.c_str()); |
| minijail_use_seccomp_filter(jail_); |
| } |
| |
| void SandboxedProcess::NewCgroupNamespace() { |
| minijail_namespace_cgroups(jail_); |
| } |
| |
| void SandboxedProcess::NewIpcNamespace() { |
| minijail_namespace_ipc(jail_); |
| } |
| |
| void SandboxedProcess::NewMountNamespace() { |
| minijail_namespace_vfs(jail_); |
| } |
| |
| void SandboxedProcess::EnterExistingMountNamespace(const std::string& ns_path) { |
| minijail_namespace_enter_vfs(jail_, ns_path.c_str()); |
| } |
| |
| void SandboxedProcess::NewPidNamespace() { |
| minijail_namespace_pids(jail_); |
| minijail_run_as_init(jail_); |
| minijail_reset_signal_mask(jail_); |
| minijail_reset_signal_handlers(jail_); |
| run_custom_init_ = true; |
| } |
| |
| bool SandboxedProcess::SetUpMinimalMounts() { |
| if (minijail_bind(jail_, "/", "/", 0)) |
| return false; |
| if (minijail_bind(jail_, "/proc", "/proc", 0)) |
| return false; |
| minijail_remount_proc_readonly(jail_); |
| minijail_mount_tmp(jail_); |
| |
| // Create a minimal /dev with a very restricted set of device nodes. |
| minijail_mount_dev(jail_); |
| if (minijail_bind(jail_, "/dev/log", "/dev/log", 0)) |
| return false; |
| return true; |
| } |
| |
| bool SandboxedProcess::BindMount(const std::string& from, |
| const std::string& to, |
| bool writeable, |
| bool recursive) { |
| MountOptions::Flags flags = MS_BIND; |
| if (!writeable) { |
| flags |= MS_RDONLY; |
| } |
| if (recursive) { |
| flags |= MS_REC; |
| } |
| return minijail_mount(jail_, from.c_str(), to.c_str(), "", flags) == 0; |
| } |
| |
| bool SandboxedProcess::Mount(const std::string& src, |
| const std::string& to, |
| const std::string& type, |
| const char* data) { |
| return minijail_mount_with_data(jail_, src.c_str(), to.c_str(), type.c_str(), |
| 0, data) == 0; |
| } |
| |
| bool SandboxedProcess::EnterPivotRoot() { |
| return minijail_enter_pivot_root(jail_, "/mnt/empty") == 0; |
| } |
| |
| void SandboxedProcess::NewNetworkNamespace() { |
| minijail_namespace_net(jail_); |
| } |
| |
| void SandboxedProcess::SkipRemountPrivate() { |
| minijail_skip_remount_private(jail_); |
| } |
| |
| void SandboxedProcess::SetNoNewPrivileges() { |
| minijail_no_new_privs(jail_); |
| } |
| |
| void SandboxedProcess::SetCapabilities(uint64_t capabilities) { |
| minijail_use_caps(jail_, capabilities); |
| } |
| |
| void SandboxedProcess::SetGroupId(gid_t group_id) { |
| minijail_change_gid(jail_, group_id); |
| } |
| |
| void SandboxedProcess::SetUserId(uid_t user_id) { |
| minijail_change_uid(jail_, user_id); |
| } |
| |
| void SandboxedProcess::SetSupplementaryGroupIds(base::span<const gid_t> gids) { |
| minijail_set_supplementary_gids(jail_, gids.size(), gids.data()); |
| } |
| |
| void SandboxedProcess::CloseOpenFds() { |
| minijail_close_open_fds(jail_); |
| } |
| |
| bool SandboxedProcess::PreserveFile(const base::File& file) { |
| return minijail_preserve_fd(jail_, file.GetPlatformFile(), |
| file.GetPlatformFile()) == 0; |
| } |
| |
| pid_t SandboxedProcess::StartImpl(base::ScopedFD in_fd, |
| base::ScopedFD out_fd, |
| base::ScopedFD err_fd) { |
| char* const* const args = GetArguments(); |
| DCHECK(args && args[0]); |
| |
| pid_t child_pid = kInvalidProcessId; |
| |
| if (!run_custom_init_) { |
| minijail_preserve_fd(jail_, in_fd.get(), STDIN_FILENO); |
| minijail_preserve_fd(jail_, out_fd.get(), STDOUT_FILENO); |
| minijail_preserve_fd(jail_, err_fd.get(), STDERR_FILENO); |
| |
| const int ret = minijail_run_pid(jail_, args[0], args, &child_pid); |
| if (ret < 0) { |
| LOG(ERROR) << "Cannot run minijail_run_pid_pipes: " |
| << base::safe_strerror(-ret); |
| return kInvalidProcessId; |
| } |
| } else { |
| SandboxedInit init(std::move(in_fd), std::move(out_fd), std::move(err_fd), |
| SubprocessPipe::Open(SubprocessPipe::kChildToParent, |
| &custom_init_control_fd_)); |
| |
| // Create child process. |
| child_pid = minijail_fork(jail_); |
| if (child_pid < 0) { |
| LOG(ERROR) << "Cannot run minijail_fork: " |
| << base::safe_strerror(-child_pid); |
| return kInvalidProcessId; |
| } |
| |
| if (child_pid == 0) { |
| // In child process. |
| init.RunInsideSandboxNoReturn(base::BindOnce(Exec, args)); |
| NOTREACHED(); |
| } else { |
| // In parent process. |
| CHECK(base::SetNonBlocking(custom_init_control_fd_.get())); |
| } |
| } |
| |
| return child_pid; |
| } |
| |
| int SandboxedProcess::WaitImpl() { |
| while (true) { |
| const int status = minijail_wait(jail_); |
| if (status >= 0) { |
| return status; |
| } |
| |
| const int err = -status; |
| if (err != EINTR) { |
| LOG(ERROR) << "Cannot wait for process " << pid() << ": " |
| << base::safe_strerror(err); |
| return MINIJAIL_ERR_INIT; |
| } |
| } |
| } |
| |
| int SandboxedProcess::WaitNonBlockingImpl() { |
| int exit_code; |
| |
| if (run_custom_init_ && |
| SandboxedInit::PollLauncherStatus(&custom_init_control_fd_, &exit_code)) { |
| return exit_code; |
| } |
| |
| // TODO(chromium:971667) Use Minijail's non-blocking wait once it exists. |
| int wstatus; |
| const pid_t child_pid = pid(); |
| const int ret = waitpid(child_pid, &wstatus, WNOHANG); |
| if (ret < 0) { |
| PLOG(ERROR) << "Cannot wait for process " << child_pid; |
| return MINIJAIL_ERR_INIT; |
| } |
| |
| if (ret == 0) { |
| // Process is still running. |
| return -1; |
| } |
| |
| return SandboxedInit::WStatusToStatus(wstatus); |
| } |
| |
| } // namespace cros_disks |