blob: d625be82e27882c4fb7a400601b5afd21bba8006 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// clang-format off
#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
#include <Weave/DeviceLayer/ConfigurationManager.h>
#include <Weave/Core/WeaveKeyIds.h>
#include "src/connectivity/weave/adaptation/configuration_manager_impl.h"
#include "src/connectivity/weave/adaptation/group_key_store_impl.h"
#include <Weave/Profiles/security/WeaveApplicationKeys.h>
#include <Weave/Core/WeaveVendorIdentifiers.hpp>
#include <Weave/DeviceLayer/internal/GenericConfigurationManagerImpl.ipp>
// clang-format on
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fit/defer.h>
#include <lib/syslog/cpp/logger.h>
#include <net/ethernet.h>
#include "src/lib/fxl/logging.h"
#include "src/lib/fsl/vmo/file.h"
#include "src/lib/fsl/vmo/strings.h"
#include <zircon/status.h>
#include <sys/stat.h>
#include <unistd.h>
namespace nl {
namespace Weave {
namespace DeviceLayer {
namespace {
using Internal::GroupKeyStoreImpl;
using Internal::WeaveConfigManager;
// Singleton instance of Weave Group Key Store for the Fuchsia.
//
// NOTE: This is declared as a private global variable, rather than a static
// member of ConfigurationManagerImpl, to reduce the number of headers that
// must be included by the application when using the ConfigurationManager API.
//
GroupKeyStoreImpl gGroupKeyStore;
// Store path and keys for static device information.
constexpr char kDeviceInfoStorePath[] = "/config/data/device_info.json";
constexpr char kDeviceInfoConfigKey_DeviceId[] = "device-id";
constexpr char kDeviceInfoConfigKey_DeviceIdPath[] = "device-id-path";
constexpr char kDeviceInfoConfigKey_FirmwareRevision[] = "firmware-revision";
constexpr char kDeviceInfoConfigKey_MfrDeviceCertPath[] = "mfr-device-cert-path";
constexpr char kDeviceInfoConfigKey_ProductId[] = "product-id";
constexpr char kDeviceInfoConfigKey_VendorId[] = "vendor-id";
// Maximum number of chars in hex for a uint64_t
constexpr int kWeaveDeviceIdMaxLength = 16;
// Maximum size of Weave certificate
constexpr int kWeaveCertificateMaxLength = UINT16_MAX;
} // unnamed namespace
/* Singleton instance of the ConfigurationManager implementation object for the Fuchsia. */
ConfigurationManagerImpl ConfigurationManagerImpl::sInstance;
ConfigurationManagerImpl::ConfigurationManagerImpl() : ConfigurationManagerImpl(nullptr) {}
ConfigurationManagerImpl::ConfigurationManagerImpl(std::unique_ptr<sys::ComponentContext> context)
: context_(std::move(context)),
device_info_(WeaveConfigManager::CreateReadOnlyInstance(kDeviceInfoStorePath)) {}
WEAVE_ERROR ConfigurationManagerImpl::_Init() {
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (!context_) {
context_ = sys::ComponentContext::CreateAndServeOutgoingDirectory();
}
FX_CHECK(context_->svc()->Connect(wlan_device_service_.NewRequest()) == ZX_OK)
<< "Failed to connect to wlan service.";
FX_CHECK(context_->svc()->Connect(hwinfo_device_.NewRequest()) == ZX_OK)
<< "Failed to connect to hwinfo device service.";
FX_CHECK(context_->svc()->Connect(weave_factory_data_manager_.NewRequest()) == ZX_OK)
<< "Failed to connect to weave factory data manager service.";
FX_CHECK(context_->svc()->Connect(factory_store_provider_.NewRequest()) == ZX_OK)
<< "Failed to connect to factory store";
err = EnvironmentConfig::Init();
if (err != WEAVE_NO_ERROR) {
return err;
}
err = GetAndStoreHWInfo();
if (err != WEAVE_NO_ERROR && err != WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND) {
return err;
}
err = GetAndStorePairingCode();
if (err != WEAVE_NO_ERROR && err != WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND) {
return err;
}
return WEAVE_NO_ERROR;
}
WEAVE_ERROR ConfigurationManagerImpl::GetAndStoreHWInfo() {
fuchsia::hwinfo::DeviceInfo device_info;
if (ZX_OK == hwinfo_device_->GetInfo(&device_info) && device_info.has_serial_number()) {
return StoreSerialNumber(device_info.serial_number().c_str(),
device_info.serial_number().length());
}
return WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
WEAVE_ERROR ConfigurationManagerImpl::GetAndStorePairingCode() {
fuchsia::weave::FactoryDataManager_GetPairingCode_Result pairing_code_result;
fuchsia::weave::FactoryDataManager_GetPairingCode_Response pairing_code_response;
std::string pairing_code;
char read_value[kMaxPairingCodeLength + 1];
size_t read_value_size = 0;
WEAVE_ERROR err;
zx_status_t status = weave_factory_data_manager_->GetPairingCode(&pairing_code_result);
if (ZX_OK != status || !pairing_code_result.is_response()) {
return WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
pairing_code_response = pairing_code_result.response();
if (pairing_code_response.pairing_code.size() > kMaxPairingCodeLength) {
return WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
err = StorePairingCode((const char*)pairing_code_response.pairing_code.data(),
pairing_code_response.pairing_code.size());
if (WEAVE_NO_ERROR != err) {
return err;
}
// Device pairing code can be overridden with configured value for testing.
// Current unit tests only look for this configured value. To ensure code coverage
// in unit tests device pairing code is read and stored even if a pairing code
// is configured for test. TODO: fxb/49671
err = device_info_->ReadConfigValueStr(kConfigKey_PairingCode, read_value,
kMaxPairingCodeLength + 1, &read_value_size);
if (err == WEAVE_NO_ERROR) {
return StorePairingCode(read_value, read_value_size);
}
// return no error, will continue to use device pairing code
return WEAVE_NO_ERROR;
}
WEAVE_ERROR ConfigurationManagerImpl::GetAndStoreMfrDeviceCert() {
char path[PATH_MAX] = {'\0'};
char mfr_cert[kWeaveCertificateMaxLength];
size_t out_size;
zx_status_t status;
WEAVE_ERROR err;
err = device_info_->ReadConfigValueStr(kDeviceInfoConfigKey_MfrDeviceCertPath, path, sizeof(path),
&out_size);
if (err != WEAVE_NO_ERROR) {
FX_LOGS(WARNING) << "No manufacturer device certificate was found";
return err;
}
status = ReadFactoryFile(path, mfr_cert, sizeof(mfr_cert), &out_size);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed getting manufacturer certificate from factory with status "
<< zx_status_get_string(status) << " for path: " << path;
return WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
return StoreManufacturerDeviceCertificate(reinterpret_cast<uint8_t*>(mfr_cert), out_size);
}
WEAVE_ERROR ConfigurationManagerImpl::_GetVendorId(uint16_t& vendor_id) {
return device_info_->ReadConfigValue(kDeviceInfoConfigKey_VendorId, &vendor_id);
}
WEAVE_ERROR ConfigurationManagerImpl::_GetProductId(uint16_t& product_id) {
return device_info_->ReadConfigValue(kDeviceInfoConfigKey_ProductId, &product_id);
}
WEAVE_ERROR ConfigurationManagerImpl::_GetFirmwareRevision(char* buf, size_t buf_size,
size_t& out_len) {
return device_info_->ReadConfigValueStr(kDeviceInfoConfigKey_FirmwareRevision, buf, buf_size,
&out_len);
}
zx_status_t ConfigurationManagerImpl::ReadFactoryFile(const char* path, char* buf, size_t buf_size,
size_t* out_len) {
fuchsia::io::DirectorySyncPtr factory_directory;
zx_status_t status;
struct stat statbuf;
int dir_fd;
// Open the factory store directory as a file descriptor.
status = factory_store_provider_->GetFactoryStore(factory_directory.NewRequest());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to get factory store: " << zx_status_get_string(status);
return status;
}
status = fdio_fd_create(factory_directory.Unbind().TakeChannel().release(), &dir_fd);
if (status != ZX_OK || dir_fd < 0) {
FX_LOGS(ERROR) << "Failed to open factory store: " << zx_status_get_string(status);
return status;
}
auto close_dir_defer = fit::defer([&] { close(dir_fd); });
// Grab the fd of the corresponding file path and validate.
int fd = openat(dir_fd, path, O_RDONLY);
if (fd < 0) {
FX_LOGS(ERROR) << "Failed to open " << path << ": " << strerror(errno);
return ZX_ERR_IO;
}
auto close_fd_defer = fit::defer([&] { close(fd); });
// Check the size of the file.
if (fstat(fd, &statbuf) < 0) {
FX_LOGS(ERROR) << "Could not stat file: " << path << ": " << strerror(errno);
return ZX_ERR_IO;
}
size_t file_size = static_cast<size_t>(statbuf.st_size);
if (file_size > buf_size) {
FX_LOGS(ERROR) << "File too large for buffer: File size = " << file_size
<< ", buffer size = " << buf_size;
return ZX_ERR_BUFFER_TOO_SMALL;
}
// Read up to buf_size bytes into buf.
size_t total_read = 0;
ssize_t current_read = 0;
while ((current_read = read(fd, buf + total_read, buf_size - total_read)) > 0) {
total_read += current_read;
}
// Store the total size read.
if (out_len) {
*out_len = total_read;
}
// Confirm that the last read was successful.
if (current_read < 0) {
FX_LOGS(ERROR) << "Failed to read from file: " << strerror(errno);
status = ZX_ERR_IO;
}
return status;
}
WEAVE_ERROR ConfigurationManagerImpl::_GetDeviceId(uint64_t& device_id) {
WEAVE_ERROR err = ReadConfigValue(kConfigKey_MfrDeviceId, device_id);
if (err == WEAVE_NO_ERROR) {
return WEAVE_NO_ERROR;
}
err = device_info_->ReadConfigValue(kDeviceInfoConfigKey_DeviceId, &device_id);
if (err != WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND) {
return err;
}
char path[PATH_MAX] = {'\0'};
size_t out_size;
err = device_info_->ReadConfigValueStr(kDeviceInfoConfigKey_DeviceIdPath, path, sizeof(path),
&out_size);
if (err == WEAVE_NO_ERROR) {
err = GetDeviceIdFromFactory(path, &device_id);
FX_CHECK(err == WEAVE_NO_ERROR) << "Failed getting device id from factory at path: " << path;
StoreManufacturerDeviceId(device_id);
return err;
}
err = GetDeviceIdFromFactory(path, &device_id);
if (err != WEAVE_NO_ERROR) {
return err;
}
return StoreManufacturerDeviceId(device_id);
}
WEAVE_ERROR ConfigurationManagerImpl::_GetManufacturerDeviceCertificate(uint8_t* buf,
size_t buf_size,
size_t& out_len) {
WEAVE_ERROR err = ReadConfigValueBin(kConfigKey_MfrDeviceCert, buf, buf_size, out_len);
if (err == WEAVE_NO_ERROR) {
return err;
}
err = GetAndStoreMfrDeviceCert();
if (err != WEAVE_NO_ERROR) {
return err;
}
return ReadConfigValueBin(kConfigKey_MfrDeviceCert, buf, buf_size, out_len);
}
zx_status_t ConfigurationManagerImpl::GetDeviceIdFromFactory(const char* path,
uint64_t* factory_device_id) {
zx_status_t status;
char output[kWeaveDeviceIdMaxLength + 1] = {'\0'};
status = ReadFactoryFile(path, output, kWeaveDeviceIdMaxLength, nullptr);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "ReadFactoryfile failed " << status;
return status;
}
if (output[0] == '\0') {
FX_LOGS(ERROR) << "Factory file output string is empty";
return ZX_ERR_IO;
}
*factory_device_id = strtoull(output, NULL, 16);
if (errno == ERANGE) {
FX_LOGS(ERROR) << "strtoull failed: " << strerror(errno);
return ZX_ERR_IO;
}
return ZX_OK;
}
WEAVE_ERROR ConfigurationManagerImpl::_GetPrimaryWiFiMACAddress(uint8_t* buf) {
fuchsia::wlan::device::service::ListPhysResponse phy_list_resp;
if (ZX_OK != wlan_device_service_->ListPhys(&phy_list_resp)) {
return WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
for (auto phy : phy_list_resp.phys) {
fuchsia::wlan::device::service::QueryPhyRequest req;
int32_t out_status;
std::unique_ptr<fuchsia::wlan::device::service::QueryPhyResponse> phy_resp;
req.phy_id = phy.phy_id;
if (ZX_OK != wlan_device_service_->QueryPhy(std::move(req), &out_status, &phy_resp) ||
0 != out_status) {
continue;
}
for (auto role : phy_resp->info.mac_roles) {
if (role == fuchsia::wlan::device::MacRole::CLIENT) {
memcpy(buf, phy_resp->info.hw_mac_address.data(), ETH_ALEN);
return WEAVE_NO_ERROR;
}
}
}
return WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
::nl::Weave::Profiles::Security::AppKeys::GroupKeyStoreBase*
ConfigurationManagerImpl::_GetGroupKeyStore() {
return &gGroupKeyStore;
}
bool ConfigurationManagerImpl::_CanFactoryReset() { return true; }
void ConfigurationManagerImpl::_InitiateFactoryReset() { EnvironmentConfig::FactoryResetConfig(); }
WEAVE_ERROR ConfigurationManagerImpl::_ReadPersistedStorageValue(
::nl::Weave::Platform::PersistedStorage::Key key, uint32_t& value) {
WEAVE_ERROR err = ReadConfigValue(key, value);
return (err == WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND)
? WEAVE_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND
: err;
}
WEAVE_ERROR ConfigurationManagerImpl::_WritePersistedStorageValue(
::nl::Weave::Platform::PersistedStorage::Key key, uint32_t value) {
WEAVE_ERROR err = WriteConfigValue(key, value);
return (err != WEAVE_NO_ERROR) ? WEAVE_ERROR_PERSISTED_STORAGE_FAIL : err;
}
} // namespace DeviceLayer
} // namespace Weave
} // namespace nl