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 24 namespace em = enterprise_management; 25 26 namespace policy { 27 28 namespace { 29 30 // Retry for InstallAttrs initialization every 500ms. 31 const int kLockRetryIntervalMs = 500; 32 // Maximum time to retry InstallAttrs initialization before we give up. 33 const 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. 38 const char kTestingRobotToken[] = "test-token"; 39 40 } // namespace 41 42 EnrollmentHandlerChromeOS::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 83 EnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() { 84 Stop(); 85 store_->RemoveObserver(this); 86 } 87 88 void EnrollmentHandlerChromeOS::StartEnrollment() { 89 CHECK_EQ(STEP_PENDING, enrollment_step_); 90 enrollment_step_ = STEP_STATE_KEYS; 91 state_keys_broker_->RequestStateKeys( 92 base::Bind(&EnrollmentHandlerChromeOS::HandleStateKeysResult, 93 weak_ptr_factory_.GetWeakPtr())); 94 } 95 96 scoped_ptr<CloudPolicyClient> EnrollmentHandlerChromeOS::ReleaseClient() { 97 Stop(); 98 return client_.Pass(); 99 } 100 101 void 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::HandlePolicyValidationResult, 144 weak_ptr_factory_.GetWeakPtr())); 145 } 146 147 void 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 169 void 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 183 void 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 called, 188 // then StartRegistration() bails silently. This gets registration rolling 189 // again after the store finishes loading. 190 StartRegistration(); 191 } else if (enrollment_step_ == STEP_STORE_POLICY) { 192 ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS)); 193 } 194 } 195 196 void 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 209 void EnrollmentHandlerChromeOS::HandleStateKeysResult( 210 const std::vector<std::string>& state_keys, bool /* first_boot */) { 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 client_->SetStateKeysToUpload(state_keys); 217 current_state_key_ = state_keys_broker_->current_state_key(); 218 if (state_keys.empty() || current_state_key_.empty()) { 219 ReportResult( 220 EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_NO_STATE_KEYS)); 221 return; 222 } 223 } 224 225 enrollment_step_ = STEP_LOADING_STORE; 226 StartRegistration(); 227 } 228 229 void EnrollmentHandlerChromeOS::StartRegistration() { 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 } else { 237 // Do nothing. StartRegistration() will be called again from OnStoreLoaded() 238 // after the CloudPolicyStore has initialized. 239 } 240 } 241 242 void EnrollmentHandlerChromeOS::HandlePolicyValidationResult( 243 DeviceCloudPolicyValidator* validator) { 244 CHECK_EQ(STEP_VALIDATION, enrollment_step_); 245 if (validator->success()) { 246 policy_ = validator->policy().Pass(); 247 username_ = validator->policy_data()->username(); 248 device_id_ = validator->policy_data()->device_id(); 249 request_token_ = validator->policy_data()->request_token(); 250 251 if (CommandLine::ForCurrentProcess()->HasSwitch( 252 chromeos::switches::kEnterpriseEnrollmentSkipRobotAuth)) { 253 // For test purposes we allow enrollment to succeed without proper robot 254 // account and use the provided value as a token. 255 refresh_token_ = kTestingRobotToken; 256 enrollment_step_ = STEP_LOCK_DEVICE; 257 StartLockDevice(); 258 return; 259 } 260 261 enrollment_step_ = STEP_ROBOT_AUTH_FETCH; 262 client_->FetchRobotAuthCodes(auth_token_); 263 } else { 264 ReportResult(EnrollmentStatus::ForValidationError(validator->status())); 265 } 266 } 267 268 void EnrollmentHandlerChromeOS::OnRobotAuthCodesFetched( 269 CloudPolicyClient* client) { 270 DCHECK_EQ(client_.get(), client); 271 CHECK_EQ(STEP_ROBOT_AUTH_FETCH, enrollment_step_); 272 273 enrollment_step_ = STEP_ROBOT_AUTH_REFRESH; 274 275 gaia::OAuthClientInfo client_info; 276 client_info.client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id(); 277 client_info.client_secret = 278 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(); 279 client_info.redirect_uri = "oob"; 280 281 // Use the system request context to avoid sending user cookies. 282 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient( 283 g_browser_process->system_request_context())); 284 gaia_oauth_client_->GetTokensFromAuthCode(client_info, 285 client->robot_api_auth_code(), 286 0 /* max_retries */, 287 this); 288 } 289 290 // GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched. 291 void EnrollmentHandlerChromeOS::OnGetTokensResponse( 292 const std::string& refresh_token, 293 const std::string& access_token, 294 int expires_in_seconds) { 295 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_); 296 297 refresh_token_ = refresh_token; 298 299 enrollment_step_ = STEP_LOCK_DEVICE; 300 StartLockDevice(); 301 } 302 303 // GaiaOAuthClient::Delegate 304 void EnrollmentHandlerChromeOS::OnRefreshTokenResponse( 305 const std::string& access_token, 306 int expires_in_seconds) { 307 // We never use the code that should trigger this callback. 308 LOG(FATAL) << "Unexpected callback invoked."; 309 } 310 311 // GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request. 312 void EnrollmentHandlerChromeOS::OnOAuthError() { 313 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_); 314 // OnOAuthError is only called if the request is bad (malformed) or the 315 // response is bad (empty access token returned). 316 LOG(ERROR) << "OAuth protocol error while fetching API refresh token."; 317 ReportResult( 318 EnrollmentStatus::ForRobotRefreshFetchError(net::HTTP_BAD_REQUEST)); 319 } 320 321 // GaiaOAuthClient::Delegate network error when fetching refresh token. 322 void EnrollmentHandlerChromeOS::OnNetworkError(int response_code) { 323 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_); 324 LOG(ERROR) << "Network error while fetching API refresh token: " 325 << response_code; 326 ReportResult( 327 EnrollmentStatus::ForRobotRefreshFetchError(response_code)); 328 } 329 330 void EnrollmentHandlerChromeOS::StartLockDevice() { 331 CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_); 332 // Since this method is also called directly. 333 weak_ptr_factory_.InvalidateWeakPtrs(); 334 335 if (management_mode_ == em::PolicyData::CONSUMER_MANAGED) { 336 // Consumer device enrollment doesn't use install attributes. Instead, 337 // we put the information in the owners settings. 338 enrollment_step_ = STEP_STORE_TOKEN_AND_ID; 339 device_settings_service_->SetManagementSettings( 340 management_mode_, request_token_, device_id_, 341 base::Bind(&EnrollmentHandlerChromeOS::HandleSetManagementSettingsDone, 342 weak_ptr_factory_.GetWeakPtr())); 343 } else { 344 install_attributes_->LockDevice( 345 username_, device_mode_, device_id_, 346 base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult, 347 weak_ptr_factory_.GetWeakPtr())); 348 } 349 } 350 351 void EnrollmentHandlerChromeOS::HandleSetManagementSettingsDone() { 352 CHECK_EQ(STEP_STORE_TOKEN_AND_ID, enrollment_step_); 353 if (device_settings_service_->status() != 354 chromeos::DeviceSettingsService::STORE_SUCCESS) { 355 ReportResult(EnrollmentStatus::ForStatus( 356 EnrollmentStatus::STATUS_STORE_TOKEN_AND_ID_FAILED)); 357 return; 358 } 359 360 StartStoreRobotAuth(); 361 } 362 363 void EnrollmentHandlerChromeOS::HandleLockDeviceResult( 364 EnterpriseInstallAttributes::LockResult lock_result) { 365 CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_); 366 switch (lock_result) { 367 case EnterpriseInstallAttributes::LOCK_SUCCESS: 368 StartStoreRobotAuth(); 369 break; 370 case EnterpriseInstallAttributes::LOCK_NOT_READY: 371 // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't 372 // succeeded by then show an error to the user and stop the enrollment. 373 if (lockbox_init_duration_ < kLockRetryTimeoutMs) { 374 // InstallAttributes not ready yet, retry later. 375 LOG(WARNING) << "Install Attributes not ready yet will retry in " 376 << kLockRetryIntervalMs << "ms."; 377 base::MessageLoop::current()->PostDelayedTask( 378 FROM_HERE, 379 base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice, 380 weak_ptr_factory_.GetWeakPtr()), 381 base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs)); 382 lockbox_init_duration_ += kLockRetryIntervalMs; 383 } else { 384 ReportResult(EnrollmentStatus::ForStatus( 385 EnrollmentStatus::STATUS_LOCK_TIMEOUT)); 386 } 387 break; 388 case EnterpriseInstallAttributes::LOCK_BACKEND_ERROR: 389 ReportResult(EnrollmentStatus::ForStatus( 390 EnrollmentStatus::STATUS_LOCK_ERROR)); 391 break; 392 case EnterpriseInstallAttributes::LOCK_WRONG_USER: 393 LOG(ERROR) << "Enrollment cannot proceed because the InstallAttrs " 394 << "has been locked already!"; 395 ReportResult(EnrollmentStatus::ForStatus( 396 EnrollmentStatus::STATUS_LOCK_WRONG_USER)); 397 break; 398 } 399 } 400 401 void EnrollmentHandlerChromeOS::StartStoreRobotAuth() { 402 // Get the token service so we can store our robot refresh token. 403 enrollment_step_ = STEP_STORE_ROBOT_AUTH; 404 chromeos::DeviceOAuth2TokenServiceFactory::Get()->SetAndSaveRefreshToken( 405 refresh_token_, 406 base::Bind(&EnrollmentHandlerChromeOS::HandleStoreRobotAuthTokenResult, 407 weak_ptr_factory_.GetWeakPtr())); 408 } 409 410 void EnrollmentHandlerChromeOS::HandleStoreRobotAuthTokenResult(bool result) { 411 CHECK_EQ(STEP_STORE_ROBOT_AUTH, enrollment_step_); 412 413 if (!result) { 414 LOG(ERROR) << "Failed to store API refresh token."; 415 ReportResult(EnrollmentStatus::ForStatus( 416 EnrollmentStatus::STATUS_ROBOT_REFRESH_STORE_FAILED)); 417 return; 418 } 419 420 if (management_mode_ == em::PolicyData::CONSUMER_MANAGED) { 421 // For consumer management enrollment, we don't store the policy. 422 ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS)); 423 return; 424 } 425 426 enrollment_step_ = STEP_STORE_POLICY; 427 store_->InstallInitialPolicy(*policy_); 428 } 429 430 void EnrollmentHandlerChromeOS::Stop() { 431 if (client_.get()) 432 client_->RemoveObserver(this); 433 enrollment_step_ = STEP_FINISHED; 434 weak_ptr_factory_.InvalidateWeakPtrs(); 435 completion_callback_.Reset(); 436 } 437 438 void EnrollmentHandlerChromeOS::ReportResult(EnrollmentStatus status) { 439 EnrollmentCallback callback = completion_callback_; 440 Stop(); 441 442 if (status.status() != EnrollmentStatus::STATUS_SUCCESS) { 443 LOG(WARNING) << "Enrollment failed: " << status.status() 444 << ", client: " << status.client_status() 445 << ", validation: " << status.validation_status() 446 << ", store: " << status.store_status(); 447 } 448 449 if (!callback.is_null()) 450 callback.Run(status); 451 } 452 453 } // namespace policy 454