| // 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 |