blob: e5100d7daffde29510e1bc6dd09ab45a77312a22 [file] [log] [blame]
// Copyright 2021 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 "cryptohome/storage/cryptohome_vault_factory.h"
#include <memory>
#include <string>
#include <utility>
#include <base/files/file_path.h>
#include "cryptohome/filesystem_layout.h"
#include "cryptohome/platform.h"
#include "cryptohome/storage/cryptohome_vault.h"
#include "cryptohome/storage/encrypted_container/encrypted_container.h"
#include "cryptohome/storage/encrypted_container/encrypted_container_factory.h"
#include "cryptohome/storage/encrypted_container/filesystem_key.h"
namespace {
// Size of logical volumes to use for the dm-crypt cryptohomes.
constexpr uint64_t kLogicalVolumeSizePercent = 90;
// By default, each ext4 filesystem takes up ~2% of the entire filesystem space
// for storing filesystem metadata including inode tables. Tune the number of
// inodes such that the overall metadata cost is <1 % of the filesystem size.
// For larger storage devices, we increase the inode count up to an upper limit
// of 2^20 inodes.
uint64_t CalculateInodeCount(int64_t filesystem_size) {
constexpr uint64_t kGigabytes = 1024 * 1024 * 1024;
constexpr uint64_t kBaseInodeCount = 256 * 1024;
if (filesystem_size <= 16 * kGigabytes)
return kBaseInodeCount;
if (filesystem_size <= 32 * kGigabytes)
return 2 * kBaseInodeCount;
return 4 * kBaseInodeCount;
}
} // namespace
namespace cryptohome {
CryptohomeVaultFactory::CryptohomeVaultFactory(
Platform* platform,
std::unique_ptr<EncryptedContainerFactory> encrypted_container_factory)
: platform_(platform),
encrypted_container_factory_(std::move(encrypted_container_factory)) {}
CryptohomeVaultFactory::CryptohomeVaultFactory(Platform* platform)
: CryptohomeVaultFactory(
platform, std::make_unique<EncryptedContainerFactory>(platform)) {}
CryptohomeVaultFactory::~CryptohomeVaultFactory() {}
std::unique_ptr<EncryptedContainer>
CryptohomeVaultFactory::GenerateEncryptedContainer(
EncryptedContainerType type,
const std::string& obfuscated_username,
const FileSystemKeyReference& key_reference,
const std::string& container_identifier,
bool keylocker_enabled) {
EncryptedContainerConfig config;
base::FilePath stateful_device;
uint64_t stateful_size;
switch (type) {
case EncryptedContainerType::kEcryptfs:
config.backing_dir = GetEcryptfsUserVaultPath(obfuscated_username);
config.type = EncryptedContainerType::kEcryptfs;
break;
case EncryptedContainerType::kFscrypt:
config.backing_dir = GetUserMountDirectory(obfuscated_username);
config.type = EncryptedContainerType::kFscrypt;
break;
case EncryptedContainerType::kDmcrypt:
// Calculate size for dm-crypt partition.
stateful_device = platform_->GetStatefulDevice();
if (stateful_device.empty())
return nullptr;
if (!platform_->GetBlkSize(stateful_device, &stateful_size))
return nullptr;
LOG_IF(INFO, keylocker_enabled) << "Using Keylocker for encryption";
config.type = EncryptedContainerType::kDmcrypt;
config.dmcrypt_config = {
.backing_device_config =
{.type = BackingDeviceType::kLogicalVolumeBackingDevice,
.name = LogicalVolumePrefix(obfuscated_username) +
container_identifier,
.size = static_cast<int64_t>(
(stateful_size * kLogicalVolumeSizePercent) /
(100 * 1024 * 1024)),
.logical_volume = {.thinpool_name = "thinpool",
.physical_volume = stateful_device}},
.dmcrypt_device_name =
DmcryptVolumePrefix(obfuscated_username) + container_identifier,
.dmcrypt_cipher = keylocker_enabled ? "capi:xts-aes-aeskl-plain64"
: "aes-xts-plain64",
// TODO(sarthakkukreti): Add more dynamic checks for filesystem
// features once dm-crypt cryptohomes are stable.
.mkfs_opts = {"-O", "^huge_file,^flex_bg,", "-N",
base::StringPrintf("%" PRIu64,
CalculateInodeCount(stateful_size)),
"-E", "discard"},
.tune2fs_opts = {"-O", "verity,quota,project", "-Q",
"usrquota,grpquota,prjquota"}};
break;
case EncryptedContainerType::kEphemeral:
config.type = EncryptedContainerType::kEphemeral;
config.backing_file_name = obfuscated_username;
break;
case EncryptedContainerType::kEcryptfsToFscrypt:
// The migrating type is handled by the higher level abstraction.
// FALLTHROUGH
case EncryptedContainerType::kUnknown:
return nullptr;
}
return encrypted_container_factory_->Generate(config, key_reference);
}
std::unique_ptr<CryptohomeVault> CryptohomeVaultFactory::Generate(
const std::string& obfuscated_username,
const FileSystemKeyReference& key_reference,
EncryptedContainerType vault_type,
bool keylocker_enabled) {
EncryptedContainerType container_type = EncryptedContainerType::kUnknown;
EncryptedContainerType migrating_container_type =
EncryptedContainerType::kUnknown;
if (vault_type != EncryptedContainerType::kEcryptfsToFscrypt) {
container_type = vault_type;
} else {
container_type = EncryptedContainerType::kEcryptfs;
migrating_container_type = EncryptedContainerType::kFscrypt;
}
// Generate containers for the vault.
std::unique_ptr<EncryptedContainer> container = GenerateEncryptedContainer(
container_type, obfuscated_username, key_reference,
kDmcryptDataContainerSuffix, keylocker_enabled);
if (!container) {
LOG(ERROR) << "Could not create vault container";
return nullptr;
}
std::unique_ptr<EncryptedContainer> migrating_container;
if (migrating_container_type != EncryptedContainerType::kUnknown) {
migrating_container = GenerateEncryptedContainer(
migrating_container_type, obfuscated_username, key_reference,
kDmcryptDataContainerSuffix, keylocker_enabled);
if (!migrating_container) {
LOG(ERROR) << "Could not create vault container for migration";
return nullptr;
}
}
std::unique_ptr<EncryptedContainer> cache_container;
if (container_type == EncryptedContainerType::kDmcrypt) {
cache_container =
GenerateEncryptedContainer(container_type, obfuscated_username,
key_reference, kDmcryptCacheContainerSuffix);
if (!cache_container) {
LOG(ERROR) << "Could not create vault container for cache";
return nullptr;
}
}
return std::make_unique<CryptohomeVault>(
obfuscated_username, std::move(container), std::move(migrating_container),
std::move(cache_container), platform_);
}
} // namespace cryptohome