blob: 789f9bf7cace01980d6ada7dff9acc1fa857c683 [file] [log] [blame]
// Copyright 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 "debugd/src/dev_features_tool.h"
#include <functional>
#include <base/bind.h>
#include <base/callback.h>
#include <chromeos/dbus/service_constants.h>
#include "debugd/src/error_utils.h"
#include "debugd/src/process_with_output.h"
namespace debugd {
namespace {
using ArgList = ProcessWithOutput::ArgList;
const char kDefaultRootPassword[] = "test0000";
const char kDevFeaturesErrorString[] = "org.chromium.debugd.error.DevFeatures";
const char kRootfsLockedErrorString[] =
"Rootfs verification must be removed first";
// Executes a helper process with the expectation that any message printed to
// stderr indicates a failure that should be passed back over the D-Bus.
// Returns false if any errors launching the process occur. Returns true
// otherwise, and sets |exit_status| if it isn't null.
bool RunHelper(const std::string& command,
const ArgList& arguments,
bool requires_root,
const std::string* stdin,
int* exit_status,
brillo::ErrorPtr* error) {
std::string stderr;
int result =
ProcessWithOutput::RunHelper(command, arguments, requires_root, stdin,
nullptr, // Don't need stdout.
&stderr, error);
if (!stderr.empty()) {
DEBUGD_ADD_ERROR(error, kDevFeaturesErrorString, stderr.c_str());
return false;
}
if (exit_status)
*exit_status = result;
return true;
}
bool RemoveRootfsVerificationQuery(int* exit_status, brillo::ErrorPtr* error) {
return RunHelper("dev_features_rootfs_verification", ArgList{"-q"},
true, // requires root to check if / is writable by root.
nullptr, // no stdin.
exit_status, error);
}
bool EnableBootFromUsbQuery(int* exit_status, brillo::ErrorPtr* error) {
return RunHelper("dev_features_usb_boot", ArgList{"-q"},
true, // requires root for crossystem queries.
nullptr, // no stdin.
exit_status, error);
}
bool ConfigureSshServerQuery(int* exit_status, brillo::ErrorPtr* error) {
return RunHelper("dev_features_ssh", ArgList{"-q"},
true, // needs root to check for files in 700 folders.
nullptr, // no stdin.
exit_status, error);
}
bool EnableChromeRemoteDebuggingQuery(int* exit_status,
brillo::ErrorPtr* error) {
return RunHelper("dev_features_chrome_remote_debugging", ArgList{"-q"}, false,
nullptr, // no stdin.
exit_status, error);
}
bool SetUserPasswordQuery(const std::string& username,
bool system,
int* exit_status,
brillo::ErrorPtr* error) {
ArgList args{"-q", "--user=" + username};
if (system)
args.push_back("--system");
return RunHelper("dev_features_password", args,
true, // requires root to read either password file.
nullptr, // no stdin.
exit_status, error);
}
} // namespace
bool DevFeaturesTool::RemoveRootfsVerification(brillo::ErrorPtr* error) const {
return RunHelper("dev_features_rootfs_verification", ArgList{},
true, // requires root for make_dev_ssd.sh script.
nullptr, // no stdin.
nullptr, // exit status doesn't matter.
error);
}
bool DevFeaturesTool::EnableBootFromUsb(brillo::ErrorPtr* error) const {
return RunHelper("dev_features_usb_boot", ArgList{},
true, // requires root for enable_dev_usb_boot script.
nullptr, // no stdin.
nullptr, // exit status doesn't matter.
error);
}
bool DevFeaturesTool::ConfigureSshServer(brillo::ErrorPtr* error) const {
// SSH server configuration requires writing to rootfs.
int exit_status;
if (!RemoveRootfsVerificationQuery(&exit_status, error) || exit_status != 0) {
DEBUGD_ADD_ERROR(error, kDevFeaturesErrorString, kRootfsLockedErrorString);
return false;
}
return RunHelper("dev_features_ssh", ArgList{},
true, // requires root to write to rootfs directories.
nullptr, // no stdin.
nullptr, // exit status doesn't matter.
error);
}
bool DevFeaturesTool::EnableChromeRemoteDebugging(
brillo::ErrorPtr* error) const {
int exit_status;
if (!RemoveRootfsVerificationQuery(&exit_status, error) || exit_status != 0) {
DEBUGD_ADD_ERROR(error, kDevFeaturesErrorString, kRootfsLockedErrorString);
return false;
}
return RunHelper("dev_features_chrome_remote_debugging", ArgList{},
true, // requires root to write to rootfs directories.
nullptr, // no stdin.
nullptr, // exit status doesn't matter.
error);
}
bool DevFeaturesTool::SetUserPassword(const std::string& username,
const std::string& password,
brillo::ErrorPtr* error) const {
ArgList args{"--user=" + username};
// Set the devmode password regardless of rootfs verification state.
if (!RunHelper("dev_features_password", args,
true, // requires root to write devmode password file.
&password, // pipe the password through stdin.
nullptr, // exit status doesn't matter.
error)) {
return false;
}
// If rootfs is locked, don't bother setting the system password.
int exit_status;
if (!RemoveRootfsVerificationQuery(&exit_status, error) || exit_status != 0)
return true;
args.push_back("--system");
return RunHelper("dev_features_password", args,
true, // requires root to write system password file.
&password, // pipe the password through stdin.
nullptr, // exit status doesn't matter.
error);
}
bool DevFeaturesTool::EnableChromeDevFeatures(const std::string& root_password,
brillo::ErrorPtr* error) const {
if (!EnableBootFromUsb(error))
return false;
if (!ConfigureSshServer(error))
return false;
return SetUserPassword(
"root", root_password.empty() ? kDefaultRootPassword : root_password,
error);
}
namespace {
struct Query {
// The callback should launch the query program. If launching fails, return
// false and set the error. If it succeeds, put the exit status in the
// integer out-argument.
using Function = base::Callback<bool(int*, brillo::ErrorPtr*)>;
Function function;
DevFeatureFlag flag;
};
} // namespace
bool DevFeaturesTool::QueryDevFeatures(int32_t* flags,
brillo::ErrorPtr* error) const {
DCHECK(flags);
Query queries[] = {
{base::Bind(&RemoveRootfsVerificationQuery),
DEV_FEATURE_ROOTFS_VERIFICATION_REMOVED},
{base::Bind(&EnableBootFromUsbQuery), DEV_FEATURE_BOOT_FROM_USB_ENABLED},
{base::Bind(&EnableChromeRemoteDebuggingQuery),
DEV_FEATURE_CHROME_REMOTE_DEBUGGING_ENABLED},
{base::Bind(&ConfigureSshServerQuery), DEV_FEATURE_SSH_SERVER_CONFIGURED},
{base::Bind(&SetUserPasswordQuery, "root", /* system = */ false),
DEV_FEATURE_DEV_MODE_ROOT_PASSWORD_SET},
{base::Bind(&SetUserPasswordQuery, "root", /* system = */ true),
DEV_FEATURE_SYSTEM_ROOT_PASSWORD_SET}};
int32_t result_flags = 0;
for (const auto& query : queries) {
int exit_status;
if (!query.function.Run(&exit_status, error)) {
// D-Bus is only set up to handle a single error so exit as soon as we
// hit one.
return false;
}
if (exit_status == 0)
result_flags |= query.flag;
}
*flags = result_flags;
return true;
}
} // namespace debugd