|  | // Copyright 2012 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "chromeos/ash/components/install_attributes/install_attributes.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/functional/callback_helpers.h" | 
|  | #include "base/location.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/metrics/histogram_base.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/notreached.h" | 
|  | #include "base/path_service.h" | 
|  | #include "base/system/sys_info.h" | 
|  | #include "base/task/single_thread_task_runner.h" | 
|  | #include "base/time/time.h" | 
|  | #include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h" | 
|  | #include "chromeos/ash/components/dbus/device_management/device_management_interface.pb.h" | 
|  | #include "chromeos/ash/components/dbus/device_management/install_attributes_util.h" | 
|  | #include "chromeos/dbus/constants/dbus_paths.h" | 
|  | #include "chromeos/dbus/tpm_manager/tpm_manager.pb.h" | 
|  | #include "chromeos/dbus/tpm_manager/tpm_manager_client.h" | 
|  | #include "components/policy/proto/install_attributes.pb.h" | 
|  | #include "google_apis/gaia/gaia_auth_util.h" | 
|  | #include "third_party/cros_system_api/dbus/service_constants.h" | 
|  |  | 
|  | namespace ash { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | InstallAttributes* g_install_attributes = nullptr; | 
|  |  | 
|  | // Calling SetForTesting sets this flag. This flag means that the production | 
|  | // code which calls Initialize and Shutdown will have no effect - the test | 
|  | // install attributes will remain in place until ShutdownForTesting is called. | 
|  | bool g_using_install_attributes_for_testing = false; | 
|  |  | 
|  | // Number of TPM lock state query retries during consistency check. | 
|  | const int kDbusRetryCount = 12; | 
|  |  | 
|  | // Interval of TPM lock state query retries during consistency check. | 
|  | constexpr base::TimeDelta kDbusRetryInterval = base::Seconds(5); | 
|  |  | 
|  | std::string ReadMapKey(const std::map<std::string, std::string>& map, | 
|  | const std::string& key) { | 
|  | std::map<std::string, std::string>::const_iterator entry = map.find(key); | 
|  | if (entry != map.end()) { | 
|  | return entry->second; | 
|  | } | 
|  | return std::string(); | 
|  | } | 
|  |  | 
|  | void WarnIfNonempty(const std::map<std::string, std::string>& map, | 
|  | const std::string& key) { | 
|  | if (!ReadMapKey(map, key).empty()) { | 
|  | LOG(WARNING) << key << " expected to be empty."; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | void InstallAttributes::Initialize() { | 
|  | // Don't reinitialize if a specific instance has already been set for test. | 
|  | if (g_using_install_attributes_for_testing) | 
|  | return; | 
|  |  | 
|  | DCHECK(!g_install_attributes); | 
|  | g_install_attributes = new InstallAttributes(InstallAttributesClient::Get()); | 
|  | g_install_attributes->Init(base::PathService::CheckedGet( | 
|  | chromeos::dbus_paths::FILE_INSTALL_ATTRIBUTES)); | 
|  | } | 
|  |  | 
|  | // static | 
|  | bool InstallAttributes::IsInitialized() { | 
|  | return g_install_attributes; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void InstallAttributes::Shutdown() { | 
|  | if (g_using_install_attributes_for_testing) | 
|  | return; | 
|  |  | 
|  | DCHECK(g_install_attributes); | 
|  | delete g_install_attributes; | 
|  | g_install_attributes = nullptr; | 
|  | } | 
|  |  | 
|  | // static | 
|  | InstallAttributes* InstallAttributes::Get() { | 
|  | DCHECK(g_install_attributes); | 
|  | return g_install_attributes; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void InstallAttributes::SetForTesting(InstallAttributes* test_instance) { | 
|  | DCHECK(!g_install_attributes); | 
|  | DCHECK(!g_using_install_attributes_for_testing); | 
|  | g_install_attributes = test_instance; | 
|  | g_using_install_attributes_for_testing = true; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void InstallAttributes::ShutdownForTesting() { | 
|  | DCHECK(g_using_install_attributes_for_testing); | 
|  | // Don't delete the test instance, we are not the owner. | 
|  | g_install_attributes = nullptr; | 
|  | g_using_install_attributes_for_testing = false; | 
|  | } | 
|  |  | 
|  | InstallAttributes::InstallAttributes( | 
|  | InstallAttributesClient* userdataauth_client) | 
|  | : install_attributes_client_(userdataauth_client) {} | 
|  |  | 
|  | InstallAttributes::~InstallAttributes() = default; | 
|  |  | 
|  | void InstallAttributes::Init(const base::FilePath& cache_file) { | 
|  | DCHECK(!device_locked_); | 
|  |  | 
|  | // Mark the consistency check as running to ensure that LockDevice() is | 
|  | // blocked, but wait for the cryptohome service to be available before | 
|  | // actually calling TriggerConsistencyCheck(). | 
|  | consistency_check_running_ = true; | 
|  | install_attributes_client_->WaitForServiceToBeAvailable( | 
|  | base::BindOnce(&InstallAttributes::OnCryptohomeServiceInitiallyAvailable, | 
|  | weak_ptr_factory_.GetWeakPtr())); | 
|  |  | 
|  | if (!base::PathExists(cache_file)) { | 
|  | LOG_IF(WARNING, base::SysInfo::IsRunningOnChromeOS()) | 
|  | << "Install attributes missing, first sign in"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | device_locked_ = true; | 
|  |  | 
|  | char buf[16384]; | 
|  | int len = base::ReadFile(cache_file, buf, sizeof(buf)); | 
|  | if (len == -1 || len >= static_cast<int>(sizeof(buf))) { | 
|  | PLOG(ERROR) << "Failed to read " << cache_file.value(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | cryptohome::SerializedInstallAttributes install_attrs_proto; | 
|  | if (!install_attrs_proto.ParseFromArray(buf, len)) { | 
|  | LOG(ERROR) << "Failed to parse install attributes cache."; | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::map<std::string, std::string> attr_map; | 
|  | for (const auto& entry : install_attrs_proto.attributes()) { | 
|  | // The protobuf values contain terminating null characters, so we have to | 
|  | // sanitize the value here. | 
|  | attr_map.emplace(entry.name(), std::string(entry.value().c_str())); | 
|  | } | 
|  |  | 
|  | DecodeInstallAttributes(attr_map); | 
|  | } | 
|  |  | 
|  | void InstallAttributes::ReadImmutableAttributes(base::OnceClosure callback) { | 
|  | if (device_locked_) { | 
|  | std::move(callback).Run(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Get Install Attributes Status to know if it's ready. | 
|  | install_attributes_client_->InstallAttributesGetStatus( | 
|  | device_management::InstallAttributesGetStatusRequest(), | 
|  | base::BindOnce(&InstallAttributes::ReadAttributesIfReady, | 
|  | weak_ptr_factory_.GetWeakPtr(), std::move(callback))); | 
|  | } | 
|  |  | 
|  | void InstallAttributes::ReadAttributesIfReady( | 
|  | base::OnceClosure callback, | 
|  | std::optional<device_management::InstallAttributesGetStatusReply> reply) { | 
|  | base::ScopedClosureRunner callback_runner(std::move(callback)); | 
|  |  | 
|  | // Can't proceed if the call failed. | 
|  | if (!reply.has_value()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Can't proceed if not ready. | 
|  | if (reply->state() == ::device_management::InstallAttributesState::UNKNOWN || | 
|  | reply->state() == | 
|  | ::device_management::InstallAttributesState::TPM_NOT_OWNED) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | registration_mode_ = policy::DEVICE_MODE_NOT_SET; | 
|  | // TODO(b/183608597): Migrate this to check reply->state() to avoid the | 
|  | // blocking calls below. | 
|  | if (!install_attributes_util::InstallAttributesIsInvalid() && | 
|  | !install_attributes_util::InstallAttributesIsFirstInstall()) { | 
|  | device_locked_ = true; | 
|  |  | 
|  | static const char* const kEnterpriseAttributes[] = { | 
|  | kAttrEnterpriseDeviceId, kAttrEnterpriseDomain, kAttrEnterpriseRealm, | 
|  | kAttrEnterpriseMode,     kAttrEnterpriseOwned, | 
|  | }; | 
|  | std::map<std::string, std::string> attr_map; | 
|  | for (size_t i = 0; i < std::size(kEnterpriseAttributes); ++i) { | 
|  | std::string value; | 
|  | if (install_attributes_util::InstallAttributesGet( | 
|  | UNSAFE_TODO(kEnterpriseAttributes[i]), &value)) { | 
|  | attr_map[UNSAFE_TODO(kEnterpriseAttributes[i])] = value; | 
|  | } | 
|  | } | 
|  |  | 
|  | DecodeInstallAttributes(attr_map); | 
|  | } | 
|  | } | 
|  |  | 
|  | void InstallAttributes::SetBlockDevmodeInTpm( | 
|  | bool block_devmode, | 
|  | chromeos::DBusMethodCallback< | 
|  | device_management::SetFirmwareManagementParametersReply> callback) { | 
|  | DCHECK(!callback.is_null()); | 
|  | DCHECK(!device_locked_); | 
|  |  | 
|  | device_management::SetFirmwareManagementParametersRequest request; | 
|  | // Set the flags, according to enum FirmwareManagementParametersFlags from | 
|  | // rpc.proto if devmode is blocked. | 
|  | if (block_devmode) { | 
|  | request.mutable_fwmp()->set_flags( | 
|  | cryptohome::DEVELOPER_DISABLE_BOOT | | 
|  | cryptohome::DEVELOPER_DISABLE_CASE_CLOSED_DEBUGGING_UNLOCK); | 
|  | } | 
|  |  | 
|  | install_attributes_client_->SetFirmwareManagementParameters( | 
|  | request, std::move(callback)); | 
|  | } | 
|  |  | 
|  | void InstallAttributes::LockDevice(policy::DeviceMode device_mode, | 
|  | const std::string& domain, | 
|  | const std::string& realm, | 
|  | const std::string& device_id, | 
|  | LockResultCallback callback) { | 
|  | CHECK((device_mode == policy::DEVICE_MODE_ENTERPRISE && !domain.empty() && | 
|  | realm.empty() && !device_id.empty()) || | 
|  | (device_mode == policy::DEVICE_MODE_DEMO && !domain.empty() && | 
|  | realm.empty() && !device_id.empty())); | 
|  | DCHECK(callback); | 
|  | CHECK_EQ(device_lock_running_, false); | 
|  |  | 
|  | // Check for existing lock first. | 
|  | if (device_locked_) { | 
|  | if (device_mode != registration_mode_) { | 
|  | LOG(ERROR) << "Trying to re-lock with wrong mode: device_mode: " | 
|  | << device_mode | 
|  | << ", registration_mode: " << registration_mode_; | 
|  | std::move(callback).Run(LOCK_WRONG_MODE); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (domain != registration_domain_ || realm != registration_realm_ || | 
|  | device_id != registration_device_id_) { | 
|  | LOG(ERROR) << "Trying to re-lock with non-matching parameters."; | 
|  | std::move(callback).Run(LOCK_WRONG_DOMAIN); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Already locked in the right mode, signal success. | 
|  | std::move(callback).Run(LOCK_SUCCESS); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // In case the consistency check is still running, postpone the device locking | 
|  | // until it has finished.  This should not introduce additional delay since | 
|  | // device locking must wait for TPM initialization anyways. | 
|  | if (consistency_check_running_) { | 
|  | CHECK(post_check_action_.is_null()); | 
|  | post_check_action_ = base::BindOnce( | 
|  | &InstallAttributes::LockDevice, weak_ptr_factory_.GetWeakPtr(), | 
|  | device_mode, domain, realm, device_id, std::move(callback)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | device_lock_running_ = true; | 
|  | // Get Install Attributes Status to know if it's ready. | 
|  | install_attributes_client_->InstallAttributesGetStatus( | 
|  | device_management::InstallAttributesGetStatusRequest(), | 
|  | base::BindOnce(&InstallAttributes::LockDeviceIfAttributesIsReady, | 
|  | weak_ptr_factory_.GetWeakPtr(), device_mode, domain, realm, | 
|  | device_id, std::move(callback))); | 
|  | } | 
|  |  | 
|  | void InstallAttributes::LockDeviceIfAttributesIsReady( | 
|  | policy::DeviceMode device_mode, | 
|  | const std::string& domain, | 
|  | const std::string& realm, | 
|  | const std::string& device_id, | 
|  | LockResultCallback callback, | 
|  | std::optional<device_management::InstallAttributesGetStatusReply> reply) { | 
|  | if (!reply.has_value() || | 
|  | reply->state() == ::device_management::InstallAttributesState::UNKNOWN || | 
|  | reply->state() == | 
|  | ::device_management::InstallAttributesState::TPM_NOT_OWNED) { | 
|  | device_lock_running_ = false; | 
|  | std::move(callback).Run(LOCK_NOT_READY); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Clearing the TPM password seems to be always a good deal. At this point | 
|  | // install attributes is ready, which implies TPM readiness as well. | 
|  | chromeos::TpmManagerClient::Get()->ClearStoredOwnerPassword( | 
|  | ::tpm_manager::ClearStoredOwnerPasswordRequest(), | 
|  | base::BindOnce(&InstallAttributes::OnClearStoredOwnerPassword, | 
|  | weak_ptr_factory_.GetWeakPtr())); | 
|  |  | 
|  | // Make sure we really have a working InstallAttrs. | 
|  | if (install_attributes_util::InstallAttributesIsInvalid()) { | 
|  | LOG(ERROR) << "Install attributes invalid."; | 
|  | device_lock_running_ = false; | 
|  | std::move(callback).Run(LOCK_BACKEND_INVALID); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!install_attributes_util::InstallAttributesIsFirstInstall()) { | 
|  | LOG(ERROR) << "Install attributes already installed."; | 
|  | device_lock_running_ = false; | 
|  | std::move(callback).Run(LOCK_ALREADY_LOCKED); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Set values in the InstallAttrs. | 
|  | std::string mode = GetDeviceModeString(device_mode); | 
|  | if (!install_attributes_util::InstallAttributesSet(kAttrEnterpriseOwned, | 
|  | "true") || | 
|  | !install_attributes_util::InstallAttributesSet(kAttrEnterpriseMode, | 
|  | mode) || | 
|  | !install_attributes_util::InstallAttributesSet(kAttrEnterpriseDomain, | 
|  | domain) || | 
|  | !install_attributes_util::InstallAttributesSet(kAttrEnterpriseRealm, | 
|  | realm) || | 
|  | !install_attributes_util::InstallAttributesSet(kAttrEnterpriseDeviceId, | 
|  | device_id)) { | 
|  | LOG(ERROR) << "Failed writing attributes."; | 
|  | device_lock_running_ = false; | 
|  | std::move(callback).Run(LOCK_SET_ERROR); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!install_attributes_util::InstallAttributesFinalize() || | 
|  | install_attributes_util::InstallAttributesIsFirstInstall()) { | 
|  | LOG(ERROR) << "Failed locking."; | 
|  | device_lock_running_ = false; | 
|  | std::move(callback).Run(LOCK_FINALIZE_ERROR); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ReadImmutableAttributes( | 
|  | base::BindOnce(&InstallAttributes::OnReadImmutableAttributes, | 
|  | weak_ptr_factory_.GetWeakPtr(), device_mode, domain, realm, | 
|  | device_id, std::move(callback))); | 
|  | } | 
|  |  | 
|  | void InstallAttributes::OnReadImmutableAttributes(policy::DeviceMode mode, | 
|  | const std::string& domain, | 
|  | const std::string& realm, | 
|  | const std::string& device_id, | 
|  | LockResultCallback callback) { | 
|  | device_lock_running_ = false; | 
|  |  | 
|  | if (registration_mode_ != mode || registration_domain_ != domain || | 
|  | registration_realm_ != realm || registration_device_id_ != device_id) { | 
|  | LOG(ERROR) << "Locked data doesn't match."; | 
|  | std::move(callback).Run(LOCK_READBACK_ERROR); | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::move(callback).Run(LOCK_SUCCESS); | 
|  | } | 
|  |  | 
|  | bool InstallAttributes::IsEnterpriseManaged() const { | 
|  | if (!device_locked_) { | 
|  | return false; | 
|  | } | 
|  | return registration_mode_ == policy::DEVICE_MODE_ENTERPRISE || | 
|  | registration_mode_ == policy::DEVICE_MODE_DEMO; | 
|  | } | 
|  |  | 
|  | bool InstallAttributes::IsCloudManaged() const { | 
|  | if (!device_locked_) { | 
|  | return false; | 
|  | } | 
|  | return registration_mode_ == policy::DEVICE_MODE_ENTERPRISE || | 
|  | registration_mode_ == policy::DEVICE_MODE_DEMO; | 
|  | } | 
|  |  | 
|  | bool InstallAttributes::IsDeviceInDemoMode() const { | 
|  | bool is_demo_device_mode = | 
|  | registration_mode_ == policy::DeviceMode::DEVICE_MODE_DEMO; | 
|  | bool is_demo_device_domain = registration_domain_ == policy::kDemoModeDomain; | 
|  |  | 
|  | // We check device mode and domain to allow for dev/test | 
|  | // setup that is done by manual enrollment into demo domain. Device mode is | 
|  | // not set to DeviceMode::DEVICE_MODE_DEMO then. | 
|  | return is_demo_device_mode || is_demo_device_domain; | 
|  | } | 
|  |  | 
|  | void InstallAttributes::TriggerConsistencyCheck(int dbus_retries) { | 
|  | chromeos::TpmManagerClient::Get()->GetTpmNonsensitiveStatus( | 
|  | ::tpm_manager::GetTpmNonsensitiveStatusRequest(), | 
|  | base::BindOnce(&InstallAttributes::OnTpmStatusComplete, | 
|  | weak_ptr_factory_.GetWeakPtr(), dbus_retries)); | 
|  | } | 
|  |  | 
|  | void InstallAttributes::OnTpmStatusComplete( | 
|  | int dbus_retries_remaining, | 
|  | const ::tpm_manager::GetTpmNonsensitiveStatusReply& reply) { | 
|  | if (reply.status() != ::tpm_manager::STATUS_SUCCESS && | 
|  | dbus_retries_remaining) { | 
|  | LOG(WARNING) << "Failed to get tpm status reply; status: " | 
|  | << reply.status(); | 
|  | base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&InstallAttributes::TriggerConsistencyCheck, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | dbus_retries_remaining - 1), | 
|  | kDbusRetryInterval); | 
|  | return; | 
|  | } | 
|  |  | 
|  | base::HistogramBase::Sample32 state; | 
|  | // If we get the TPM status successfully, we are interested if install | 
|  | // attributes file exists (device_locked_), if the device is enrolled | 
|  | // (registration_mode_) and if the TPM is locked, meaning the TPM password | 
|  | // is deleted so the value from result is empty. | 
|  | if (reply.status() == ::tpm_manager::STATUS_SUCCESS) { | 
|  | const bool is_cloud_managed = | 
|  | registration_mode_ == policy::DEVICE_MODE_ENTERPRISE || | 
|  | registration_mode_ == policy::DEVICE_MODE_DEMO; | 
|  | state = (device_locked_ ? 1 : 0) | (is_cloud_managed ? 2 : 0) | | 
|  | (!reply.is_owner_password_present() ? 4 : 0); | 
|  | } else { | 
|  | LOG(WARNING) << "Failed to get TPM status after retries; status: " | 
|  | << reply.status(); | 
|  | state = 8; | 
|  | } | 
|  | UMA_HISTOGRAM_ENUMERATION("Enterprise.AttributesTPMConsistency", state, 9); | 
|  |  | 
|  | // Run any action (LockDevice call) that might have queued behind the | 
|  | // consistency check. | 
|  | consistency_check_running_ = false; | 
|  | if (post_check_action_) { | 
|  | std::move(post_check_action_).Run(); | 
|  | post_check_action_.Reset(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void InstallAttributes::OnClearStoredOwnerPassword( | 
|  | const ::tpm_manager::ClearStoredOwnerPasswordReply& reply) { | 
|  | LOG_IF(WARNING, reply.status() != ::tpm_manager::STATUS_SUCCESS) | 
|  | << "Failed to call ClearStoredOwnerPassword; status: " << reply.status(); | 
|  | } | 
|  |  | 
|  | // Warning: The values for these keys (but not the keys themselves) are stored | 
|  | // in the protobuf with a trailing zero.  Also note that some of these constants | 
|  | // have been copied to login_manager/device_policy_service.cc.  Please make sure | 
|  | // that all changes to the constants are reflected there as well. | 
|  | const char InstallAttributes::kConsumerDeviceMode[] = "consumer"; | 
|  | const char InstallAttributes::kEnterpriseDeviceMode[] = "enterprise"; | 
|  | const char InstallAttributes::kLegacyRetailDeviceMode[] = "kiosk"; | 
|  | const char InstallAttributes::kLegacyConsumerKioskDeviceMode[] = | 
|  | "consumer_kiosk"; | 
|  | const char InstallAttributes::kDemoDeviceMode[] = "demo_mode"; | 
|  |  | 
|  | const char InstallAttributes::kAttrEnterpriseDeviceId[] = | 
|  | "enterprise.device_id"; | 
|  | const char InstallAttributes::kAttrEnterpriseDomain[] = "enterprise.domain"; | 
|  | const char InstallAttributes::kAttrEnterpriseRealm[] = "enterprise.realm"; | 
|  | const char InstallAttributes::kAttrEnterpriseMode[] = "enterprise.mode"; | 
|  | const char InstallAttributes::kAttrEnterpriseOwned[] = "enterprise.owned"; | 
|  |  | 
|  | void InstallAttributes::OnCryptohomeServiceInitiallyAvailable( | 
|  | bool service_is_ready) { | 
|  | if (!service_is_ready) | 
|  | LOG(ERROR) << "Failed waiting for cryptohome D-Bus service availability."; | 
|  |  | 
|  | // Start the consistency check even if we failed to wait for availability; | 
|  | // hopefully the service will become available eventually. | 
|  | TriggerConsistencyCheck(kDbusRetryCount); | 
|  | } | 
|  |  | 
|  | std::string InstallAttributes::GetDeviceModeString(policy::DeviceMode mode) { | 
|  | switch (mode) { | 
|  | case policy::DEVICE_MODE_CONSUMER: | 
|  | return InstallAttributes::kConsumerDeviceMode; | 
|  | case policy::DEVICE_MODE_ENTERPRISE: | 
|  | return InstallAttributes::kEnterpriseDeviceMode; | 
|  | case policy::DEPRECATED_DEVICE_MODE_LEGACY_RETAIL_MODE: | 
|  | return InstallAttributes::kLegacyRetailDeviceMode; | 
|  | case policy::DEPRECATED_DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH: | 
|  | return InstallAttributes::kLegacyConsumerKioskDeviceMode; | 
|  | case policy::DEVICE_MODE_DEMO: | 
|  | return InstallAttributes::kDemoDeviceMode; | 
|  | case policy::DEVICE_MODE_PENDING: | 
|  | case policy::DEVICE_MODE_NOT_SET: | 
|  | break; | 
|  | } | 
|  | NOTREACHED() << "Invalid device mode: " << mode; | 
|  | } | 
|  |  | 
|  | policy::DeviceMode InstallAttributes::GetDeviceModeFromString( | 
|  | const std::string& mode) { | 
|  | if (mode == InstallAttributes::kConsumerDeviceMode) | 
|  | return policy::DEVICE_MODE_CONSUMER; | 
|  | if (mode == InstallAttributes::kEnterpriseDeviceMode) | 
|  | return policy::DEVICE_MODE_ENTERPRISE; | 
|  | if (mode == InstallAttributes::kLegacyRetailDeviceMode) | 
|  | return policy::DEPRECATED_DEVICE_MODE_LEGACY_RETAIL_MODE; | 
|  | if (mode == InstallAttributes::kLegacyConsumerKioskDeviceMode) { | 
|  | return policy::DEPRECATED_DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH; | 
|  | } | 
|  | if (mode == InstallAttributes::kDemoDeviceMode) | 
|  | return policy::DEVICE_MODE_DEMO; | 
|  | return policy::DEVICE_MODE_NOT_SET; | 
|  | } | 
|  |  | 
|  | void InstallAttributes::DecodeInstallAttributes( | 
|  | const std::map<std::string, std::string>& attr_map) { | 
|  | // Start from a clean slate. | 
|  | registration_mode_ = policy::DEVICE_MODE_NOT_SET; | 
|  | registration_domain_.clear(); | 
|  | registration_realm_.clear(); | 
|  | registration_device_id_.clear(); | 
|  |  | 
|  | const std::string enterprise_owned = | 
|  | ReadMapKey(attr_map, kAttrEnterpriseOwned); | 
|  | const std::string mode = ReadMapKey(attr_map, kAttrEnterpriseMode); | 
|  | const std::string domain = ReadMapKey(attr_map, kAttrEnterpriseDomain); | 
|  | const std::string realm = ReadMapKey(attr_map, kAttrEnterpriseRealm); | 
|  | const std::string device_id = ReadMapKey(attr_map, kAttrEnterpriseDeviceId); | 
|  |  | 
|  | if (enterprise_owned == "true") { | 
|  | registration_device_id_ = device_id; | 
|  |  | 
|  | // Set registration_mode_. | 
|  | registration_mode_ = GetDeviceModeFromString(mode); | 
|  | if (registration_mode_ != policy::DEVICE_MODE_ENTERPRISE && | 
|  | registration_mode_ != policy::DEVICE_MODE_DEMO) { | 
|  | if (!mode.empty()) { | 
|  | LOG(WARNING) << "Bad " << kAttrEnterpriseMode << ": " << mode; | 
|  | } | 
|  | registration_mode_ = policy::DEVICE_MODE_ENTERPRISE; | 
|  | } | 
|  |  | 
|  | if (registration_mode_ == policy::DEVICE_MODE_ENTERPRISE || | 
|  | registration_mode_ == policy::DEVICE_MODE_DEMO) { | 
|  | // Either set registration_domain_ ... | 
|  | WarnIfNonempty(attr_map, kAttrEnterpriseRealm); | 
|  | if (!domain.empty()) { | 
|  | // The canonicalization is for compatibility with earlier versions. | 
|  | registration_domain_ = gaia::CanonicalizeDomain(domain); | 
|  | } else { | 
|  | LOG(WARNING) << "Couldn't read domain."; | 
|  | } | 
|  | } else { | 
|  | // ... or set registration_realm_. | 
|  | WarnIfNonempty(attr_map, kAttrEnterpriseDomain); | 
|  | if (!realm.empty()) { | 
|  | registration_realm_ = realm; | 
|  | } else { | 
|  | LOG(WARNING) << "Couldn't read realm."; | 
|  | } | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | WarnIfNonempty(attr_map, kAttrEnterpriseOwned); | 
|  | WarnIfNonempty(attr_map, kAttrEnterpriseDomain); | 
|  | WarnIfNonempty(attr_map, kAttrEnterpriseRealm); | 
|  | WarnIfNonempty(attr_map, kAttrEnterpriseDeviceId); | 
|  |  | 
|  | registration_mode_ = policy::DEVICE_MODE_CONSUMER; | 
|  | } | 
|  |  | 
|  | }  // namespace ash |