// Copyright (c) 2011 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/policy/cloud_policy_controller.h" #include <algorithm> #include "base/logging.h" #include "base/message_loop.h" #include "base/rand_util.h" #include "base/string_util.h" #include "chrome/browser/policy/cloud_policy_cache_base.h" #include "chrome/browser/policy/cloud_policy_subsystem.h" #include "chrome/browser/policy/device_management_backend.h" #include "chrome/browser/policy/device_management_service.h" #include "chrome/browser/policy/proto/device_management_constants.h" // Domain names that are known not to be managed. // We don't register the device when such a user logs in. static const char* kNonManagedDomains[] = { "@googlemail.com", "@gmail.com" }; // Checks the domain part of the given username against the list of known // non-managed domain names. Returns false if |username| is empty or // in a domain known not to be managed. static bool CanBeInManagedDomain(const std::string& username) { if (username.empty()) { // This means incognito user in case of ChromiumOS and // no logged-in user in case of Chromium (SigninService). return false; } for (size_t i = 0; i < arraysize(kNonManagedDomains); i++) { if (EndsWith(username, kNonManagedDomains[i], true)) { return false; } } return true; } namespace policy { namespace em = enterprise_management; // The maximum ratio in percent of the policy refresh rate we use for adjusting // the policy refresh time instant. The rationale is to avoid load spikes from // many devices that were set up in sync for some reason. static const int kPolicyRefreshDeviationFactorPercent = 10; // Maximum deviation we are willing to accept. static const int64 kPolicyRefreshDeviationMaxInMilliseconds = 30 * 60 * 1000; // These are the base values for delays before retrying after an error. They // will be doubled each time they are used. static const int64 kPolicyRefreshErrorDelayInMilliseconds = 5 * 60 * 1000; // 5 minutes // Default value for the policy refresh rate. static const int kPolicyRefreshRateInMilliseconds = 3 * 60 * 60 * 1000; // 3 hours. CloudPolicyController::CloudPolicyController( DeviceManagementService* service, CloudPolicyCacheBase* cache, DeviceTokenFetcher* token_fetcher, CloudPolicyIdentityStrategy* identity_strategy, PolicyNotifier* notifier) : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { Initialize(service, cache, token_fetcher, identity_strategy, notifier, kPolicyRefreshRateInMilliseconds, kPolicyRefreshDeviationFactorPercent, kPolicyRefreshDeviationMaxInMilliseconds, kPolicyRefreshErrorDelayInMilliseconds); } CloudPolicyController::~CloudPolicyController() { token_fetcher_->RemoveObserver(this); identity_strategy_->RemoveObserver(this); CancelDelayedWork(); } void CloudPolicyController::SetRefreshRate(int64 refresh_rate_milliseconds) { policy_refresh_rate_ms_ = refresh_rate_milliseconds; // Reschedule the refresh task if necessary. if (state_ == STATE_POLICY_VALID) SetState(STATE_POLICY_VALID); } void CloudPolicyController::Retry() { CancelDelayedWork(); DoWork(); } void CloudPolicyController::StopAutoRetry() { CancelDelayedWork(); backend_.reset(); } void CloudPolicyController::HandlePolicyResponse( const em::DevicePolicyResponse& response) { if (response.response_size() > 0) { if (response.response_size() > 1) { LOG(WARNING) << "More than one policy in the response of the device " << "management server, discarding."; } if (response.response(0).error_code() != DeviceManagementBackend::kErrorServicePolicyNotFound) { cache_->SetPolicy(response.response(0)); SetState(STATE_POLICY_VALID); } else { SetState(STATE_POLICY_UNAVAILABLE); } } } void CloudPolicyController::OnError(DeviceManagementBackend::ErrorCode code) { switch (code) { case DeviceManagementBackend::kErrorServiceDeviceNotFound: case DeviceManagementBackend::kErrorServiceManagementTokenInvalid: { LOG(WARNING) << "The device token was either invalid or unknown to the " << "device manager, re-registering device."; // Will retry fetching a token but gracefully backing off. SetState(STATE_TOKEN_ERROR); break; } case DeviceManagementBackend::kErrorServiceManagementNotSupported: { VLOG(1) << "The device is no longer managed."; token_fetcher_->SetUnmanagedState(); SetState(STATE_TOKEN_UNMANAGED); break; } case DeviceManagementBackend::kErrorServicePolicyNotFound: case DeviceManagementBackend::kErrorRequestInvalid: case DeviceManagementBackend::kErrorServiceActivationPending: case DeviceManagementBackend::kErrorResponseDecoding: case DeviceManagementBackend::kErrorHttpStatus: { VLOG(1) << "An error in the communication with the policy server occurred" << ", will retry in a few hours."; SetState(STATE_POLICY_UNAVAILABLE); break; } case DeviceManagementBackend::kErrorRequestFailed: case DeviceManagementBackend::kErrorTemporaryUnavailable: { VLOG(1) << "A temporary error in the communication with the policy server" << " occurred."; // Will retry last operation but gracefully backing off. SetState(STATE_POLICY_ERROR); } } } void CloudPolicyController::OnDeviceTokenAvailable() { identity_strategy_->OnDeviceTokenAvailable(token_fetcher_->GetDeviceToken()); } void CloudPolicyController::OnDeviceTokenChanged() { if (identity_strategy_->GetDeviceToken().empty()) SetState(STATE_TOKEN_UNAVAILABLE); else SetState(STATE_TOKEN_VALID); } void CloudPolicyController::OnCredentialsChanged() { effective_policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms_; SetState(STATE_TOKEN_UNAVAILABLE); } CloudPolicyController::CloudPolicyController( DeviceManagementService* service, CloudPolicyCacheBase* cache, DeviceTokenFetcher* token_fetcher, CloudPolicyIdentityStrategy* identity_strategy, PolicyNotifier* notifier, int64 policy_refresh_rate_ms, int policy_refresh_deviation_factor_percent, int64 policy_refresh_deviation_max_ms, int64 policy_refresh_error_delay_ms) : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { Initialize(service, cache, token_fetcher, identity_strategy, notifier, policy_refresh_rate_ms, policy_refresh_deviation_factor_percent, policy_refresh_deviation_max_ms, policy_refresh_error_delay_ms); } void CloudPolicyController::Initialize( DeviceManagementService* service, CloudPolicyCacheBase* cache, DeviceTokenFetcher* token_fetcher, CloudPolicyIdentityStrategy* identity_strategy, PolicyNotifier* notifier, int64 policy_refresh_rate_ms, int policy_refresh_deviation_factor_percent, int64 policy_refresh_deviation_max_ms, int64 policy_refresh_error_delay_ms) { DCHECK(cache); service_ = service; cache_ = cache; token_fetcher_ = token_fetcher; identity_strategy_ = identity_strategy; notifier_ = notifier; state_ = STATE_TOKEN_UNAVAILABLE; delayed_work_task_ = NULL; policy_refresh_rate_ms_ = policy_refresh_rate_ms; policy_refresh_deviation_factor_percent_ = policy_refresh_deviation_factor_percent; policy_refresh_deviation_max_ms_ = policy_refresh_deviation_max_ms; policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms; effective_policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms; token_fetcher_->AddObserver(this); identity_strategy_->AddObserver(this); if (!identity_strategy_->GetDeviceToken().empty()) SetState(STATE_TOKEN_VALID); else SetState(STATE_TOKEN_UNAVAILABLE); } void CloudPolicyController::FetchToken() { std::string username; std::string auth_token; std::string device_id = identity_strategy_->GetDeviceID(); std::string machine_id = identity_strategy_->GetMachineID(); std::string machine_model = identity_strategy_->GetMachineModel(); em::DeviceRegisterRequest_Type policy_type = identity_strategy_->GetPolicyRegisterType(); if (identity_strategy_->GetCredentials(&username, &auth_token) && CanBeInManagedDomain(username)) { token_fetcher_->FetchToken(auth_token, device_id, policy_type, machine_id, machine_model); } } void CloudPolicyController::SendPolicyRequest() { backend_.reset(service_->CreateBackend()); DCHECK(!identity_strategy_->GetDeviceToken().empty()); em::DevicePolicyRequest policy_request; em::PolicyFetchRequest* fetch_request = policy_request.add_request(); fetch_request->set_signature_type(em::PolicyFetchRequest::SHA1_RSA); fetch_request->set_policy_type(identity_strategy_->GetPolicyType()); if (!cache_->is_unmanaged() && !cache_->last_policy_refresh_time().is_null()) { base::TimeDelta timestamp = cache_->last_policy_refresh_time() - base::Time::UnixEpoch(); fetch_request->set_timestamp(timestamp.InMilliseconds()); } int key_version = 0; if (cache_->GetPublicKeyVersion(&key_version)) fetch_request->set_public_key_version(key_version); backend_->ProcessPolicyRequest(identity_strategy_->GetDeviceToken(), identity_strategy_->GetDeviceID(), policy_request, this); } void CloudPolicyController::DoDelayedWork() { DCHECK(delayed_work_task_); delayed_work_task_ = NULL; DoWork(); } void CloudPolicyController::DoWork() { switch (state_) { case STATE_TOKEN_UNAVAILABLE: case STATE_TOKEN_ERROR: FetchToken(); return; case STATE_TOKEN_VALID: case STATE_POLICY_VALID: case STATE_POLICY_ERROR: case STATE_POLICY_UNAVAILABLE: SendPolicyRequest(); return; case STATE_TOKEN_UNMANAGED: return; } NOTREACHED() << "Unhandled state" << state_; } void CloudPolicyController::CancelDelayedWork() { if (delayed_work_task_) { delayed_work_task_->Cancel(); delayed_work_task_ = NULL; } } void CloudPolicyController::SetState( CloudPolicyController::ControllerState new_state) { state_ = new_state; backend_.reset(); // Discard any pending requests. base::Time now(base::Time::NowFromSystemTime()); base::Time refresh_at; base::Time last_refresh(cache_->last_policy_refresh_time()); if (last_refresh.is_null()) last_refresh = now; // Determine when to take the next step. bool inform_notifier_done = false; switch (state_) { case STATE_TOKEN_UNMANAGED: notifier_->Inform(CloudPolicySubsystem::UNMANAGED, CloudPolicySubsystem::NO_DETAILS, PolicyNotifier::POLICY_CONTROLLER); break; case STATE_TOKEN_UNAVAILABLE: // The controller is not yet initialized and needs to immediately fetch // token and policy if present. case STATE_TOKEN_VALID: // Immediately try to fetch the token on initialization or policy after a // token update. Subsequent retries will respect the back-off strategy. refresh_at = now; // |notifier_| isn't informed about anything at this point, we wait for // the result of the next action first. break; case STATE_POLICY_VALID: // Delay is only reset if the policy fetch operation was successful. This // will ensure the server won't get overloaded with retries in case of // a bug on either side. effective_policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms_; refresh_at = last_refresh + base::TimeDelta::FromMilliseconds(GetRefreshDelay()); notifier_->Inform(CloudPolicySubsystem::SUCCESS, CloudPolicySubsystem::NO_DETAILS, PolicyNotifier::POLICY_CONTROLLER); break; case STATE_TOKEN_ERROR: notifier_->Inform(CloudPolicySubsystem::NETWORK_ERROR, CloudPolicySubsystem::BAD_DMTOKEN, PolicyNotifier::POLICY_CONTROLLER); inform_notifier_done = true; case STATE_POLICY_ERROR: if (!inform_notifier_done) { notifier_->Inform(CloudPolicySubsystem::NETWORK_ERROR, CloudPolicySubsystem::POLICY_NETWORK_ERROR, PolicyNotifier::POLICY_CONTROLLER); } refresh_at = now + base::TimeDelta::FromMilliseconds( effective_policy_refresh_error_delay_ms_); effective_policy_refresh_error_delay_ms_ = std::min(effective_policy_refresh_error_delay_ms_ * 2, policy_refresh_rate_ms_); break; case STATE_POLICY_UNAVAILABLE: effective_policy_refresh_error_delay_ms_ = policy_refresh_rate_ms_; refresh_at = now + base::TimeDelta::FromMilliseconds( effective_policy_refresh_error_delay_ms_); notifier_->Inform(CloudPolicySubsystem::NETWORK_ERROR, CloudPolicySubsystem::POLICY_NETWORK_ERROR, PolicyNotifier::POLICY_CONTROLLER); break; } // Update the delayed work task. CancelDelayedWork(); if (!refresh_at.is_null()) { int64 delay = std::max<int64>((refresh_at - now).InMilliseconds(), 0); delayed_work_task_ = method_factory_.NewRunnableMethod( &CloudPolicyController::DoDelayedWork); MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_work_task_, delay); } } int64 CloudPolicyController::GetRefreshDelay() { int64 deviation = (policy_refresh_deviation_factor_percent_ * policy_refresh_rate_ms_) / 100; deviation = std::min(deviation, policy_refresh_deviation_max_ms_); return policy_refresh_rate_ms_ - base::RandGenerator(deviation + 1); } } // namespace policy