blob: 2695f40914cd4574c7ec44bf8fd118d80558a88e [file] [log] [blame]
// Copyright 2012 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/lockbox-cache.h"
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <bindings/device_management_backend.pb.h>
#include <brillo/proto_bindings/install_attributes.pb.h>
#include <brillo/secure_blob.h>
#include <metrics/metrics_library.h>
#include <policy/device_local_account_policy_util.h>
#include <policy/device_policy_impl.h>
#include "cryptohome/lockbox.h"
namespace cryptohome {
namespace {
// Permissions of cache file (modulo umask).
const mode_t kCacheFilePermissions = 0644;
// Permissions of persistent file (modulo umask).
const mode_t kPersistentFilePermissions = 0644;
// An indicator to indicate that this is a device where we restored attributes.
const char kRestoredInstallAttributesFile[] =
"/home/.shadow/install_attributes.restored";
// Record the result of the install attributes restoring process.
const char kInstallAttributesRestoreState[] =
"Platform.DeviceManagement.InstallAttributesRestoreResult";
// The maximum length of the domain is 253, and assume the user name is
// less than 256, the total length of the username should be less than 512.
const size_t kMaxUsernameLength = 512;
// The maximum length of the device id is 36 (uuid length).
const size_t kMaxDeviceIdLength = 36;
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class RestoreResult {
kNoDevicePolicy = 0,
kSuccessWithEmpty = 1,
kSuccessWithEnterprise = 2,
kFailed = 3,
kMaxValue = kFailed,
};
enum class Mode {
kConsumerDeviceMode,
kEnterpriseDeviceMode,
kLegacyRetailDeviceMode,
kConsumerKioskDeviceMode,
kDemoDeviceMode,
};
bool AddSerializedAttribute(SerializedInstallAttributes& attrs,
const std::string& name,
const std::string& value) {
SerializedInstallAttributes::Attribute* attr = attrs.add_attributes();
if (!attr) {
LOG(ERROR) << "Failed to add a new attribute.";
return false;
}
attr->set_name(name);
attr->set_value(value + std::string("\0", 1));
return true;
}
std::optional<SerializedInstallAttributes> SerializedInstallAttributesFromMode(
Mode mode, const enterprise_management::PolicyData& policy_data) {
if (policy_data.username().size() > kMaxUsernameLength) {
LOG(ERROR) << "Username is too long.";
return std::nullopt;
}
if (policy_data.device_id().size() > kMaxDeviceIdLength) {
LOG(ERROR) << "Device ID is too long.";
return std::nullopt;
}
SerializedInstallAttributes attrs;
attrs.set_version(1);
std::string kiosk_enabled;
std::string enterprise_owned;
std::string enterprise_mode;
std::string domain;
std::string device_id;
switch (mode) {
case Mode::kConsumerDeviceMode:
enterprise_owned = "true";
enterprise_mode = "consumer";
domain = policy::ExtractDomainName(policy_data.username());
device_id = policy_data.device_id();
break;
case Mode::kEnterpriseDeviceMode:
enterprise_owned = "true";
enterprise_mode = "enterprise";
domain = policy::ExtractDomainName(policy_data.username());
device_id = policy_data.device_id();
break;
case Mode::kLegacyRetailDeviceMode:
enterprise_owned = "true";
enterprise_mode = "kiosk";
domain = policy::ExtractDomainName(policy_data.username());
device_id = policy_data.device_id();
break;
case Mode::kConsumerKioskDeviceMode:
kiosk_enabled = "true";
enterprise_mode = "consumer_kiosk";
break;
case Mode::kDemoDeviceMode:
enterprise_owned = "true";
enterprise_mode = "demo_mode";
domain = policy::ExtractDomainName(policy_data.username());
device_id = policy_data.device_id();
break;
}
if (!AddSerializedAttribute(attrs, "consumer.app_kiosk_enabled",
kiosk_enabled)) {
return std::nullopt;
}
if (!AddSerializedAttribute(attrs, "enterprise.owned", enterprise_owned)) {
return std::nullopt;
}
if (!AddSerializedAttribute(attrs, "enterprise.mode", enterprise_mode)) {
return std::nullopt;
}
if (!AddSerializedAttribute(attrs, "enterprise.domain", domain)) {
return std::nullopt;
}
if (!AddSerializedAttribute(attrs, "enterprise.realm", "")) {
return std::nullopt;
}
if (!AddSerializedAttribute(attrs, "enterprise.device_id", device_id)) {
return std::nullopt;
}
return attrs;
}
bool RestoreIfVerificationPasses(const base::FilePath& lockbox_path,
const SerializedInstallAttributes& attrs,
libstorage::Platform& platform,
LockboxContents& lockbox,
brillo::Blob& lockbox_data) {
lockbox_data.resize(attrs.ByteSizeLong());
attrs.SerializeWithCachedSizesToArray(
static_cast<google::protobuf::uint8*>(lockbox_data.data()));
if (lockbox.Verify(lockbox_data) !=
LockboxContents::VerificationResult::kValid) {
return false;
}
// Indicate that we restored the install attributes.
if (!platform.WriteFileAtomic(base::FilePath(kRestoredInstallAttributesFile),
lockbox_data, kPersistentFilePermissions)) {
LOG(ERROR) << "Failed to write install attributes indicator";
return false;
}
// Restore the install attributes file.
if (!platform.WriteFileAtomic(lockbox_path, lockbox_data,
kPersistentFilePermissions)) {
LOG(ERROR) << "Failed to write cache file";
return false;
}
return true;
}
bool RestoreEmptyInstallAttributes(const base::FilePath& lockbox_path,
libstorage::Platform& platform,
LockboxContents& lockbox,
brillo::Blob& lockbox_data) {
SerializedInstallAttributes attrs;
attrs.set_version(1);
/* Try to restore with empty SerializedInstallAttributes */
if (!RestoreIfVerificationPasses(lockbox_path, attrs, platform, lockbox,
lockbox_data)) {
return false;
}
LOG(INFO) << "Restored with empty install attributes successfully.";
return true;
}
bool RestoreEnterpriseInstallAttributes(
const base::FilePath& lockbox_path,
const enterprise_management::PolicyData& policy_data,
libstorage::Platform& platform,
LockboxContents& lockbox,
brillo::Blob& lockbox_data) {
for (Mode mode : {
Mode::kEnterpriseDeviceMode,
Mode::kDemoDeviceMode,
Mode::kConsumerKioskDeviceMode,
Mode::kLegacyRetailDeviceMode,
Mode::kConsumerDeviceMode,
}) {
std::optional<SerializedInstallAttributes> attrs =
SerializedInstallAttributesFromMode(mode, policy_data);
if (!attrs.has_value()) {
continue;
}
if (RestoreIfVerificationPasses(lockbox_path, *attrs, platform, lockbox,
lockbox_data)) {
LOG(INFO) << "Restored with enterprise (mode= " << static_cast<int>(mode)
<< ") install attributes successfully.";
return true;
}
}
return false;
}
bool RestoreInstallAttributes(const base::FilePath& lockbox_path,
libstorage::Platform& platform,
LockboxContents& lockbox,
brillo::Blob& lockbox_data) {
MetricsLibrary metrics;
policy::DevicePolicyImpl device_policy;
bool policy_loaded = device_policy.LoadPolicy(/*delete_invalid_files=*/false);
if (device_policy.get_number_of_policy_files() == 0 || !policy_loaded) {
LOG(ERROR) << "No valid device policy.";
metrics.SendEnumToUMA(kInstallAttributesRestoreState,
RestoreResult::kNoDevicePolicy);
return false;
}
const enterprise_management::PolicyData& policy_data =
device_policy.get_policy_data();
if (RestoreEmptyInstallAttributes(lockbox_path, platform, lockbox,
lockbox_data)) {
metrics.SendEnumToUMA(kInstallAttributesRestoreState,
RestoreResult::kSuccessWithEmpty);
return true;
}
if (RestoreEnterpriseInstallAttributes(lockbox_path, policy_data, platform,
lockbox, lockbox_data)) {
metrics.SendEnumToUMA(kInstallAttributesRestoreState,
RestoreResult::kSuccessWithEnterprise);
return true;
}
LOG(ERROR) << "Failed to restore install attributes.";
metrics.SendEnumToUMA(kInstallAttributesRestoreState, RestoreResult::kFailed);
return false;
}
} // namespace
bool CacheLockbox(libstorage::Platform* platform,
const base::FilePath& nvram_path,
const base::FilePath& lockbox_path,
const base::FilePath& cache_path) {
brillo::SecureBlob nvram;
if (!platform->ReadFileToSecureBlob(nvram_path, &nvram)) {
LOG(INFO) << "Failed to read NVRAM contents from " << nvram_path.value();
return false;
}
std::unique_ptr<LockboxContents> lockbox = LockboxContents::New();
if (!lockbox) {
LOG(ERROR) << "Unsupported lockbox size!";
return false;
}
if (!lockbox->Decode(nvram)) {
LOG(ERROR) << "Lockbox failed to decode NVRAM data";
return false;
}
brillo::Blob lockbox_data;
if (!platform->ReadFile(lockbox_path, &lockbox_data)) {
LOG(INFO) << "Failed to read lockbox data from " << lockbox_path.value();
if (!RestoreInstallAttributes(lockbox_path, *platform, *lockbox,
lockbox_data)) {
return false;
}
}
if (lockbox->Verify(lockbox_data) !=
LockboxContents::VerificationResult::kValid) {
LOG(ERROR) << "Lockbox did not verify!";
if (!RestoreInstallAttributes(lockbox_path, *platform, *lockbox,
lockbox_data)) {
return false;
}
}
// Write atomically (not durably) because cache file resides on tmpfs.
if (!platform->WriteFileAtomic(cache_path, lockbox_data,
kCacheFilePermissions)) {
LOG(ERROR) << "Failed to write cache file";
return false;
}
return true;
}
} // namespace cryptohome