blob: 16f91a6026895a4a4ada2b46955d64cecc0adec6 [file] [log] [blame]
// Copyright 2020 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 "diagnostics/cros_healthd/minijail/minijail_configuration.h"
#include <sys/mount.h>
#include <string>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <libminijail.h>
#include <scoped_minijail.h>
namespace diagnostics {
namespace {
// User and group to run as.
constexpr char kCrosHealthdUserName[] = "cros_healthd";
constexpr char kCrosHealthdGroupName[] = "cros_healthd";
// Path to the SECCOMP filter to apply.
constexpr char kSeccompFilterPath[] =
"/usr/share/policy/cros_healthd-seccomp.policy";
// Checks to see if |file_path| exists on the device. If it does, it will be
// bind-mounted inside |jail| at the same path it exists outside the minijail,
// and it will not be writeable from inside |jail|.
void BindMountIfPathExists(struct minijail* jail,
const base::FilePath& file_path) {
if (!base::PathExists(file_path))
return;
const char* path_string = file_path.value().c_str();
minijail_bind(jail, path_string, path_string, 0);
}
} // namespace
void ConfigureAndEnterMinijail() {
ScopedMinijail jail(minijail_new());
minijail_no_new_privs(jail.get()); // The no_new_privs bit.
minijail_remount_proc_readonly(jail.get()); // Remount /proc readonly.
minijail_namespace_ipc(jail.get()); // New IPC namespace.
minijail_namespace_net(jail.get()); // New network namespace.
minijail_namespace_uts(jail.get()); // New UTS namespace.
minijail_namespace_vfs(jail.get()); // New VFS namespace.
minijail_mount_tmp(jail.get()); // Mount new tmpfs.
minijail_enter_pivot_root(jail.get(),
"/mnt/empty"); // Set /mnt/empty as rootfs.
// Bind-mount /, /dev and /proc. /dev is necessary to send ioctls to the
// system's block devices.
minijail_bind(jail.get(), "/", "/", 0);
minijail_bind(jail.get(), "/dev", "/dev", 0);
minijail_bind(jail.get(), "/proc", "/proc", 0);
// Create a new tmpfs filesystem for /run and mount necessary files.
minijail_mount_with_data(jail.get(), "tmpfs", "/run", "tmpfs", 0, "");
minijail_bind(jail.get(), "/run/dbus", "/run/dbus",
0); // Shared socket file for talking to the D-Bus daemon.
minijail_bind(jail.get(), "/run/systemd/journal", "/run/systemd/journal",
0); // Needed for logging.
minijail_bind(jail.get(), "/run/chromeos-config/v1",
"/run/chromeos-config/v1",
0); // Needed for access to chromeos-config.
// Create a new tmpfs filesystem for /sys and mount necessary files.
minijail_mount_with_data(jail.get(), "tmpfs", "/sys", "tmpfs", 0, "");
minijail_bind(jail.get(), "/sys/block", "/sys/block",
0); // Files related to the system's block devices.
minijail_bind(jail.get(), "/sys/devices", "/sys/devices",
0); // Needed to get the names of the block device dev nodes.
minijail_bind(
jail.get(), "/sys/devices/system/cpu", "/sys/devices/system/cpu",
0); // Used by the stressapptest diagnostic. TODO: Do we need this?
// The following sysfs paths don't exist on every device, so test for their
// existence and bind-mount them if they do exist.
BindMountIfPathExists(
jail.get(),
base::FilePath("/sys/class/backlight")); // Files related to the system's
// backlights.
BindMountIfPathExists(
jail.get(),
base::FilePath("/sys/class/chromeos")); // Files related to Chrome OS
// hardware devices.
BindMountIfPathExists(
jail.get(),
base::FilePath("/sys/class/hwmon")); // Files related to Chrome OS
// hardware monitors.
BindMountIfPathExists(
jail.get(),
base::FilePath("/sys/class/power_supply")); // Files related to the
// system's power supplies.
BindMountIfPathExists(
jail.get(),
base::FilePath("/sys/firmware/vpd/ro")); // Files with R/O cached VPD.
BindMountIfPathExists(
jail.get(),
base::FilePath("/sys/firmware/vpd/rw")); // Files with R/W cached VPD.
BindMountIfPathExists(
jail.get(),
base::FilePath("/sys/class/dmi/id")); // Files related to the
// system's DMI information.
// Create a new tmpfs filesystem for /var and mount necessary files.
minijail_mount_with_data(jail.get(), "tmpfs", "/var", "tmpfs", 0, "");
minijail_bind(jail.get(), "/var/lib/timezone", "/var/lib/timezone",
0); // Symlink for reading the timezone file.
minijail_bind(jail.get(), "/var/cache/diagnostics", "/var/cache/diagnostics",
1); // Diagnostics can create test files in this directory.
// Bind-mount other necessary files.
minijail_bind(
jail.get(), "/dev/shm", "/dev/shm",
1); // Allows creation of shared memory files that are used to set up
// mojo::ScopedHandles which can be returned by GetRoutineUpdate.
minijail_bind(jail.get(), "/mnt/stateful_partition",
"/mnt/stateful_partition",
0); // Needed by the StatefulPartition probe.
minijail_bind(jail.get(), "/usr/share/zoneinfo", "/usr/share/zoneinfo",
0); // Directory holding timezone files.
// Run as the cros_healthd user and group. Inherit supplementary groups to
// allow cros_healthd access to disk files.
CHECK_EQ(0, minijail_change_user(jail.get(), kCrosHealthdUserName));
CHECK_EQ(0, minijail_change_group(jail.get(), kCrosHealthdGroupName));
minijail_inherit_usergroups(jail.get());
// Apply SECCOMP filtering.
minijail_use_seccomp_filter(jail.get());
minijail_parse_seccomp_filters(jail.get(), kSeccompFilterPath);
minijail_enter(jail.get());
}
void NewMountNamespace() {
ScopedMinijail j(minijail_new());
// Create a minimalistic mount namespace with just the bare minimum required.
minijail_namespace_vfs(j.get());
minijail_mount_tmp(j.get());
if (minijail_enter_pivot_root(j.get(), "/mnt/empty"))
LOG(FATAL) << "minijail_enter_pivot_root() failed";
minijail_bind(j.get(), "/", "/", 0);
if (minijail_mount_with_data(j.get(), "none", "/proc", "proc",
MS_NOSUID | MS_NOEXEC | MS_NODEV, nullptr)) {
LOG(FATAL) << "minijail_mount_with_data(\"/proc\") failed";
}
if (minijail_mount_with_data(j.get(), "tmpfs", "/run", "tmpfs",
MS_NOSUID | MS_NOEXEC | MS_NODEV, nullptr)) {
LOG(FATAL) << "minijail_mount_with_data(\"/run\") failed";
}
// Mount /run/systemd/journal to be able to log to journald.
if (minijail_bind(j.get(), "/run/systemd/journal", "/run/systemd/journal", 0))
LOG(FATAL) << "minijail_bind(\"/run/systemd/journal\") failed";
if (minijail_mount_with_data(j.get(), "/dev", "/dev", "bind",
MS_BIND | MS_REC, nullptr)) {
LOG(FATAL) << "minijail_mount_with_data(\"/dev\") failed";
}
minijail_enter(j.get());
}
} // namespace diagnostics