// 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/settings/device_settings_service.h" #include "base/bind.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "base/time/time.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h" #include "chrome/browser/chromeos/settings/owner_key_util.h" #include "chrome/browser/chromeos/settings/session_manager_operation.h" #include "components/policy/core/common/cloud/cloud_policy_constants.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "crypto/rsa_private_key.h" namespace em = enterprise_management; namespace { // Delay between load retries when there was a validation error. // NOTE: This code is here to mitigate clock loss on some devices where policy // loads will fail with a validation error caused by RTC clock bing reset when // the battery is drained. int kLoadRetryDelayMs = 1000 * 5; // Maximal number of retries before we give up. Calculated to allow for 10 min // of retry time. int kMaxLoadRetries = (1000 * 60 * 10) / kLoadRetryDelayMs; } // namespace namespace chromeos { OwnerKey::OwnerKey(scoped_ptr<std::vector<uint8> > public_key, scoped_ptr<crypto::RSAPrivateKey> private_key) : public_key_(public_key.Pass()), private_key_(private_key.Pass()) {} OwnerKey::~OwnerKey() {} DeviceSettingsService::Observer::~Observer() {} static DeviceSettingsService* g_device_settings_service = NULL; // static void DeviceSettingsService::Initialize() { CHECK(!g_device_settings_service); g_device_settings_service = new DeviceSettingsService(); } // static bool DeviceSettingsService::IsInitialized() { return g_device_settings_service; } // static void DeviceSettingsService::Shutdown() { DCHECK(g_device_settings_service); delete g_device_settings_service; g_device_settings_service = NULL; } // static DeviceSettingsService* DeviceSettingsService::Get() { CHECK(g_device_settings_service); return g_device_settings_service; } DeviceSettingsService::DeviceSettingsService() : session_manager_client_(NULL), store_status_(STORE_SUCCESS), waiting_for_tpm_token_(true), owner_key_loaded_with_tpm_token_(false), load_retries_left_(kMaxLoadRetries), weak_factory_(this) { if (TPMTokenLoader::IsInitialized()) { waiting_for_tpm_token_ = !TPMTokenLoader::Get()->IsTPMTokenReady(); TPMTokenLoader::Get()->AddObserver(this); } } DeviceSettingsService::~DeviceSettingsService() { DCHECK(pending_operations_.empty()); if (TPMTokenLoader::IsInitialized()) TPMTokenLoader::Get()->RemoveObserver(this); } void DeviceSettingsService::SetSessionManager( SessionManagerClient* session_manager_client, scoped_refptr<OwnerKeyUtil> owner_key_util) { DCHECK(session_manager_client); DCHECK(owner_key_util.get()); DCHECK(!session_manager_client_); DCHECK(!owner_key_util_.get()); session_manager_client_ = session_manager_client; owner_key_util_ = owner_key_util; session_manager_client_->AddObserver(this); StartNextOperation(); } void DeviceSettingsService::UnsetSessionManager() { STLDeleteContainerPointers(pending_operations_.begin(), pending_operations_.end()); pending_operations_.clear(); if (session_manager_client_) session_manager_client_->RemoveObserver(this); session_manager_client_ = NULL; owner_key_util_ = NULL; } scoped_refptr<OwnerKey> DeviceSettingsService::GetOwnerKey() { return owner_key_; } void DeviceSettingsService::Load() { EnqueueLoad(false); } void DeviceSettingsService::SignAndStore( scoped_ptr<em::ChromeDeviceSettingsProto> new_settings, const base::Closure& callback) { scoped_ptr<em::PolicyData> new_policy = AssemblePolicy(*new_settings); if (!new_policy) { HandleError(STORE_POLICY_ERROR, callback); return; } Enqueue( new SignAndStoreSettingsOperation( base::Bind(&DeviceSettingsService::HandleCompletedOperation, weak_factory_.GetWeakPtr(), callback), new_policy.Pass())); } void DeviceSettingsService::SetManagementSettings( em::PolicyData::ManagementMode management_mode, const std::string& request_token, const std::string& device_id, const base::Closure& callback) { if (!CheckManagementModeTransition(management_mode)) { LOG(ERROR) << "Invalid management mode transition: current mode = " << GetManagementMode() << ", new mode = " << management_mode; HandleError(STORE_POLICY_ERROR, callback); return; } scoped_ptr<em::PolicyData> policy = AssemblePolicy(*device_settings_); if (!policy) { HandleError(STORE_POLICY_ERROR, callback); return; } policy->set_management_mode(management_mode); policy->set_request_token(request_token); policy->set_device_id(device_id); Enqueue( new SignAndStoreSettingsOperation( base::Bind(&DeviceSettingsService::HandleCompletedOperation, weak_factory_.GetWeakPtr(), callback), policy.Pass())); } void DeviceSettingsService::Store(scoped_ptr<em::PolicyFetchResponse> policy, const base::Closure& callback) { Enqueue( new StoreSettingsOperation( base::Bind(&DeviceSettingsService::HandleCompletedOperation, weak_factory_.GetWeakPtr(), callback), policy.Pass())); } DeviceSettingsService::OwnershipStatus DeviceSettingsService::GetOwnershipStatus() { if (owner_key_.get()) return owner_key_->public_key() ? OWNERSHIP_TAKEN : OWNERSHIP_NONE; return OWNERSHIP_UNKNOWN; } void DeviceSettingsService::GetOwnershipStatusAsync( const OwnershipStatusCallback& callback) { if (owner_key_.get()) { // If there is a key, report status immediately. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind( callback, owner_key_->public_key() ? OWNERSHIP_TAKEN : OWNERSHIP_NONE)); } else { // If the key hasn't been loaded yet, enqueue the callback to be fired when // the next SessionManagerOperation completes. If no operation is pending, // start a load operation to fetch the key and report the result. pending_ownership_status_callbacks_.push_back(callback); if (pending_operations_.empty()) EnqueueLoad(false); } } bool DeviceSettingsService::HasPrivateOwnerKey() { return owner_key_.get() && owner_key_->private_key(); } void DeviceSettingsService::IsCurrentUserOwnerAsync( const IsCurrentUserOwnerCallback& callback) { if (owner_key_loaded_with_tpm_token_) { // If the current owner key was loaded while the certificates were loaded, // or the certificate loader is not initialized, in which case the private // key cannot be set, report status immediately. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(callback, HasPrivateOwnerKey())); } else { // If the key hasn't been loaded with the known certificates, enqueue the // callback to be fired when the next SessionManagerOperation completes in // an environment where the certificates are loaded. There is no need to // start a new operation, as the reload operation will be started when the // certificates are loaded. pending_is_current_user_owner_callbacks_.push_back(callback); } } void DeviceSettingsService::InitOwner(const std::string& username, crypto::ScopedPK11Slot slot) { if (!username_.empty()) return; username_ = username; slot_ = slot.Pass(); // The private key may have become available, so force a key reload. owner_key_ = NULL; EnsureReload(true); } const std::string& DeviceSettingsService::GetUsername() const { return username_; } void DeviceSettingsService::AddObserver(Observer* observer) { observers_.AddObserver(observer); } void DeviceSettingsService::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } void DeviceSettingsService::OwnerKeySet(bool success) { if (!success) { LOG(ERROR) << "Owner key change failed."; return; } owner_key_ = NULL; EnsureReload(true); } void DeviceSettingsService::PropertyChangeComplete(bool success) { if (!success) { LOG(ERROR) << "Policy update failed."; return; } EnsureReload(false); } void DeviceSettingsService::OnTPMTokenReady() { waiting_for_tpm_token_ = false; // TPMTokenLoader initializes the TPM and NSS database which is necessary to // determine ownership. Force a reload once we know these are initialized. EnsureReload(true); } void DeviceSettingsService::Enqueue(SessionManagerOperation* operation) { pending_operations_.push_back(operation); if (pending_operations_.front() == operation) StartNextOperation(); } void DeviceSettingsService::EnqueueLoad(bool force_key_load) { SessionManagerOperation* operation = new LoadSettingsOperation( base::Bind(&DeviceSettingsService::HandleCompletedOperation, weak_factory_.GetWeakPtr(), base::Closure())); operation->set_force_key_load(force_key_load); operation->set_username(username_); operation->set_slot(slot_.get()); Enqueue(operation); } void DeviceSettingsService::EnsureReload(bool force_key_load) { if (!pending_operations_.empty()) { pending_operations_.front()->set_username(username_); pending_operations_.front()->set_slot(slot_.get()); pending_operations_.front()->RestartLoad(force_key_load); } else { EnqueueLoad(force_key_load); } } void DeviceSettingsService::StartNextOperation() { if (!pending_operations_.empty() && session_manager_client_ && owner_key_util_.get()) { pending_operations_.front()->Start(session_manager_client_, owner_key_util_, owner_key_); } } void DeviceSettingsService::HandleCompletedOperation( const base::Closure& callback, SessionManagerOperation* operation, Status status) { DCHECK_EQ(operation, pending_operations_.front()); store_status_ = status; OwnershipStatus ownership_status = OWNERSHIP_UNKNOWN; bool is_owner = false; scoped_refptr<OwnerKey> new_key(operation->owner_key()); if (new_key.get()) { ownership_status = new_key->public_key() ? OWNERSHIP_TAKEN : OWNERSHIP_NONE; is_owner = (new_key->private_key() != NULL); } else { NOTREACHED() << "Failed to determine key status."; } bool new_owner_key = false; if (owner_key_.get() != new_key.get()) { owner_key_ = new_key; new_owner_key = true; } if (status == STORE_SUCCESS) { policy_data_ = operation->policy_data().Pass(); device_settings_ = operation->device_settings().Pass(); load_retries_left_ = kMaxLoadRetries; } else if (status != STORE_KEY_UNAVAILABLE) { LOG(ERROR) << "Session manager operation failed: " << status; // Validation errors can be temporary if the rtc has gone on holiday for a // short while. So we will retry such loads for up to 10 minutes. if (status == STORE_TEMP_VALIDATION_ERROR) { if (load_retries_left_ > 0) { load_retries_left_--; LOG(ERROR) << "A re-load has been scheduled due to a validation error."; content::BrowserThread::PostDelayedTask( content::BrowserThread::UI, FROM_HERE, base::Bind(&DeviceSettingsService::Load, base::Unretained(this)), base::TimeDelta::FromMilliseconds(kLoadRetryDelayMs)); } else { // Once we've given up retrying, the validation error is not temporary // anymore. store_status_ = STORE_VALIDATION_ERROR; } } } if (new_owner_key) { FOR_EACH_OBSERVER(Observer, observers_, OwnershipStatusChanged()); content::NotificationService::current()->Notify( chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED, content::Source<DeviceSettingsService>(this), content::NotificationService::NoDetails()); } FOR_EACH_OBSERVER(Observer, observers_, DeviceSettingsUpdated()); std::vector<OwnershipStatusCallback> callbacks; callbacks.swap(pending_ownership_status_callbacks_); for (std::vector<OwnershipStatusCallback>::iterator iter(callbacks.begin()); iter != callbacks.end(); ++iter) { iter->Run(ownership_status); } if (!waiting_for_tpm_token_) { owner_key_loaded_with_tpm_token_ = true; std::vector<IsCurrentUserOwnerCallback> is_owner_callbacks; is_owner_callbacks.swap(pending_is_current_user_owner_callbacks_); for (std::vector<IsCurrentUserOwnerCallback>::iterator iter( is_owner_callbacks.begin()); iter != is_owner_callbacks.end(); ++iter) { iter->Run(is_owner); } } // The completion callback happens after the notification so clients can // filter self-triggered updates. if (!callback.is_null()) callback.Run(); // Only remove the pending operation here, so new operations triggered by any // of the callbacks above are queued up properly. pending_operations_.pop_front(); delete operation; StartNextOperation(); } void DeviceSettingsService::HandleError(Status status, const base::Closure& callback) { store_status_ = status; LOG(ERROR) << "Session manager operation failed: " << status; FOR_EACH_OBSERVER(Observer, observers_, DeviceSettingsUpdated()); // The completion callback happens after the notification so clients can // filter self-triggered updates. if (!callback.is_null()) callback.Run(); } scoped_ptr<em::PolicyData> DeviceSettingsService::AssemblePolicy( const em::ChromeDeviceSettingsProto& settings) const { scoped_ptr<em::PolicyData> policy(new em::PolicyData()); if (policy_data_) { // Preserve management settings. if (policy_data_->has_management_mode()) policy->set_management_mode(policy_data_->management_mode()); if (policy_data_->has_request_token()) policy->set_request_token(policy_data_->request_token()); if (policy_data_->has_device_id()) policy->set_device_id(policy_data_->device_id()); } else { // If there's no previous policy data, this is the first time the device // setting is set. We set the management mode to NOT_MANAGED initially. policy->set_management_mode(em::PolicyData::NOT_MANAGED); } policy->set_policy_type(policy::dm_protocol::kChromeDevicePolicyType); policy->set_timestamp((base::Time::Now() - base::Time::UnixEpoch()). InMilliseconds()); policy->set_username(username_); if (!settings.SerializeToString(policy->mutable_policy_value())) return scoped_ptr<em::PolicyData>(); return policy.Pass(); } em::PolicyData::ManagementMode DeviceSettingsService::GetManagementMode() const { if (policy_data_ && policy_data_->has_management_mode()) return policy_data_->management_mode(); return em::PolicyData::NOT_MANAGED; } bool DeviceSettingsService::CheckManagementModeTransition( em::PolicyData::ManagementMode new_mode) const { em::PolicyData::ManagementMode current_mode = GetManagementMode(); // Mode is not changed. if (current_mode == new_mode) return true; switch (current_mode) { case em::PolicyData::NOT_MANAGED: // For consumer management enrollment. return new_mode == em::PolicyData::CONSUMER_MANAGED; case em::PolicyData::ENTERPRISE_MANAGED: // Management mode cannot be set when it is currently ENTERPRISE_MANAGED. return false; case em::PolicyData::CONSUMER_MANAGED: // For consumer management unenrollment. return new_mode == em::PolicyData::NOT_MANAGED; } NOTREACHED(); return false; } ScopedTestDeviceSettingsService::ScopedTestDeviceSettingsService() { DeviceSettingsService::Initialize(); } ScopedTestDeviceSettingsService::~ScopedTestDeviceSettingsService() { // Clean pending operations. DeviceSettingsService::Get()->UnsetSessionManager(); DeviceSettingsService::Shutdown(); } } // namespace chromeos