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