blob: c7d0b45cc02e005c5f9681fc05ec268db2804ae1 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device_management/client/client.h"
#include "device_management/common/print_device_management_interface_proto.h"
#include "device_management/proto_bindings/device_management_interface.pb.h"
#include "device_management-client/device_management/dbus-proxies.h"
#include <utility>
namespace device_management {
namespace switches {
constexpr char kAttrNameSwitch[] = "name";
constexpr char kAttrValueSwitch[] = "value";
constexpr char kDevKeyHashSwitch[] = "developer_key_hash";
constexpr char kFlagsSwitch[] = "flags";
constexpr char kOutputFormatSwitch[] = "output-format";
constexpr struct {
const char* name;
const OutputFormat format;
} kOutputFormats[] = {{"default", OutputFormat::kDefault},
{"binary-protobuf", OutputFormat::kBinaryProtobuf}};
} // namespace switches
namespace {
// Converts a brillo::Error* to string for printing.
std::string BrilloErrorToString(brillo::Error* err) {
std::string result;
if (err) {
result = "(" + err->GetDomain() + ", " + err->GetCode() + ", " +
err->GetMessage() + ")";
} else {
result = "(null)";
}
return result;
}
bool GetAttrName(std::unique_ptr<Printer>& printer,
const base::CommandLine* cl,
std::string* name_out) {
*name_out = cl->GetSwitchValueASCII(switches::kAttrNameSwitch);
if (name_out->length() == 0) {
printer->PrintHumanOutput(
"No install attribute name specified (--name=<name>)\n");
return false;
}
return true;
}
bool GetAttrValue(std::unique_ptr<Printer>& printer,
const base::CommandLine* cl,
std::string* value_out) {
*value_out = cl->GetSwitchValueASCII(switches::kAttrValueSwitch);
if (value_out->length() == 0) {
printer->PrintHumanOutput(
"No install attribute value specified (--value=<value>)\n");
return false;
}
return true;
}
} // namespace
DeviceManagementClient::DeviceManagementClient(
std::unique_ptr<org::chromium::DeviceManagementProxy>
device_management_proxy,
scoped_refptr<dbus::Bus> bus)
: device_management_proxy_(std::move(device_management_proxy)), bus_(bus) {}
DeviceManagementClient::~DeviceManagementClient() {
bus_->ShutdownAndBlock();
}
std::unique_ptr<DeviceManagementClient>
DeviceManagementClient::CreateDeviceManagementClient() {
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
if (!bus->Connect()) {
LOG(ERROR) << "D-Bus system bus is not ready";
return nullptr;
}
auto device_management_proxy =
std::make_unique<org::chromium::DeviceManagementProxy>(bus);
return std::unique_ptr<DeviceManagementClient>(
new DeviceManagementClient(std::move(device_management_proxy), bus));
}
bool DeviceManagementClient::InitializePrinter(const base::CommandLine* cl) {
// Use output format to construct a printer. We process this argument first
// so that we can use the resulting printer for outputting errors when
// processing any of the other arguments.
OutputFormat output_format = OutputFormat::kDefault;
if (cl->HasSwitch(switches::kOutputFormatSwitch)) {
std::string output_format_str =
cl->GetSwitchValueASCII(switches::kOutputFormatSwitch);
std::optional<OutputFormat> found_output_format;
for (const auto& value : switches::kOutputFormats) {
if (output_format_str == value.name) {
found_output_format = value.format;
break;
}
}
if (found_output_format) {
output_format = *found_output_format;
} else {
// Do manual output here because we don't have a working printer.
std::cerr << "Invalid output format: " << output_format_str << std::endl;
return false;
}
}
printer_ = std::make_unique<Printer>(output_format);
return true;
}
bool DeviceManagementClient::IsInstallAttributesReady() {
device_management::InstallAttributesGetStatusRequest status_request;
device_management::InstallAttributesGetStatusReply status_reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->InstallAttributesGetStatus(
status_request, &status_reply, &error, timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesGetStatus call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
if (status_reply.state() ==
device_management::InstallAttributesState::UNKNOWN ||
status_reply.state() ==
device_management::InstallAttributesState::TPM_NOT_OWNED) {
printer_->PrintHumanOutput("InstallAttributes() is not ready.\n");
return false;
}
return true;
}
bool DeviceManagementClient::GetInstallAttributes(const base::CommandLine* cl) {
std::string name;
if (!GetAttrName(printer_, cl, &name)) {
printer_->PrintHumanOutput("No attribute name specified.\n");
return false;
}
// Make sure install attributes are ready.
if (!IsInstallAttributesReady()) {
return false;
}
device_management::InstallAttributesGetRequest request;
device_management::InstallAttributesGetReply reply;
brillo::ErrorPtr error;
request.set_name(name);
error.reset();
if (!device_management_proxy_->InstallAttributesGet(request, &reply, &error,
timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesGet call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
if (reply.error() == device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintFormattedHumanOutput("%s\n", reply.value().c_str());
} else {
return false;
}
return true;
}
bool DeviceManagementClient::SetInstallAttributes(const base::CommandLine* cl) {
std::string name;
if (!GetAttrName(printer_, cl, &name)) {
printer_->PrintHumanOutput("No attribute name specified.\n");
return false;
}
std::string value;
if (!GetAttrValue(printer_, cl, &value)) {
printer_->PrintHumanOutput("No attribute value specified.\n");
return false;
}
// Make sure install attributes are ready.
if (!IsInstallAttributesReady()) {
return false;
}
device_management::InstallAttributesSetRequest req;
device_management::InstallAttributesSetReply reply;
brillo::ErrorPtr error;
req.set_name(name);
// It is expected that a null terminator is part of the value.
value.push_back('\0');
req.set_value(value);
error.reset();
if (!device_management_proxy_->InstallAttributesSet(req, &reply, &error,
timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesSet call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintHumanOutput("Call to InstallAttributesSet() failed.\n");
return false;
}
return true;
}
bool DeviceManagementClient::FinalizeInstallAttributes() {
// Make sure install attributes are ready.
if (!IsInstallAttributesReady()) {
return false;
}
device_management::InstallAttributesFinalizeRequest req;
device_management::InstallAttributesFinalizeReply reply;
brillo::ErrorPtr error;
error.reset();
if (!device_management_proxy_->InstallAttributesFinalize(req, &reply, &error,
timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesFinalize() failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
bool result = reply.error() == device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET;
printer_->PrintFormattedHumanOutput("InstallAttributesFinalize(): %d\n",
static_cast<int>(result));
return true;
}
bool DeviceManagementClient::GetStatusInstallAttributes() {
device_management::InstallAttributesGetStatusRequest request;
device_management::InstallAttributesGetStatusReply reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->InstallAttributesGetStatus(
request, &reply, &error, timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesGetStatus() call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintHumanOutput(
"Call to InstallAttributesGetStatus() failed.\n");
return false;
}
printer_->PrintFormattedHumanOutput(
"%s\n", InstallAttributesState_Name(reply.state()).c_str());
return true;
}
bool DeviceManagementClient::GetCountInstallAttributes() {
device_management::InstallAttributesGetStatusRequest req;
device_management::InstallAttributesGetStatusReply reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->InstallAttributesGetStatus(req, &reply, &error,
timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesGetStatus() call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintHumanOutput(
"Call to InstallAttributesGetStatus() failed.\n");
return false;
}
printer_->PrintFormattedHumanOutput("InstallAttributesCount(): %d\n",
reply.count());
return true;
}
bool DeviceManagementClient::IsReadyInstallAttributes() {
device_management::InstallAttributesGetStatusRequest req;
device_management::InstallAttributesGetStatusReply reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->InstallAttributesGetStatus(req, &reply, &error,
timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesGetStatus() call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintHumanOutput(
"Call to InstallAttributesGetStatus() failed.\n");
return false;
}
bool result =
(reply.state() != device_management::InstallAttributesState::UNKNOWN &&
reply.state() !=
device_management::InstallAttributesState::TPM_NOT_OWNED);
printer_->PrintFormattedHumanOutput("InstallAttributesIsReady(): %d\n",
static_cast<int>(result));
return true;
}
bool DeviceManagementClient::IsSecureInstallAttributes() {
device_management::InstallAttributesGetStatusRequest req;
device_management::InstallAttributesGetStatusReply reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->InstallAttributesGetStatus(req, &reply, &error,
timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesGetStatus() call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintHumanOutput(
"Call to InstallAttributesGetStatus() failed.\n");
return false;
}
bool result = reply.is_secure();
printer_->PrintFormattedHumanOutput("InstallAttributesIsSecure(): %d\n",
static_cast<int>(result));
return true;
}
bool DeviceManagementClient::IsInvalidInstallAttributes() {
device_management::InstallAttributesGetStatusRequest req;
device_management::InstallAttributesGetStatusReply reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->InstallAttributesGetStatus(req, &reply, &error,
timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesGetStatus() call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintHumanOutput(
"Call to InstallAttributesGetStatus() failed.\n");
return false;
}
bool result =
(reply.state() == device_management::InstallAttributesState::INVALID);
printer_->PrintFormattedHumanOutput("InstallAttributesIsInvalid(): %d\n",
static_cast<int>(result));
return true;
}
bool DeviceManagementClient::IsFirstInstallInstallAttributes() {
device_management::InstallAttributesGetStatusRequest req;
device_management::InstallAttributesGetStatusReply reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->InstallAttributesGetStatus(req, &reply, &error,
timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"InstallAttributesGetStatus() call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintHumanOutput(
"Call to InstallAttributesGetStatus() failed.\n");
return false;
}
bool result = (reply.state() ==
device_management::InstallAttributesState::FIRST_INSTALL);
printer_->PrintFormattedHumanOutput("InstallAttributesIsFirstInstall(): %d\n",
static_cast<int>(result));
return true;
}
bool DeviceManagementClient::GetEnterpriseOwnedStatus() {
device_management::EnterpriseOwnedGetStatusRequest req;
device_management::EnterpriseOwnedGetStatusReply reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->EnterpriseOwnedGetStatus(req, &reply, &error,
timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"EnterpriseOwnedGetStatus() call failed: %s.\n",
BrilloErrorToString(error.get()).c_str());
return false;
}
bool result =
(reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_ENTERPRISED_OWNED);
printer_->PrintFormattedHumanOutput("EnterpriseOwnedGetStatus(): %d\n",
static_cast<int>(result));
return true;
}
bool DeviceManagementClient::GetFWMP() {
base::ElapsedTimer timer;
device_management::GetFirmwareManagementParametersRequest request;
device_management::GetFirmwareManagementParametersReply reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->GetFirmwareManagementParameters(
request, &reply, &error, timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"Failed to call GetFirmwareManagementParameters: %s\n",
BrilloErrorToString(error.get()).c_str());
return false;
} else {
printer_->PrintReplyProtobuf(reply);
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintFormattedHumanOutput(
"Failed to call GetFirmwareManagementParameters: status %d\n",
static_cast<int>(reply.error()));
return false;
}
}
printer_->PrintFormattedHumanOutput("flags=0x%08x\n", reply.fwmp().flags());
brillo::Blob hash = brillo::BlobFromString(reply.fwmp().developer_key_hash());
printer_->PrintFormattedHumanOutput(
"hash=%s\n", hwsec_foundation::BlobToHex(hash).c_str());
printer_->PrintHumanOutput("GetFirmwareManagementParameters success.\n");
return true;
}
bool DeviceManagementClient::SetFWMP(const base::CommandLine* cl) {
base::ElapsedTimer timer;
device_management::SetFirmwareManagementParametersRequest request;
device_management::SetFirmwareManagementParametersReply reply;
if (cl->HasSwitch(switches::kFlagsSwitch)) {
std::string flags_str = cl->GetSwitchValueASCII(switches::kFlagsSwitch);
char* end = NULL;
int32_t flags = strtol(flags_str.c_str(), &end, 0);
if (end && *end != '\0') {
printer_->PrintHumanOutput("Bad flags value.\n");
return false;
}
request.mutable_fwmp()->set_flags(flags);
} else {
printer_->PrintHumanOutput(
"Use --flags (and optionally --developer_key_hash).\n");
return false;
}
if (cl->HasSwitch(switches::kDevKeyHashSwitch)) {
std::string hash_str = cl->GetSwitchValueASCII(switches::kDevKeyHashSwitch);
brillo::Blob hash;
if (!base::HexStringToBytes(hash_str, &hash)) {
printer_->PrintHumanOutput("Bad hash value.\n");
return false;
}
if (hash.size() != SHA256_DIGEST_LENGTH) {
printer_->PrintHumanOutput("Bad hash size.\n");
return false;
}
request.mutable_fwmp()->set_developer_key_hash(brillo::BlobToString(hash));
}
brillo::ErrorPtr error;
if (!device_management_proxy_->SetFirmwareManagementParameters(
request, &reply, &error, timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"Failed to call SetFirmwareManagementParameters: %s\n",
BrilloErrorToString(error.get()).c_str());
return false;
} else {
printer_->PrintReplyProtobuf(reply);
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintFormattedHumanOutput(
"Failed to call SetFirmwareManagementParameters: status %d\n",
static_cast<int>(reply.error()));
return false;
}
}
printer_->PrintHumanOutput("SetFirmwareManagementParameters success.\n");
return true;
}
bool DeviceManagementClient::RemoveFWMP() {
base::ElapsedTimer timer;
device_management::RemoveFirmwareManagementParametersRequest request;
device_management::RemoveFirmwareManagementParametersReply reply;
brillo::ErrorPtr error;
if (!device_management_proxy_->RemoveFirmwareManagementParameters(
request, &reply, &error, timeout_ms) ||
error) {
printer_->PrintFormattedHumanOutput(
"Failed to call RemoveFirmwareManagementParameters: %s\n",
BrilloErrorToString(error.get()).c_str());
return false;
} else {
printer_->PrintReplyProtobuf(reply);
if (reply.error() != device_management::DeviceManagementErrorCode::
DEVICE_MANAGEMENT_ERROR_NOT_SET) {
printer_->PrintFormattedHumanOutput(
"Failed to call RemoveFirmwareManagementParameters: status %d\n",
static_cast<int>(reply.error()));
return false;
}
}
printer_->PrintHumanOutput("RemoveFirmwareManagementParameters success.\n");
return true;
}
} // namespace device_management