blob: 24fdbced2e49dc66c495310792ad2ae5ad8c3c15 [file] [log] [blame]
// Copyright 2015 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 "settingsd/cros_install_attributes.h"
#include <algorithm>
#include <arpa/inet.h>
#include <map>
#include <set>
#include <stddef.h>
#include <string>
#include <vector>
#include <base/logging.h>
#include "bindings/install_attributes.pb.h"
#include "settingsd/crypto.h"
#include "settingsd/identifier_utils.h"
#include "settingsd/key.h"
#include "settingsd/nvram.h"
#include "settingsd/settings_keys.h"
#include "settingsd/settings_service.h"
#include "settingsd/source.h"
#include "settingsd/version_stamp.h"
namespace settingsd {
namespace {
// A SettingsDocument class that wraps an install attributes protobuf message
// and exports the key-value pairs stored in install attributes via the
// SettingsDocument interface.
class CrosInstallAttributesDocument : public SettingsDocument {
public:
explicit CrosInstallAttributesDocument(
std::unique_ptr<cryptohome::SerializedInstallAttributes>
install_attributes_message);
~CrosInstallAttributesDocument() override;
// SettingsDocument:
BlobRef GetValue(const Key& key) const override;
std::set<Key> GetKeys(const Key& prefix) const override;
std::set<Key> GetDeletions(const Key& prefix) const override;
VersionStamp GetVersionStamp() const override;
bool HasKeysOrDeletions(const Key& prefix) const override;
private:
// Sanitizes an attribute name and converts it to a Key.
static bool SanitizeKey(const std::string& attribute_name, Key* key);
std::unique_ptr<cryptohome::SerializedInstallAttributes>
install_attributes_message_;
DISALLOW_COPY_AND_ASSIGN(CrosInstallAttributesDocument);
};
CrosInstallAttributesDocument::CrosInstallAttributesDocument(
std::unique_ptr<cryptohome::SerializedInstallAttributes>
install_attributes_message)
: install_attributes_message_(std::move(install_attributes_message)) {}
CrosInstallAttributesDocument::~CrosInstallAttributesDocument() {}
BlobRef CrosInstallAttributesDocument::GetValue(const Key& key) const {
for (auto& attribute : *install_attributes_message_->mutable_attributes()) {
Key attribute_key;
if (SanitizeKey(attribute.name(), &attribute_key) && attribute_key == key)
return BlobRef(attribute.mutable_value());
}
return BlobRef();
}
std::set<Key> CrosInstallAttributesDocument::GetKeys(const Key& prefix) const {
std::set<Key> result;
for (auto& attribute : install_attributes_message_->attributes()) {
Key attribute_key;
if (SanitizeKey(attribute.name(), &attribute_key))
result.insert(attribute_key);
}
return result;
}
std::set<Key> CrosInstallAttributesDocument::GetDeletions(
const Key& prefix) const {
// Install attributes never contain deletions.
return std::set<Key>();
}
VersionStamp CrosInstallAttributesDocument::GetVersionStamp() const {
// Install attributes never contain versions. This means they can never
// supersede values received from other sources.
return VersionStamp();
}
bool CrosInstallAttributesDocument::HasKeysOrDeletions(
const Key& prefix) const {
return !GetKeys(prefix).empty();
}
// static
bool CrosInstallAttributesDocument::SanitizeKey(
const std::string& attribute_name,
Key* key) {
CHECK(key);
// Existing code has a bug to terminate attribute names with a NULL character,
// which gets stripped by initializing with the c_str() pointer.
std::string sanitized_name(attribute_name.c_str());
if (!Key::IsValidKey(sanitized_name))
return false;
*key = Key(sanitized_name);
return true;
}
} // namespace
CrosInstallAttributesContainer::~CrosInstallAttributesContainer() {}
BlobRef CrosInstallAttributesContainer::GetData() const {
return BlobRef(&data_);
}
CrosInstallAttributesContainer::CrosInstallAttributesContainer(
std::vector<uint8_t> data)
: data_(std::move(data)) {}
// static
std::unique_ptr<LockedSettingsContainer> CrosInstallAttributesContainer::Parse(
const std::string& format,
BlobRef data) {
// TODO(mnissler): Avoid the copy here.
return std::unique_ptr<LockedSettingsContainer>(
new CrosInstallAttributesContainer(data.ToVector()));
}
std::unique_ptr<SettingsDocument>
CrosInstallAttributesContainer::DecodePayloadInternal() {
BlobRef blob(&data_);
std::unique_ptr<cryptohome::SerializedInstallAttributes>
install_attributes_message(new cryptohome::SerializedInstallAttributes);
if (install_attributes_message->ParseFromArray(blob.data(), blob.size())) {
return std::unique_ptr<SettingsDocument>(new CrosInstallAttributesDocument(
std::move(install_attributes_message)));
}
return nullptr;
}
const uint32_t CrosInstallAttributesSourceDelegate::kReservedSizeBytes;
const uint32_t CrosInstallAttributesSourceDelegate::kReservedFlagsBytes;
const uint32_t CrosInstallAttributesSourceDelegate::kReservedSaltBytesV1;
const uint32_t CrosInstallAttributesSourceDelegate::kReservedSaltBytesV2;
const uint32_t CrosInstallAttributesSourceDelegate::kReservedDigestBytes;
const uint32_t CrosInstallAttributesSourceDelegate::kReservedNvramBytesV1;
const uint32_t CrosInstallAttributesSourceDelegate::kReservedNvramBytesV2;
CrosInstallAttributesSourceDelegate::CrosInstallAttributesSourceDelegate(
const NVRam* nvram,
uint32_t nvram_index)
: nvram_(nvram), nvram_index_(nvram_index) {}
CrosInstallAttributesSourceDelegate::~CrosInstallAttributesSourceDelegate() {}
bool CrosInstallAttributesSourceDelegate::ValidateVersionComponent(
const LockedVersionComponent& component) const {
return false;
}
bool CrosInstallAttributesSourceDelegate::ValidateContainer(
const LockedSettingsContainer& container) const {
// Extract the verification parameters from |nvram_|.
size_t size;
std::vector<uint8_t> salt;
std::vector<uint8_t> hash;
if (!ExtractNVRamParameters(&size, &salt, &hash))
return false;
// Verify size.
BlobRef container_data = container.GetData();
if (size != container_data.size()) {
LOG(WARNING) << "Blob size doesn't match NVRAM: " << container_data.size()
<< " vs " << size;
return false;
}
// Verify the hash.
std::vector<uint8_t> salted_container_data(container_data.ToVector());
salted_container_data.insert(salted_container_data.end(), salt.begin(),
salt.end());
if (!crypto::VerifyDigest(crypto::kDigestSha256,
BlobRef(&salted_container_data), BlobRef(&hash))) {
LOG(WARNING) << "Blob digest doesn't match NVRAM.";
return false;
}
return true;
}
// static
std::unique_ptr<SourceDelegate> CrosInstallAttributesSourceDelegate::Create(
const NVRam* nvram,
const std::string& source_id,
const SettingsService& settings) {
// Extract the index from the source configuration.
BlobRef nvram_index_value = settings.GetValue(
MakeSourceKey(source_id).Extend({keys::sources::kNVRamIndex}));
if (!nvram_index_value.valid())
return std::unique_ptr<SourceDelegate>();
size_t pos = 0;
uint32_t nvram_index = std::stoul(nvram_index_value.ToString(), &pos, 0);
if (pos == 0 || pos != nvram_index_value.size())
return std::unique_ptr<SourceDelegate>();
return std::unique_ptr<SourceDelegate>(
new CrosInstallAttributesSourceDelegate(nvram, nvram_index));
}
bool CrosInstallAttributesSourceDelegate::ExtractNVRamParameters(
size_t* size,
std::vector<uint8_t>* salt,
std::vector<uint8_t>* hash) const {
// A locked NVRam is required.
bool read_lock = false;
bool write_lock = false;
if (nvram_->IsSpaceLocked(nvram_index_, &read_lock, &write_lock) !=
NVRam::Status::kSuccess ||
!write_lock) {
LOG(WARNING) << "NVRam space " << nvram_index_ << " not locked.";
return false;
}
std::vector<uint8_t> nvram_data;
if (nvram_->ReadSpace(nvram_index_, &nvram_data) != NVRam::Status::kSuccess) {
LOG(ERROR) << "Failed to read NVRam space " << nvram_index_;
return false;
}
// If the read is successful, but the size is not an expected value,
// we've got tampering or an unexpected bug/race during set.
switch (nvram_data.size()) {
case kReservedNvramBytesV1:
salt->resize(kReservedSaltBytesV1);
break;
case kReservedNvramBytesV2:
salt->resize(kReservedSaltBytesV2);
break;
default:
LOG(ERROR) << "Unexpected NVRAM size: " << nvram_data.size();
return false;
}
const uint8_t* nvram_ptr = nvram_data.data();
// Extract the expected data size.
//
// For reasons lost to history, the size field is stored in inverse (!) host
// byte order. The code below originates from cryptohome. Note that the loop
// reads the size field in little-endian format in an endian-independent way.
// The ntohl invocation then inverts byte order to big-endian on little-endian
// hosts.
uint32_t stored_size = 0;
for (uint32_t index = 0; index < kReservedSizeBytes; ++index)
stored_size |= static_cast<uint32_t>(nvram_ptr[index]) << (index * 8);
*size = ntohl(stored_size);
nvram_ptr += kReservedSizeBytes;
// Strip the flags byte, which is currently unused.
nvram_ptr += kReservedFlagsBytes;
// Grab the salt.
std::copy(nvram_ptr, nvram_ptr + salt->size(), salt->begin());
nvram_ptr += salt->size();
// Grab the hash.
hash->resize(kReservedDigestBytes);
std::copy(nvram_ptr, nvram_ptr + hash->size(), hash->begin());
nvram_ptr += hash->size();
// Sanity check for making sure the pointer arithmetic above is correct.
CHECK_EQ(nvram_ptr, nvram_data.data() + nvram_data.size());
return true;
}
} // namespace settingsd