| // Copyright (c) 2012 The Chromium 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 "chrome/browser/chromeos/policy/enterprise_install_attributes.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/file_util.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/message_loop/message_loop.h" |
| #include "chrome/browser/policy/proto/chromeos/install_attributes.pb.h" |
| #include "chromeos/cryptohome/cryptohome_util.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| |
| namespace policy { |
| |
| namespace cryptohome_util = chromeos::cryptohome_util; |
| |
| namespace { |
| |
| // Translates DeviceMode constants to strings used in the lockbox. |
| std::string GetDeviceModeString(DeviceMode mode) { |
| switch (mode) { |
| case DEVICE_MODE_CONSUMER: |
| return EnterpriseInstallAttributes::kConsumerDeviceMode; |
| case DEVICE_MODE_ENTERPRISE: |
| return EnterpriseInstallAttributes::kEnterpiseDeviceMode; |
| case DEVICE_MODE_RETAIL_KIOSK: |
| return EnterpriseInstallAttributes::kRetailKioskDeviceMode; |
| case DEVICE_MODE_CONSUMER_KIOSK: |
| return EnterpriseInstallAttributes::kConsumerKioskDeviceMode; |
| case DEVICE_MODE_PENDING: |
| case DEVICE_MODE_NOT_SET: |
| break; |
| } |
| NOTREACHED() << "Invalid device mode: " << mode; |
| return EnterpriseInstallAttributes::kUnknownDeviceMode; |
| } |
| |
| // Translates strings used in the lockbox to DeviceMode values. |
| DeviceMode GetDeviceModeFromString( |
| const std::string& mode) { |
| if (mode == EnterpriseInstallAttributes::kConsumerDeviceMode) |
| return DEVICE_MODE_CONSUMER; |
| else if (mode == EnterpriseInstallAttributes::kEnterpiseDeviceMode) |
| return DEVICE_MODE_ENTERPRISE; |
| else if (mode == EnterpriseInstallAttributes::kRetailKioskDeviceMode) |
| return DEVICE_MODE_RETAIL_KIOSK; |
| else if (mode == EnterpriseInstallAttributes::kConsumerKioskDeviceMode) |
| return DEVICE_MODE_CONSUMER_KIOSK; |
| NOTREACHED() << "Unknown device mode string: " << mode; |
| return DEVICE_MODE_NOT_SET; |
| } |
| |
| bool ReadMapKey(const std::map<std::string, std::string>& map, |
| const std::string& key, |
| std::string* value) { |
| std::map<std::string, std::string>::const_iterator entry = map.find(key); |
| if (entry == map.end()) |
| return false; |
| |
| *value = entry->second; |
| return true; |
| } |
| |
| } // namespace |
| |
| const char EnterpriseInstallAttributes::kConsumerDeviceMode[] = "consumer"; |
| const char EnterpriseInstallAttributes::kEnterpiseDeviceMode[] = "enterprise"; |
| const char EnterpriseInstallAttributes::kRetailKioskDeviceMode[] = "kiosk"; |
| const char EnterpriseInstallAttributes::kConsumerKioskDeviceMode[] = |
| "consumer_kiosk"; |
| const char EnterpriseInstallAttributes::kUnknownDeviceMode[] = "unknown"; |
| |
| const char EnterpriseInstallAttributes::kAttrEnterpriseDeviceId[] = |
| "enterprise.device_id"; |
| const char EnterpriseInstallAttributes::kAttrEnterpriseDomain[] = |
| "enterprise.domain"; |
| const char EnterpriseInstallAttributes::kAttrEnterpriseMode[] = |
| "enterprise.mode"; |
| const char EnterpriseInstallAttributes::kAttrEnterpriseOwned[] = |
| "enterprise.owned"; |
| const char EnterpriseInstallAttributes::kAttrEnterpriseUser[] = |
| "enterprise.user"; |
| const char EnterpriseInstallAttributes::kAttrConsumerKioskEnabled[] = |
| "consumer.app_kiosk_enabled"; |
| |
| EnterpriseInstallAttributes::EnterpriseInstallAttributes( |
| chromeos::CryptohomeClient* cryptohome_client) |
| : device_locked_(false), |
| registration_mode_(DEVICE_MODE_PENDING), |
| cryptohome_client_(cryptohome_client), |
| weak_ptr_factory_(this) {} |
| |
| EnterpriseInstallAttributes::~EnterpriseInstallAttributes() {} |
| |
| void EnterpriseInstallAttributes::ReadCacheFile( |
| const base::FilePath& cache_file) { |
| if (device_locked_ || !base::PathExists(cache_file)) |
| return; |
| |
| device_locked_ = true; |
| |
| char buf[16384]; |
| int len = file_util::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; |
| } |
| |
| google::protobuf::RepeatedPtrField< |
| const cryptohome::SerializedInstallAttributes::Attribute>::iterator entry; |
| std::map<std::string, std::string> attr_map; |
| for (entry = install_attrs_proto.attributes().begin(); |
| entry != install_attrs_proto.attributes().end(); |
| ++entry) { |
| // The protobuf values unfortunately contain terminating null characters, so |
| // we have to sanitize the value here. |
| attr_map.insert(std::make_pair(entry->name(), |
| std::string(entry->value().c_str()))); |
| } |
| |
| DecodeInstallAttributes(attr_map); |
| } |
| |
| void EnterpriseInstallAttributes::ReadImmutableAttributes( |
| const base::Closure& callback) { |
| if (device_locked_) { |
| callback.Run(); |
| return; |
| } |
| |
| cryptohome_client_->InstallAttributesIsReady( |
| base::Bind(&EnterpriseInstallAttributes::ReadAttributesIfReady, |
| weak_ptr_factory_.GetWeakPtr(), |
| callback)); |
| } |
| |
| void EnterpriseInstallAttributes::ReadAttributesIfReady( |
| const base::Closure& callback, |
| chromeos::DBusMethodCallStatus call_status, |
| bool result) { |
| if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS && result) { |
| registration_mode_ = DEVICE_MODE_NOT_SET; |
| if (!cryptohome_util::InstallAttributesIsInvalid() && |
| !cryptohome_util::InstallAttributesIsFirstInstall()) { |
| device_locked_ = true; |
| |
| static const char* kEnterpriseAttributes[] = { |
| kAttrEnterpriseDeviceId, |
| kAttrEnterpriseDomain, |
| kAttrEnterpriseMode, |
| kAttrEnterpriseOwned, |
| kAttrEnterpriseUser, |
| kAttrConsumerKioskEnabled, |
| }; |
| std::map<std::string, std::string> attr_map; |
| for (size_t i = 0; i < arraysize(kEnterpriseAttributes); ++i) { |
| std::string value; |
| if (cryptohome_util::InstallAttributesGet(kEnterpriseAttributes[i], |
| &value)) |
| attr_map[kEnterpriseAttributes[i]] = value; |
| } |
| |
| DecodeInstallAttributes(attr_map); |
| } |
| } |
| callback.Run(); |
| } |
| |
| void EnterpriseInstallAttributes::LockDevice( |
| const std::string& user, |
| DeviceMode device_mode, |
| const std::string& device_id, |
| const LockResultCallback& callback) { |
| DCHECK(!callback.is_null()); |
| CHECK_NE(device_mode, DEVICE_MODE_PENDING); |
| CHECK_NE(device_mode, DEVICE_MODE_NOT_SET); |
| |
| // Check for existing lock first. |
| if (device_locked_) { |
| if (device_mode == DEVICE_MODE_CONSUMER_KIOSK) { |
| callback.Run((registration_mode_ == device_mode) ? LOCK_SUCCESS : |
| LOCK_NOT_READY); |
| } else { |
| std::string domain = gaia::ExtractDomainName(user); |
| callback.Run( |
| (!registration_domain_.empty() && domain == registration_domain_) ? |
| LOCK_SUCCESS : LOCK_WRONG_USER); |
| } |
| return; |
| } |
| |
| cryptohome_client_->InstallAttributesIsReady( |
| base::Bind(&EnterpriseInstallAttributes::LockDeviceIfAttributesIsReady, |
| weak_ptr_factory_.GetWeakPtr(), |
| user, |
| device_mode, |
| device_id, |
| callback)); |
| } |
| |
| void EnterpriseInstallAttributes::LockDeviceIfAttributesIsReady( |
| const std::string& user, |
| DeviceMode device_mode, |
| const std::string& device_id, |
| const LockResultCallback& callback, |
| chromeos::DBusMethodCallStatus call_status, |
| bool result) { |
| if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS || !result) { |
| callback.Run(LOCK_NOT_READY); |
| return; |
| } |
| |
| // Clearing the TPM password seems to be always a good deal. |
| if (cryptohome_util::TpmIsEnabled() && |
| !cryptohome_util::TpmIsBeingOwned() && |
| cryptohome_util::TpmIsOwned()) { |
| cryptohome_client_->CallTpmClearStoredPasswordAndBlock(); |
| } |
| |
| // Make sure we really have a working InstallAttrs. |
| if (cryptohome_util::InstallAttributesIsInvalid()) { |
| LOG(ERROR) << "Install attributes invalid."; |
| callback.Run(LOCK_BACKEND_ERROR); |
| return; |
| } |
| |
| if (!cryptohome_util::InstallAttributesIsFirstInstall()) { |
| callback.Run(LOCK_WRONG_USER); |
| return; |
| } |
| |
| std::string mode = GetDeviceModeString(device_mode); |
| std::string registration_user; |
| if (!user.empty()) |
| registration_user = gaia::CanonicalizeEmail(user); |
| |
| if (device_mode == DEVICE_MODE_CONSUMER_KIOSK) { |
| // Set values in the InstallAttrs and lock it. |
| if (!cryptohome_util::InstallAttributesSet(kAttrConsumerKioskEnabled, |
| "true")) { |
| LOG(ERROR) << "Failed writing attributes"; |
| callback.Run(LOCK_BACKEND_ERROR); |
| return; |
| } |
| } else { |
| std::string domain = gaia::ExtractDomainName(registration_user); |
| // Set values in the InstallAttrs and lock it. |
| if (!cryptohome_util::InstallAttributesSet(kAttrEnterpriseOwned, "true") || |
| !cryptohome_util::InstallAttributesSet(kAttrEnterpriseUser, |
| registration_user) || |
| !cryptohome_util::InstallAttributesSet(kAttrEnterpriseDomain, |
| domain) || |
| !cryptohome_util::InstallAttributesSet(kAttrEnterpriseMode, mode) || |
| !cryptohome_util::InstallAttributesSet(kAttrEnterpriseDeviceId, |
| device_id)) { |
| LOG(ERROR) << "Failed writing attributes"; |
| callback.Run(LOCK_BACKEND_ERROR); |
| return; |
| } |
| } |
| |
| if (!cryptohome_util::InstallAttributesFinalize() || |
| cryptohome_util::InstallAttributesIsFirstInstall()) { |
| LOG(ERROR) << "Failed locking."; |
| callback.Run(LOCK_BACKEND_ERROR); |
| return; |
| } |
| |
| ReadImmutableAttributes( |
| base::Bind(&EnterpriseInstallAttributes::OnReadImmutableAttributes, |
| weak_ptr_factory_.GetWeakPtr(), |
| registration_user, |
| callback)); |
| } |
| |
| void EnterpriseInstallAttributes::OnReadImmutableAttributes( |
| const std::string& registration_user, |
| const LockResultCallback& callback) { |
| |
| if (GetRegistrationUser() != registration_user) { |
| LOG(ERROR) << "Locked data doesn't match"; |
| callback.Run(LOCK_BACKEND_ERROR); |
| return; |
| } |
| |
| callback.Run(LOCK_SUCCESS); |
| } |
| |
| bool EnterpriseInstallAttributes::IsEnterpriseDevice() { |
| return device_locked_ && !registration_user_.empty(); |
| } |
| |
| bool EnterpriseInstallAttributes::IsConsumerKioskDevice() { |
| return device_locked_ && registration_mode_ == DEVICE_MODE_CONSUMER_KIOSK; |
| } |
| |
| std::string EnterpriseInstallAttributes::GetRegistrationUser() { |
| if (!device_locked_) |
| return std::string(); |
| |
| return registration_user_; |
| } |
| |
| std::string EnterpriseInstallAttributes::GetDomain() { |
| if (!IsEnterpriseDevice()) |
| return std::string(); |
| |
| return registration_domain_; |
| } |
| |
| std::string EnterpriseInstallAttributes::GetDeviceId() { |
| if (!IsEnterpriseDevice()) |
| return std::string(); |
| |
| return registration_device_id_; |
| } |
| |
| DeviceMode EnterpriseInstallAttributes::GetMode() { |
| return registration_mode_; |
| } |
| |
| void EnterpriseInstallAttributes::DecodeInstallAttributes( |
| const std::map<std::string, std::string>& attr_map) { |
| std::string enterprise_owned; |
| std::string enterprise_user; |
| std::string consumer_kiosk_enabled; |
| if (ReadMapKey(attr_map, kAttrEnterpriseOwned, &enterprise_owned) && |
| ReadMapKey(attr_map, kAttrEnterpriseUser, &enterprise_user) && |
| enterprise_owned == "true" && |
| !enterprise_user.empty()) { |
| registration_user_ = gaia::CanonicalizeEmail(enterprise_user); |
| |
| // Initialize the mode to the legacy enterprise mode here and update |
| // below if more information is present. |
| registration_mode_ = DEVICE_MODE_ENTERPRISE; |
| |
| // If we could extract basic setting we should try to extract the |
| // extended ones too. We try to set these to defaults as good as |
| // as possible if present, which could happen for device enrolled in |
| // pre 19 revisions of the code, before these new attributes were added. |
| if (ReadMapKey(attr_map, kAttrEnterpriseDomain, ®istration_domain_)) |
| registration_domain_ = gaia::CanonicalizeDomain(registration_domain_); |
| else |
| registration_domain_ = gaia::ExtractDomainName(registration_user_); |
| |
| ReadMapKey(attr_map, kAttrEnterpriseDeviceId, ®istration_device_id_); |
| |
| std::string mode; |
| if (ReadMapKey(attr_map, kAttrEnterpriseMode, &mode)) |
| registration_mode_ = GetDeviceModeFromString(mode); |
| } else if (ReadMapKey(attr_map, |
| kAttrConsumerKioskEnabled, |
| &consumer_kiosk_enabled) && |
| consumer_kiosk_enabled == "true") { |
| registration_mode_ = DEVICE_MODE_CONSUMER_KIOSK; |
| } else if (enterprise_user.empty() && enterprise_owned != "true") { |
| // |registration_user_| is empty on consumer devices. |
| registration_mode_ = DEVICE_MODE_CONSUMER; |
| } |
| } |
| |
| } // namespace policy |