blob: edfb55b5df54bc98f0ebbfae5f33cc14b943cb66 [file] [log] [blame]
// Copyright 2018 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 <base/logging.h>
#include <base/strings/string_util.h>
#include "init/usermode-helper.h"
namespace usermode_helper {
namespace {
bool IsTestCaseFlag(const char* arg) {
return std::string_view(arg) == "--core2md_failure" ||
std::string_view(arg) == "--directory_failure";
}
// Processes core dumps from the kernel when a crash is detected.
// Controlled via /proc/sys/kernel/core_pattern.
bool ValidateCrashReporter(int argc, const char* argv[]) {
// Allow normal crash reporting. Most invocations will be this.
if (argc == 2 &&
base::StartsWith(argv[1], "--user=", base::CompareCase::SENSITIVE)) {
return true;
}
// Allow early crash reporting invocation.
if (argc == 4 && std::string_view(argv[1]) == "--early" &&
std::string_view(argv[2]) == "--log_to_stderr" &&
base::StartsWith(argv[3], "--user=", base::CompareCase::SENSITIVE)) {
return true;
}
// Allow for crash-reporter crash testing invocations.
if (argc == 3 &&
base::StartsWith(argv[1], "--user=", base::CompareCase::SENSITIVE) &&
std::string_view(argv[2]) == "--crash_test") {
return true;
}
// Allow for log-collection-failure tests. Currently the test is being
// transitioned from autotest to Tast, having 2 different versions.
// TODO(crbug.com/1040335): Remove the former one after removing the
// logging_UserCrash autotest.
if (argc == 3 &&
base::StartsWith(argv[1], "--user=", base::CompareCase::SENSITIVE) &&
IsTestCaseFlag(argv[2])) {
return true;
}
// Disallow all other invocations.
LOG(ERROR) << "crash_reporter: unknown invocation";
return false;
}
// Automatic module loading when kernel code calls request_module().
// Controlled via /proc/sys/kernel/modprobe.
bool ValidateModprobe(int argc, const char* argv[]) {
// The kernel has loaded modules with the form `modprobe -q -- modname` since
// at least linux-2.6.12. We'll enforce that until the kernel changes, but it
// rarely does, so maybe it's fine to be lazy.
if (argc != 4) {
LOG(ERROR) << "modprobe: argc must be 4";
return false;
}
if (strcmp(argv[1], "-q") != 0) {
LOG(ERROR) << "modprobe: argv[1] must be -q";
return false;
}
if (strcmp(argv[2], "--") != 0) {
LOG(ERROR) << "modprobe: argv[2] must be --";
return false;
}
// We allow the last arg to be anything since the -- marker told modprobe to
// parse it exactly as a module name and not an option.
return true;
}
// When kernel code poweroffs the system by calling orderly_poweroff().
// This is not related to userspace calling `poweroff` or uses the reboot
// syscall.
// Controlled via /proc/sys/kernel/poweroff_cmd.
bool ValidatePoweroff(int argc, const char* argv[]) {
if (argc != 1) {
LOG(ERROR) << "poweroff: argc must be 1";
return false;
}
return true;
}
// When kernel code reboots the system by calling orderly_reboot().
// This is not related to userspace calling `reboot` or uses the reboot
// syscall.
bool ValidateReboot(int argc, const char* argv[]) {
if (argc != 1) {
LOG(ERROR) << "reboot: argc must be 1";
return false;
}
return true;
}
// When the kernel needs access to a key as part of the kernel keyring.
bool ValidateRequestKey(int argc, const char* argv[]) {
// The kernel always executes this as:
// /sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>
if (argc != 8) {
LOG(ERROR) << "request-key: argc must be 8";
return false;
}
// Don't allow any command line options.
for (size_t i = 1; i < argc; ++i) {
const std::string_view arg(argv[i]);
if (arg[0] == '-') {
LOG(ERROR) << "request-key: options not allowed: " << arg;
return false;
}
}
return true;
}
bool ValidateBridgeStp(int argc, const char* argv[]) {
if (argc != 3) {
LOG(ERROR) << "bridge-stp: argc must be 3";
return false;
}
if (strcmp(argv[2], "start") != 0 && strcmp(argv[2], "stop") != 0) {
LOG(ERROR) << "bridge-stp: argv[2] must be start or stop";
return false;
}
return true;
}
} // namespace
// Whether the arguments to the program are permitted.
bool ValidateProgramArgs(int argc, const char* argv[]) {
const std::string_view prog(argv[0]);
if (prog == "/sbin/crash_reporter")
return ValidateCrashReporter(argc, argv);
else if (prog == "/sbin/modprobe")
return ValidateModprobe(argc, argv);
else if (prog == "/sbin/poweroff")
return ValidatePoweroff(argc, argv);
else if (prog == "/sbin/reboot")
return ValidateReboot(argc, argv);
else if (prog == "/sbin/request-key")
return ValidateRequestKey(argc, argv);
else if (prog == "/sbin/bridge-stp")
return ValidateBridgeStp(argc, argv);
LOG(ERROR) << "program not permitted: " << argv[0];
return false;
}
} // namespace usermode_helper