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