blob: 764ff47f2c6ac36231228e4fc88db27586926d86 [file] [log] [blame]
// Copyright (c) 2014 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 "login_manager/subprocess.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <memory>
#include <vector>
#include <base/check.h>
#include <base/check_op.h>
#include <base/logging.h>
#include <base/posix/file_descriptor_shuffle.h>
#include <base/process/launch.h>
#include <base/time/time.h>
#include <libminijail.h>
#include <scoped_minijail.h>
#include "login_manager/session_manager_service.h"
#include "login_manager/system_utils.h"
namespace login_manager {
Subprocess::Subprocess(uid_t uid, SystemUtils* system)
: pid_(-1),
desired_uid_(uid),
new_mount_namespace_(false),
system_(system) {}
Subprocess::~Subprocess() {}
void Subprocess::UseNewMountNamespace() {
new_mount_namespace_ = true;
ns_mnt_path_.reset();
}
void Subprocess::EnterExistingMountNamespace(
const base::FilePath& ns_mnt_path) {
ns_mnt_path_ = ns_mnt_path;
new_mount_namespace_ = false;
}
bool Subprocess::ForkAndExec(const std::vector<std::string>& args,
const std::vector<std::string>& env_vars) {
gid_t gid = 0;
std::vector<gid_t> groups;
if (desired_uid_ != 0 &&
!system_->GetGidAndGroups(desired_uid_, &gid, &groups)) {
LOG(ERROR) << "Can't get group info for UID " << desired_uid_;
return false;
}
ScopedMinijail j(minijail_new());
if (desired_uid_ != 0) {
minijail_change_uid(j.get(), desired_uid_);
minijail_change_gid(j.get(), gid);
minijail_set_supplementary_gids(j.get(), groups.size(), groups.data());
minijail_create_session(j.get());
}
minijail_preserve_fd(j.get(), STDIN_FILENO, STDIN_FILENO);
minijail_preserve_fd(j.get(), STDOUT_FILENO, STDOUT_FILENO);
minijail_preserve_fd(j.get(), STDERR_FILENO, STDERR_FILENO);
minijail_close_open_fds(j.get());
// Reset signal handlers in the child since they'll be blocked below.
minijail_reset_signal_mask(j.get());
minijail_reset_signal_handlers(j.get());
if (new_mount_namespace_) {
minijail_namespace_vfs(j.get());
} else if (ns_mnt_path_.has_value()) {
minijail_namespace_enter_vfs(j.get(), ns_mnt_path_.value().value().c_str());
}
if (ns_mnt_path_.has_value() || new_mount_namespace_) {
// Remount all shared mount points as MS_SLAVE to allow shared mount points
// from outside this namespace to propagate in. This is necessary for users
// to be able to access USB drives/SD cards. cros-disks runs in its own
// mount namespace and uses a shared mount point, /media, to make any
// devices it mounts available outside its mount namespace.
minijail_remount_mode(j.get(), MS_SLAVE);
}
// Block all signals before running the child so that we can avoid a race
// in which the child executes configured signal handlers before the default
// handlers are installed. In the parent, we restore original signal blocks
// immediately after SystemUtils::RunInMinijail().
sigset_t filled_sigset, old_sigset;
sigfillset(&filled_sigset);
CHECK_EQ(0, sigprocmask(SIG_SETMASK, &filled_sigset, &old_sigset));
pid_t child_pid = 0;
bool success = system_->RunInMinijail(j, args, env_vars, &child_pid);
CHECK_EQ(0, sigprocmask(SIG_SETMASK, &old_sigset, nullptr));
if (success) {
pid_ = child_pid;
}
return pid_.has_value();
}
void Subprocess::KillEverything(int signal) {
DCHECK(pid_.has_value());
if (system_->kill(-pid_.value(), desired_uid_, signal) == 0)
return;
// If we failed to kill the process group (maybe it doesn't exist yet because
// the forked process hasn't had a chance to call setsid()), just kill the
// child directly. If it hasn't called setsid() yet, then it hasn't called
// setuid() either, so kill it as root instead of as |desired_uid_|.
system_->kill(pid_.value(), 0, signal);
}
void Subprocess::Kill(int signal) {
DCHECK(pid_.has_value());
system_->kill(pid_.value(), desired_uid_, signal);
}
pid_t Subprocess::GetPid() const {
return pid_.value_or(-1);
}
void Subprocess::ClearPid() {
pid_.reset();
}
}; // namespace login_manager