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