enrollment_handler_chromeos.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/chromeos/policy/enrollment_handler_chromeos.h" 6 7#include "base/bind.h" 8#include "base/logging.h" 9#include "base/message_loop/message_loop.h" 10#include "chrome/browser/browser_process.h" 11#include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h" 12#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" 13#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" 14#include "chrome/browser/policy/cloud/cloud_policy_constants.h" 15#include "chrome/browser/policy/proto/chromeos/chrome_device_policy.pb.h" 16#include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" 17#include "google_apis/gaia/gaia_urls.h" 18 19namespace em = enterprise_management; 20 21namespace policy { 22 23namespace { 24 25// Retry for InstallAttrs initialization every 500ms. 26const int kLockRetryIntervalMs = 500; 27// Maximum time to retry InstallAttrs initialization before we give up. 28const int kLockRetryTimeoutMs = 10 * 60 * 1000; // 10 minutes. 29 30} // namespace 31 32EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS( 33 DeviceCloudPolicyStoreChromeOS* store, 34 EnterpriseInstallAttributes* install_attributes, 35 scoped_ptr<CloudPolicyClient> client, 36 scoped_refptr<base::SequencedTaskRunner> background_task_runner, 37 const std::string& auth_token, 38 const std::string& client_id, 39 bool is_auto_enrollment, 40 const std::string& requisition, 41 const AllowedDeviceModes& allowed_device_modes, 42 const EnrollmentCallback& completion_callback) 43 : store_(store), 44 install_attributes_(install_attributes), 45 client_(client.Pass()), 46 background_task_runner_(background_task_runner), 47 auth_token_(auth_token), 48 client_id_(client_id), 49 is_auto_enrollment_(is_auto_enrollment), 50 requisition_(requisition), 51 allowed_device_modes_(allowed_device_modes), 52 completion_callback_(completion_callback), 53 device_mode_(DEVICE_MODE_NOT_SET), 54 enrollment_step_(STEP_PENDING), 55 lockbox_init_duration_(0), 56 weak_factory_(this) { 57 CHECK(!client_->is_registered()); 58 CHECK_EQ(DM_STATUS_SUCCESS, client_->status()); 59 store_->AddObserver(this); 60 client_->AddObserver(this); 61 client_->AddNamespaceToFetch(PolicyNamespaceKey( 62 dm_protocol::kChromeDevicePolicyType, std::string())); 63} 64 65EnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() { 66 Stop(); 67 store_->RemoveObserver(this); 68} 69 70void EnrollmentHandlerChromeOS::StartEnrollment() { 71 CHECK_EQ(STEP_PENDING, enrollment_step_); 72 enrollment_step_ = STEP_LOADING_STORE; 73 AttemptRegistration(); 74} 75 76scoped_ptr<CloudPolicyClient> EnrollmentHandlerChromeOS::ReleaseClient() { 77 Stop(); 78 return client_.Pass(); 79} 80 81void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) { 82 DCHECK_EQ(client_.get(), client); 83 CHECK_EQ(STEP_POLICY_FETCH, enrollment_step_); 84 85 enrollment_step_ = STEP_VALIDATION; 86 87 // Validate the policy. 88 const em::PolicyFetchResponse* policy = client_->GetPolicyFor( 89 PolicyNamespaceKey(dm_protocol::kChromeDevicePolicyType, std::string())); 90 if (!policy) { 91 ReportResult(EnrollmentStatus::ForFetchError( 92 DM_STATUS_RESPONSE_DECODING_ERROR)); 93 return; 94 } 95 96 scoped_ptr<DeviceCloudPolicyValidator> validator( 97 DeviceCloudPolicyValidator::Create( 98 scoped_ptr<em::PolicyFetchResponse>( 99 new em::PolicyFetchResponse(*policy)), 100 background_task_runner_)); 101 102 validator->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(), 103 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED); 104 if (install_attributes_->IsEnterpriseDevice()) 105 validator->ValidateDomain(install_attributes_->GetDomain()); 106 validator->ValidateDMToken(client->dm_token(), 107 CloudPolicyValidatorBase::DM_TOKEN_REQUIRED); 108 validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType); 109 validator->ValidatePayload(); 110 validator->ValidateInitialKey(); 111 validator.release()->StartValidation( 112 base::Bind(&EnrollmentHandlerChromeOS::PolicyValidated, 113 weak_factory_.GetWeakPtr())); 114} 115 116void EnrollmentHandlerChromeOS::OnRegistrationStateChanged( 117 CloudPolicyClient* client) { 118 DCHECK_EQ(client_.get(), client); 119 120 if (enrollment_step_ == STEP_REGISTRATION && client_->is_registered()) { 121 enrollment_step_ = STEP_POLICY_FETCH, 122 device_mode_ = client_->device_mode(); 123 if (device_mode_ == DEVICE_MODE_NOT_SET) 124 device_mode_ = DEVICE_MODE_ENTERPRISE; 125 if (!allowed_device_modes_.test(device_mode_)) { 126 LOG(ERROR) << "Bad device mode " << device_mode_; 127 ReportResult(EnrollmentStatus::ForStatus( 128 EnrollmentStatus::STATUS_REGISTRATION_BAD_MODE)); 129 return; 130 } 131 client_->FetchPolicy(); 132 } else { 133 LOG(FATAL) << "Registration state changed to " << client_->is_registered() 134 << " in step " << enrollment_step_; 135 } 136} 137 138void EnrollmentHandlerChromeOS::OnClientError(CloudPolicyClient* client) { 139 DCHECK_EQ(client_.get(), client); 140 141 if (enrollment_step_ == STEP_ROBOT_AUTH_FETCH) { 142 LOG(WARNING) << "API authentication code fetch failed: " 143 << client_->status(); 144 // Robot auth tokens are currently optional. Skip fetching the refresh 145 // token and jump directly to the lock device step. 146 robot_refresh_token_.clear(); 147 DoLockDeviceStep(); 148 } else if (enrollment_step_ < STEP_POLICY_FETCH) { 149 ReportResult(EnrollmentStatus::ForRegistrationError(client_->status())); 150 } else { 151 ReportResult(EnrollmentStatus::ForFetchError(client_->status())); 152 } 153} 154 155void EnrollmentHandlerChromeOS::OnStoreLoaded(CloudPolicyStore* store) { 156 DCHECK_EQ(store_, store); 157 158 if (enrollment_step_ == STEP_LOADING_STORE) { 159 // If the |store_| wasn't initialized when StartEnrollment() was 160 // called, then AttemptRegistration() bails silently. This gets 161 // registration rolling again after the store finishes loading. 162 AttemptRegistration(); 163 } else if (enrollment_step_ == STEP_STORE_POLICY) { 164 // Store the robot API auth refresh token. 165 // Currently optional, so always return success. 166 chromeos::DeviceOAuth2TokenService* token_service = 167 chromeos::DeviceOAuth2TokenServiceFactory::Get(); 168 if (token_service && !robot_refresh_token_.empty()) { 169 token_service->SetAndSaveRefreshToken(robot_refresh_token_); 170 171 } 172 ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS)); 173 } 174} 175 176void EnrollmentHandlerChromeOS::OnStoreError(CloudPolicyStore* store) { 177 DCHECK_EQ(store_, store); 178 ReportResult(EnrollmentStatus::ForStoreError(store_->status(), 179 store_->validation_status())); 180} 181 182void EnrollmentHandlerChromeOS::AttemptRegistration() { 183 CHECK_EQ(STEP_LOADING_STORE, enrollment_step_); 184 if (store_->is_initialized()) { 185 enrollment_step_ = STEP_REGISTRATION; 186 client_->Register(em::DeviceRegisterRequest::DEVICE, 187 auth_token_, client_id_, is_auto_enrollment_, 188 requisition_); 189 } 190} 191 192void EnrollmentHandlerChromeOS::PolicyValidated( 193 DeviceCloudPolicyValidator* validator) { 194 CHECK_EQ(STEP_VALIDATION, enrollment_step_); 195 if (validator->success()) { 196 policy_ = validator->policy().Pass(); 197 username_ = validator->policy_data()->username(); 198 device_id_ = validator->policy_data()->device_id(); 199 200 enrollment_step_ = STEP_ROBOT_AUTH_FETCH; 201 client_->FetchRobotAuthCodes(auth_token_); 202 } else { 203 ReportResult(EnrollmentStatus::ForValidationError(validator->status())); 204 } 205} 206 207void EnrollmentHandlerChromeOS::OnRobotAuthCodesFetched( 208 CloudPolicyClient* client) { 209 DCHECK_EQ(client_.get(), client); 210 CHECK_EQ(STEP_ROBOT_AUTH_FETCH, enrollment_step_); 211 212 enrollment_step_ = STEP_ROBOT_AUTH_REFRESH; 213 214 gaia::OAuthClientInfo client_info; 215 client_info.client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id(); 216 client_info.client_secret = 217 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(); 218 client_info.redirect_uri = "oob"; 219 220 // Use the system request context to avoid sending user cookies. 221 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient( 222 g_browser_process->system_request_context())); 223 gaia_oauth_client_->GetTokensFromAuthCode(client_info, 224 client->robot_api_auth_code(), 225 0 /* max_retries */, 226 this); 227} 228 229// GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched. 230void EnrollmentHandlerChromeOS::OnGetTokensResponse( 231 const std::string& refresh_token, 232 const std::string& access_token, 233 int expires_in_seconds) { 234 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_); 235 236 robot_refresh_token_ = refresh_token; 237 238 DoLockDeviceStep(); 239} 240 241void EnrollmentHandlerChromeOS::DoLockDeviceStep() { 242 enrollment_step_ = STEP_LOCK_DEVICE, 243 StartLockDevice(username_, device_mode_, device_id_); 244} 245 246// GaiaOAuthClient::Delegate 247void EnrollmentHandlerChromeOS::OnRefreshTokenResponse( 248 const std::string& access_token, 249 int expires_in_seconds) { 250 // We never use the code that should trigger this callback. 251 LOG(FATAL) << "Unexpected callback invoked"; 252} 253 254// GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request. 255void EnrollmentHandlerChromeOS::OnOAuthError() { 256 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_); 257 DoLockDeviceStep(); 258} 259 260// GaiaOAuthClient::Delegate network error when fetching refresh token. 261void EnrollmentHandlerChromeOS::OnNetworkError(int response_code) { 262 LOG(ERROR) << "Network error while fetching API refresh token: " 263 << response_code; 264 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_); 265 DoLockDeviceStep(); 266} 267 268void EnrollmentHandlerChromeOS::StartLockDevice( 269 const std::string& user, 270 DeviceMode device_mode, 271 const std::string& device_id) { 272 CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_); 273 // Since this method is also called directly. 274 weak_factory_.InvalidateWeakPtrs(); 275 276 install_attributes_->LockDevice( 277 user, device_mode, device_id, 278 base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult, 279 weak_factory_.GetWeakPtr(), 280 user, 281 device_mode, 282 device_id)); 283} 284 285void EnrollmentHandlerChromeOS::HandleLockDeviceResult( 286 const std::string& user, 287 DeviceMode device_mode, 288 const std::string& device_id, 289 EnterpriseInstallAttributes::LockResult lock_result) { 290 CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_); 291 switch (lock_result) { 292 case EnterpriseInstallAttributes::LOCK_SUCCESS: 293 enrollment_step_ = STEP_STORE_POLICY; 294 store_->InstallInitialPolicy(*policy_); 295 return; 296 case EnterpriseInstallAttributes::LOCK_NOT_READY: 297 // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't 298 // succeeded by then show an error to the user and stop the enrollment. 299 if (lockbox_init_duration_ < kLockRetryTimeoutMs) { 300 // InstallAttributes not ready yet, retry later. 301 LOG(WARNING) << "Install Attributes not ready yet will retry in " 302 << kLockRetryIntervalMs << "ms."; 303 base::MessageLoop::current()->PostDelayedTask( 304 FROM_HERE, 305 base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice, 306 weak_factory_.GetWeakPtr(), 307 user, device_mode, device_id), 308 base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs)); 309 lockbox_init_duration_ += kLockRetryIntervalMs; 310 } else { 311 ReportResult(EnrollmentStatus::ForStatus( 312 EnrollmentStatus::STATUS_LOCK_TIMEOUT)); 313 } 314 return; 315 case EnterpriseInstallAttributes::LOCK_BACKEND_ERROR: 316 ReportResult(EnrollmentStatus::ForStatus( 317 EnrollmentStatus::STATUS_LOCK_ERROR)); 318 return; 319 case EnterpriseInstallAttributes::LOCK_WRONG_USER: 320 LOG(ERROR) << "Enrollment cannot proceed because the InstallAttrs " 321 << "has been locked already!"; 322 ReportResult(EnrollmentStatus::ForStatus( 323 EnrollmentStatus::STATUS_LOCK_WRONG_USER)); 324 return; 325 } 326 327 NOTREACHED() << "Invalid lock result " << lock_result; 328 ReportResult(EnrollmentStatus::ForStatus( 329 EnrollmentStatus::STATUS_LOCK_ERROR)); 330} 331 332void EnrollmentHandlerChromeOS::Stop() { 333 if (client_.get()) 334 client_->RemoveObserver(this); 335 enrollment_step_ = STEP_FINISHED; 336 weak_factory_.InvalidateWeakPtrs(); 337 completion_callback_.Reset(); 338} 339 340void EnrollmentHandlerChromeOS::ReportResult(EnrollmentStatus status) { 341 EnrollmentCallback callback = completion_callback_; 342 Stop(); 343 344 if (status.status() != EnrollmentStatus::STATUS_SUCCESS) { 345 LOG(WARNING) << "Enrollment failed: " << status.status() 346 << " " << status.client_status() 347 << " " << status.validation_status() 348 << " " << status.store_status(); 349 } 350 351 if (!callback.is_null()) 352 callback.Run(status); 353} 354 355} // namespace policy 356