enrollment_handler_chromeos.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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/command_line.h" 9#include "base/logging.h" 10#include "base/message_loop/message_loop.h" 11#include "chrome/browser/browser_process.h" 12#include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h" 13#include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h" 14#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h" 15#include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h" 16#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" 17#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" 18#include "chrome/browser/chromeos/settings/device_settings_service.h" 19#include "chromeos/chromeos_switches.h" 20#include "components/policy/core/common/cloud/cloud_policy_constants.h" 21#include "google_apis/gaia/gaia_urls.h" 22#include "net/http/http_status_code.h" 23 24namespace em = enterprise_management; 25 26namespace policy { 27 28namespace { 29 30// Retry for InstallAttrs initialization every 500ms. 31const int kLockRetryIntervalMs = 500; 32// Maximum time to retry InstallAttrs initialization before we give up. 33const int kLockRetryTimeoutMs = 10 * 60 * 1000; // 10 minutes. 34 35// Testing token used when the enrollment-skip-robot-auth is set to skip talking 36// to GAIA for an actual token. This is needed to be able to run against the 37// testing DMServer implementations. 38const char kTestingRobotToken[] = "test-token"; 39 40} // namespace 41 42EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS( 43 DeviceCloudPolicyStoreChromeOS* store, 44 EnterpriseInstallAttributes* install_attributes, 45 ServerBackedStateKeysBroker* state_keys_broker, 46 chromeos::DeviceSettingsService* device_settings_service, 47 scoped_ptr<CloudPolicyClient> client, 48 scoped_refptr<base::SequencedTaskRunner> background_task_runner, 49 const std::string& auth_token, 50 const std::string& client_id, 51 bool is_auto_enrollment, 52 const std::string& requisition, 53 const AllowedDeviceModes& allowed_device_modes, 54 em::PolicyData::ManagementMode management_mode, 55 const EnrollmentCallback& completion_callback) 56 : store_(store), 57 install_attributes_(install_attributes), 58 state_keys_broker_(state_keys_broker), 59 device_settings_service_(device_settings_service), 60 client_(client.Pass()), 61 background_task_runner_(background_task_runner), 62 auth_token_(auth_token), 63 client_id_(client_id), 64 is_auto_enrollment_(is_auto_enrollment), 65 requisition_(requisition), 66 allowed_device_modes_(allowed_device_modes), 67 management_mode_(management_mode), 68 completion_callback_(completion_callback), 69 device_mode_(DEVICE_MODE_NOT_SET), 70 enrollment_step_(STEP_PENDING), 71 lockbox_init_duration_(0), 72 weak_ptr_factory_(this) { 73 CHECK(!client_->is_registered()); 74 CHECK_EQ(DM_STATUS_SUCCESS, client_->status()); 75 CHECK(management_mode_ == em::PolicyData::ENTERPRISE_MANAGED || 76 management_mode_ == em::PolicyData::CONSUMER_MANAGED); 77 store_->AddObserver(this); 78 client_->AddObserver(this); 79 client_->AddNamespaceToFetch(PolicyNamespaceKey( 80 dm_protocol::kChromeDevicePolicyType, std::string())); 81} 82 83EnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() { 84 Stop(); 85 store_->RemoveObserver(this); 86} 87 88void EnrollmentHandlerChromeOS::StartEnrollment() { 89 CHECK_EQ(STEP_PENDING, enrollment_step_); 90 enrollment_step_ = STEP_STATE_KEYS; 91 state_keys_broker_->RequestStateKeys( 92 base::Bind(&EnrollmentHandlerChromeOS::CheckStateKeys, 93 weak_ptr_factory_.GetWeakPtr())); 94} 95 96scoped_ptr<CloudPolicyClient> EnrollmentHandlerChromeOS::ReleaseClient() { 97 Stop(); 98 return client_.Pass(); 99} 100 101void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) { 102 DCHECK_EQ(client_.get(), client); 103 CHECK_EQ(STEP_POLICY_FETCH, enrollment_step_); 104 105 enrollment_step_ = STEP_VALIDATION; 106 107 // Validate the policy. 108 const em::PolicyFetchResponse* policy = client_->GetPolicyFor( 109 PolicyNamespaceKey(dm_protocol::kChromeDevicePolicyType, std::string())); 110 if (!policy) { 111 ReportResult(EnrollmentStatus::ForFetchError( 112 DM_STATUS_RESPONSE_DECODING_ERROR)); 113 return; 114 } 115 116 scoped_ptr<DeviceCloudPolicyValidator> validator( 117 DeviceCloudPolicyValidator::Create( 118 scoped_ptr<em::PolicyFetchResponse>( 119 new em::PolicyFetchResponse(*policy)), 120 background_task_runner_)); 121 122 validator->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(), 123 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED); 124 125 // If this is re-enrollment, make sure that the new policy matches the 126 // previously-enrolled domain. 127 std::string domain; 128 if (install_attributes_->IsEnterpriseDevice()) { 129 domain = install_attributes_->GetDomain(); 130 validator->ValidateDomain(domain); 131 } 132 validator->ValidateDMToken(client->dm_token(), 133 CloudPolicyValidatorBase::DM_TOKEN_REQUIRED); 134 validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType); 135 validator->ValidatePayload(); 136 // If |domain| is empty here, the policy validation code will just use the 137 // domain from the username field in the policy itself to do key validation. 138 // TODO(mnissler): Plumb the enrolling user's username into this object so 139 // we can validate the username on the resulting policy, and use the domain 140 // from that username to validate the key below (http://crbug.com/343074). 141 validator->ValidateInitialKey(GetPolicyVerificationKey(), domain); 142 validator.release()->StartValidation( 143 base::Bind(&EnrollmentHandlerChromeOS::PolicyValidated, 144 weak_ptr_factory_.GetWeakPtr())); 145} 146 147void EnrollmentHandlerChromeOS::OnRegistrationStateChanged( 148 CloudPolicyClient* client) { 149 DCHECK_EQ(client_.get(), client); 150 151 if (enrollment_step_ == STEP_REGISTRATION && client_->is_registered()) { 152 enrollment_step_ = STEP_POLICY_FETCH, 153 device_mode_ = client_->device_mode(); 154 if (device_mode_ == DEVICE_MODE_NOT_SET) 155 device_mode_ = DEVICE_MODE_ENTERPRISE; 156 if (!allowed_device_modes_.test(device_mode_)) { 157 LOG(ERROR) << "Bad device mode " << device_mode_; 158 ReportResult(EnrollmentStatus::ForStatus( 159 EnrollmentStatus::STATUS_REGISTRATION_BAD_MODE)); 160 return; 161 } 162 client_->FetchPolicy(); 163 } else { 164 LOG(FATAL) << "Registration state changed to " << client_->is_registered() 165 << " in step " << enrollment_step_; 166 } 167} 168 169void EnrollmentHandlerChromeOS::OnClientError(CloudPolicyClient* client) { 170 DCHECK_EQ(client_.get(), client); 171 172 if (enrollment_step_ == STEP_ROBOT_AUTH_FETCH) { 173 LOG(ERROR) << "API authentication code fetch failed: " 174 << client_->status(); 175 ReportResult(EnrollmentStatus::ForRobotAuthFetchError(client_->status())); 176 } else if (enrollment_step_ < STEP_POLICY_FETCH) { 177 ReportResult(EnrollmentStatus::ForRegistrationError(client_->status())); 178 } else { 179 ReportResult(EnrollmentStatus::ForFetchError(client_->status())); 180 } 181} 182 183void EnrollmentHandlerChromeOS::OnStoreLoaded(CloudPolicyStore* store) { 184 DCHECK_EQ(store_, store); 185 186 if (enrollment_step_ == STEP_LOADING_STORE) { 187 // If the |store_| wasn't initialized when StartEnrollment() was 188 // called, then AttemptRegistration() bails silently. This gets 189 // registration rolling again after the store finishes loading. 190 AttemptRegistration(); 191 } else if (enrollment_step_ == STEP_STORE_POLICY) { 192 ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS)); 193 } 194} 195 196void EnrollmentHandlerChromeOS::OnStoreError(CloudPolicyStore* store) { 197 DCHECK_EQ(store_, store); 198 if (enrollment_step_ == STEP_STORE_TOKEN_AND_ID) { 199 // Calling DeviceSettingsService::SetManagementSettings() on a non- 200 // enterprise-managed device will trigger OnStoreError(), as 201 // DeviceCloudPolicyStore listens to all changes on DeviceSettingsService, 202 // and it calls OnStoreError() when the device is not enterprise-managed. 203 return; 204 } 205 ReportResult(EnrollmentStatus::ForStoreError(store_->status(), 206 store_->validation_status())); 207} 208 209void EnrollmentHandlerChromeOS::CheckStateKeys( 210 const std::vector<std::string>& state_keys) { 211 CHECK_EQ(STEP_STATE_KEYS, enrollment_step_); 212 213 // Make sure state keys are available if forced re-enrollment is on. 214 if (chromeos::AutoEnrollmentController::GetMode() == 215 chromeos::AutoEnrollmentController::MODE_FORCED_RE_ENROLLMENT) { 216 if (state_keys.empty()) { 217 ReportResult( 218 EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_NO_STATE_KEYS)); 219 return; 220 } 221 client_->SetStateKeysToUpload(state_keys); 222 current_state_key_ = state_keys_broker_->current_state_key(); 223 } 224 225 enrollment_step_ = STEP_LOADING_STORE; 226 AttemptRegistration(); 227} 228 229void EnrollmentHandlerChromeOS::AttemptRegistration() { 230 CHECK_EQ(STEP_LOADING_STORE, enrollment_step_); 231 if (store_->is_initialized()) { 232 enrollment_step_ = STEP_REGISTRATION; 233 client_->Register(em::DeviceRegisterRequest::DEVICE, 234 auth_token_, client_id_, is_auto_enrollment_, 235 requisition_, current_state_key_); 236 } 237} 238 239void EnrollmentHandlerChromeOS::PolicyValidated( 240 DeviceCloudPolicyValidator* validator) { 241 CHECK_EQ(STEP_VALIDATION, enrollment_step_); 242 if (validator->success()) { 243 policy_ = validator->policy().Pass(); 244 username_ = validator->policy_data()->username(); 245 device_id_ = validator->policy_data()->device_id(); 246 request_token_ = validator->policy_data()->request_token(); 247 248 if (CommandLine::ForCurrentProcess()->HasSwitch( 249 chromeos::switches::kEnterpriseEnrollmentSkipRobotAuth)) { 250 // For test purposes we allow enrollment to succeed without proper robot 251 // account and use the provided value as a token. 252 refresh_token_ = kTestingRobotToken; 253 enrollment_step_ = STEP_LOCK_DEVICE; 254 StartLockDevice(); 255 return; 256 } 257 258 enrollment_step_ = STEP_ROBOT_AUTH_FETCH; 259 client_->FetchRobotAuthCodes(auth_token_); 260 } else { 261 ReportResult(EnrollmentStatus::ForValidationError(validator->status())); 262 } 263} 264 265void EnrollmentHandlerChromeOS::OnRobotAuthCodesFetched( 266 CloudPolicyClient* client) { 267 DCHECK_EQ(client_.get(), client); 268 CHECK_EQ(STEP_ROBOT_AUTH_FETCH, enrollment_step_); 269 270 enrollment_step_ = STEP_ROBOT_AUTH_REFRESH; 271 272 gaia::OAuthClientInfo client_info; 273 client_info.client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id(); 274 client_info.client_secret = 275 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(); 276 client_info.redirect_uri = "oob"; 277 278 // Use the system request context to avoid sending user cookies. 279 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient( 280 g_browser_process->system_request_context())); 281 gaia_oauth_client_->GetTokensFromAuthCode(client_info, 282 client->robot_api_auth_code(), 283 0 /* max_retries */, 284 this); 285} 286 287// GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched. 288void EnrollmentHandlerChromeOS::OnGetTokensResponse( 289 const std::string& refresh_token, 290 const std::string& access_token, 291 int expires_in_seconds) { 292 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_); 293 294 refresh_token_ = refresh_token; 295 296 enrollment_step_ = STEP_LOCK_DEVICE; 297 StartLockDevice(); 298} 299 300// GaiaOAuthClient::Delegate 301void EnrollmentHandlerChromeOS::OnRefreshTokenResponse( 302 const std::string& access_token, 303 int expires_in_seconds) { 304 // We never use the code that should trigger this callback. 305 LOG(FATAL) << "Unexpected callback invoked"; 306} 307 308// GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request. 309void EnrollmentHandlerChromeOS::OnOAuthError() { 310 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_); 311 // OnOAuthError is only called if the request is bad (malformed) or the 312 // response is bad (empty access token returned). 313 LOG(ERROR) << "OAuth protocol error while fetching API refresh token."; 314 ReportResult( 315 EnrollmentStatus::ForRobotRefreshFetchError(net::HTTP_BAD_REQUEST)); 316} 317 318// GaiaOAuthClient::Delegate network error when fetching refresh token. 319void EnrollmentHandlerChromeOS::OnNetworkError(int response_code) { 320 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_); 321 LOG(ERROR) << "Network error while fetching API refresh token: " 322 << response_code; 323 ReportResult( 324 EnrollmentStatus::ForRobotRefreshFetchError(response_code)); 325} 326 327void EnrollmentHandlerChromeOS::StartLockDevice() { 328 CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_); 329 // Since this method is also called directly. 330 weak_ptr_factory_.InvalidateWeakPtrs(); 331 332 if (management_mode_ == em::PolicyData::CONSUMER_MANAGED) { 333 // Consumer device enrollment doesn't use install attributes. Instead, 334 // we put the information in the owners settings. 335 enrollment_step_ = STEP_STORE_TOKEN_AND_ID; 336 device_settings_service_->SetManagementSettings( 337 management_mode_, request_token_, device_id_, 338 base::Bind(&EnrollmentHandlerChromeOS::OnSetManagementSettingsDone, 339 weak_ptr_factory_.GetWeakPtr())); 340 } else { 341 install_attributes_->LockDevice( 342 username_, device_mode_, device_id_, 343 base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult, 344 weak_ptr_factory_.GetWeakPtr())); 345 } 346} 347 348void EnrollmentHandlerChromeOS::OnSetManagementSettingsDone() { 349 CHECK_EQ(STEP_STORE_TOKEN_AND_ID, enrollment_step_); 350 if (device_settings_service_->status() != 351 chromeos::DeviceSettingsService::STORE_SUCCESS) { 352 ReportResult(EnrollmentStatus::ForStatus( 353 EnrollmentStatus::STATUS_STORE_TOKEN_AND_ID_FAILED)); 354 return; 355 } 356 357 StoreRobotAuth(); 358} 359 360void EnrollmentHandlerChromeOS::HandleLockDeviceResult( 361 EnterpriseInstallAttributes::LockResult lock_result) { 362 CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_); 363 switch (lock_result) { 364 case EnterpriseInstallAttributes::LOCK_SUCCESS: 365 StoreRobotAuth(); 366 return; 367 case EnterpriseInstallAttributes::LOCK_NOT_READY: 368 // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't 369 // succeeded by then show an error to the user and stop the enrollment. 370 if (lockbox_init_duration_ < kLockRetryTimeoutMs) { 371 // InstallAttributes not ready yet, retry later. 372 LOG(WARNING) << "Install Attributes not ready yet will retry in " 373 << kLockRetryIntervalMs << "ms."; 374 base::MessageLoop::current()->PostDelayedTask( 375 FROM_HERE, 376 base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice, 377 weak_ptr_factory_.GetWeakPtr()), 378 base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs)); 379 lockbox_init_duration_ += kLockRetryIntervalMs; 380 } else { 381 ReportResult(EnrollmentStatus::ForStatus( 382 EnrollmentStatus::STATUS_LOCK_TIMEOUT)); 383 } 384 return; 385 case EnterpriseInstallAttributes::LOCK_BACKEND_ERROR: 386 ReportResult(EnrollmentStatus::ForStatus( 387 EnrollmentStatus::STATUS_LOCK_ERROR)); 388 return; 389 case EnterpriseInstallAttributes::LOCK_WRONG_USER: 390 LOG(ERROR) << "Enrollment cannot proceed because the InstallAttrs " 391 << "has been locked already!"; 392 ReportResult(EnrollmentStatus::ForStatus( 393 EnrollmentStatus::STATUS_LOCK_WRONG_USER)); 394 return; 395 } 396 397 NOTREACHED() << "Invalid lock result " << lock_result; 398 ReportResult(EnrollmentStatus::ForStatus( 399 EnrollmentStatus::STATUS_LOCK_ERROR)); 400} 401 402void EnrollmentHandlerChromeOS::StoreRobotAuth() { 403 // Get the token service so we can store our robot refresh token. 404 enrollment_step_ = STEP_STORE_ROBOT_AUTH; 405 chromeos::DeviceOAuth2TokenServiceFactory::Get()->SetAndSaveRefreshToken( 406 refresh_token_, 407 base::Bind(&EnrollmentHandlerChromeOS::HandleRobotAuthTokenStored, 408 weak_ptr_factory_.GetWeakPtr())); 409} 410 411void EnrollmentHandlerChromeOS::HandleRobotAuthTokenStored(bool result) { 412 CHECK_EQ(STEP_STORE_ROBOT_AUTH, enrollment_step_); 413 414 if (!result) { 415 LOG(ERROR) << "Failed to store API refresh token."; 416 ReportResult(EnrollmentStatus::ForStatus( 417 EnrollmentStatus::STATUS_ROBOT_REFRESH_STORE_FAILED)); 418 return; 419 } 420 421 if (management_mode_ == em::PolicyData::CONSUMER_MANAGED) { 422 // For consumer management enrollment, we don't store the policy. 423 ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS)); 424 return; 425 } 426 427 enrollment_step_ = STEP_STORE_POLICY; 428 store_->InstallInitialPolicy(*policy_); 429} 430 431void EnrollmentHandlerChromeOS::Stop() { 432 if (client_.get()) 433 client_->RemoveObserver(this); 434 enrollment_step_ = STEP_FINISHED; 435 weak_ptr_factory_.InvalidateWeakPtrs(); 436 completion_callback_.Reset(); 437} 438 439void EnrollmentHandlerChromeOS::ReportResult(EnrollmentStatus status) { 440 EnrollmentCallback callback = completion_callback_; 441 Stop(); 442 443 if (status.status() != EnrollmentStatus::STATUS_SUCCESS) { 444 LOG(WARNING) << "Enrollment failed: " << status.status() 445 << " " << status.client_status() 446 << " " << status.validation_status() 447 << " " << status.store_status(); 448 } 449 450 if (!callback.is_null()) 451 callback.Run(status); 452} 453 454} // namespace policy 455