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