| OLD | NEW |
| (Empty) |
| 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/policy/device_token_fetcher.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/metrics/histogram.h" | |
| 11 #include "base/time.h" | |
| 12 #include "chrome/browser/policy/cloud_policy_cache_base.h" | |
| 13 #include "chrome/browser/policy/cloud_policy_constants.h" | |
| 14 #include "chrome/browser/policy/cloud_policy_data_store.h" | |
| 15 #include "chrome/browser/policy/delayed_work_scheduler.h" | |
| 16 #include "chrome/browser/policy/device_management_service.h" | |
| 17 #include "chrome/browser/policy/enterprise_metrics.h" | |
| 18 #include "chrome/browser/policy/policy_notifier.h" | |
| 19 #include "chrome/browser/policy/proto/device_management_local.pb.h" | |
| 20 | |
| 21 namespace em = enterprise_management; | |
| 22 | |
| 23 namespace policy { | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 // Retry after 5 minutes (with exponential backoff) after token fetch errors. | |
| 28 const int64 kTokenFetchErrorDelayMilliseconds = 5 * 60 * 1000; | |
| 29 // Retry after max 3 hours after token fetch errors. | |
| 30 const int64 kTokenFetchErrorMaxDelayMilliseconds = 3 * 60 * 60 * 1000; | |
| 31 // For unmanaged devices, check once per day whether they're still unmanaged. | |
| 32 const int64 kUnmanagedDeviceRefreshRateMilliseconds = 24 * 60 * 60 * 1000; | |
| 33 | |
| 34 // Records the UMA metric corresponding to |status|, if it represents an error. | |
| 35 // Also records that a fetch response was received. | |
| 36 void SampleErrorStatus(DeviceManagementStatus status) { | |
| 37 UMA_HISTOGRAM_ENUMERATION(kMetricToken, kMetricTokenFetchResponseReceived, | |
| 38 kMetricTokenSize); | |
| 39 int sample = -1; | |
| 40 switch (status) { | |
| 41 case DM_STATUS_SUCCESS: | |
| 42 return; | |
| 43 case DM_STATUS_REQUEST_FAILED: | |
| 44 case DM_STATUS_REQUEST_INVALID: | |
| 45 case DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID: | |
| 46 sample = kMetricTokenFetchRequestFailed; | |
| 47 break; | |
| 48 case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED: | |
| 49 sample = kMetricTokenFetchManagementNotSupported; | |
| 50 break; | |
| 51 case DM_STATUS_SERVICE_DEVICE_NOT_FOUND: | |
| 52 sample = kMetricTokenFetchDeviceNotFound; | |
| 53 break; | |
| 54 case DM_STATUS_SERVICE_DEVICE_ID_CONFLICT: | |
| 55 sample = kMetricTokenFetchDeviceIdConflict; | |
| 56 break; | |
| 57 case DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER: | |
| 58 sample = kMetricTokenFetchInvalidSerialNumber; | |
| 59 break; | |
| 60 case DM_STATUS_RESPONSE_DECODING_ERROR: | |
| 61 sample = kMetricTokenFetchBadResponse; | |
| 62 break; | |
| 63 case DM_STATUS_SERVICE_MISSING_LICENSES: | |
| 64 sample = kMetricMissingLicenses; | |
| 65 break; | |
| 66 case DM_STATUS_TEMPORARY_UNAVAILABLE: | |
| 67 case DM_STATUS_SERVICE_ACTIVATION_PENDING: | |
| 68 case DM_STATUS_SERVICE_POLICY_NOT_FOUND: | |
| 69 case DM_STATUS_HTTP_STATUS_ERROR: | |
| 70 sample = kMetricTokenFetchServerFailed; | |
| 71 break; | |
| 72 } | |
| 73 if (sample != -1) | |
| 74 UMA_HISTOGRAM_ENUMERATION(kMetricToken, sample, kMetricTokenSize); | |
| 75 else | |
| 76 NOTREACHED(); | |
| 77 } | |
| 78 | |
| 79 // Translates the DeviceRegisterResponse::DeviceMode |mode| to the enum used | |
| 80 // internally to represent different device modes. | |
| 81 DeviceMode TranslateProtobufDeviceMode( | |
| 82 em::DeviceRegisterResponse::DeviceMode mode) { | |
| 83 switch (mode) { | |
| 84 case em::DeviceRegisterResponse::ENTERPRISE: | |
| 85 return DEVICE_MODE_ENTERPRISE; | |
| 86 case em::DeviceRegisterResponse::RETAIL: | |
| 87 return DEVICE_MODE_KIOSK; | |
| 88 } | |
| 89 LOG(ERROR) << "Unknown enrollment mode in registration response: " << mode; | |
| 90 return DEVICE_MODE_PENDING; | |
| 91 } | |
| 92 | |
| 93 } // namespace | |
| 94 | |
| 95 DeviceTokenFetcher::DeviceTokenFetcher( | |
| 96 DeviceManagementService* service, | |
| 97 CloudPolicyCacheBase* cache, | |
| 98 CloudPolicyDataStore* data_store, | |
| 99 PolicyNotifier* notifier) | |
| 100 : effective_token_fetch_error_delay_ms_( | |
| 101 kTokenFetchErrorDelayMilliseconds) { | |
| 102 Initialize(service, | |
| 103 cache, | |
| 104 data_store, | |
| 105 notifier, | |
| 106 new DelayedWorkScheduler); | |
| 107 } | |
| 108 | |
| 109 DeviceTokenFetcher::DeviceTokenFetcher( | |
| 110 DeviceManagementService* service, | |
| 111 CloudPolicyCacheBase* cache, | |
| 112 CloudPolicyDataStore* data_store, | |
| 113 PolicyNotifier* notifier, | |
| 114 DelayedWorkScheduler* scheduler) | |
| 115 : effective_token_fetch_error_delay_ms_( | |
| 116 kTokenFetchErrorDelayMilliseconds) { | |
| 117 Initialize(service, cache, data_store, notifier, scheduler); | |
| 118 } | |
| 119 | |
| 120 DeviceTokenFetcher::~DeviceTokenFetcher() { | |
| 121 scheduler_->CancelDelayedWork(); | |
| 122 } | |
| 123 | |
| 124 void DeviceTokenFetcher::FetchToken() { | |
| 125 SetState(STATE_INACTIVE); | |
| 126 FetchTokenInternal(); | |
| 127 } | |
| 128 | |
| 129 void DeviceTokenFetcher::SetUnmanagedState() { | |
| 130 // The call to |cache_->SetUnmanaged()| has to happen first because it sets | |
| 131 // the timestamp that |SetState()| needs to determine the correct refresh | |
| 132 // time. | |
| 133 cache_->SetUnmanaged(); | |
| 134 SetState(STATE_UNMANAGED); | |
| 135 } | |
| 136 | |
| 137 void DeviceTokenFetcher::SetSerialNumberInvalidState() { | |
| 138 SetState(STATE_BAD_SERIAL); | |
| 139 } | |
| 140 | |
| 141 void DeviceTokenFetcher::SetMissingLicensesState() { | |
| 142 SetState(STATE_MISSING_LICENSES); | |
| 143 } | |
| 144 | |
| 145 void DeviceTokenFetcher::Reset() { | |
| 146 SetState(STATE_INACTIVE); | |
| 147 } | |
| 148 | |
| 149 void DeviceTokenFetcher::Initialize(DeviceManagementService* service, | |
| 150 CloudPolicyCacheBase* cache, | |
| 151 CloudPolicyDataStore* data_store, | |
| 152 PolicyNotifier* notifier, | |
| 153 DelayedWorkScheduler* scheduler) { | |
| 154 service_ = service; | |
| 155 cache_ = cache; | |
| 156 notifier_ = notifier; | |
| 157 data_store_ = data_store; | |
| 158 effective_token_fetch_error_delay_ms_ = kTokenFetchErrorDelayMilliseconds; | |
| 159 state_ = STATE_INACTIVE; | |
| 160 scheduler_.reset(scheduler); | |
| 161 | |
| 162 if (cache_->is_unmanaged()) | |
| 163 SetState(STATE_UNMANAGED); | |
| 164 } | |
| 165 | |
| 166 void DeviceTokenFetcher::FetchTokenInternal() { | |
| 167 DCHECK(state_ != STATE_TOKEN_AVAILABLE); | |
| 168 if (!data_store_->has_auth_token() || data_store_->device_id().empty()) { | |
| 169 // Maybe this device is unmanaged, just exit. The CloudPolicyController | |
| 170 // will call FetchToken() again if something changes. | |
| 171 return; | |
| 172 } | |
| 173 // Reinitialize |request_job_|, discarding any previous requests. | |
| 174 request_job_.reset( | |
| 175 service_->CreateJob(DeviceManagementRequestJob::TYPE_REGISTRATION)); | |
| 176 request_job_->SetGaiaToken(data_store_->gaia_token()); | |
| 177 request_job_->SetOAuthToken(data_store_->oauth_token()); | |
| 178 request_job_->SetClientID(data_store_->device_id()); | |
| 179 em::DeviceRegisterRequest* request = | |
| 180 request_job_->GetRequest()->mutable_register_request(); | |
| 181 request->set_type(data_store_->policy_register_type()); | |
| 182 if (!data_store_->machine_id().empty()) | |
| 183 request->set_machine_id(data_store_->machine_id()); | |
| 184 if (!data_store_->machine_model().empty()) | |
| 185 request->set_machine_model(data_store_->machine_model()); | |
| 186 if (data_store_->known_machine_id()) | |
| 187 request->set_auto_enrolled(true); | |
| 188 if (data_store_->reregister()) | |
| 189 request->set_reregister(true); | |
| 190 request_job_->Start(base::Bind(&DeviceTokenFetcher::OnTokenFetchCompleted, | |
| 191 base::Unretained(this))); | |
| 192 UMA_HISTOGRAM_ENUMERATION(kMetricToken, kMetricTokenFetchRequested, | |
| 193 kMetricTokenSize); | |
| 194 } | |
| 195 | |
| 196 void DeviceTokenFetcher::OnTokenFetchCompleted( | |
| 197 DeviceManagementStatus status, | |
| 198 const em::DeviceManagementResponse& response) { | |
| 199 if (status == DM_STATUS_SUCCESS && !response.has_register_response()) { | |
| 200 // Handled below. | |
| 201 status = DM_STATUS_RESPONSE_DECODING_ERROR; | |
| 202 } | |
| 203 | |
| 204 LOG_IF(ERROR, status != DM_STATUS_SUCCESS) << "DMServer returned error code: " | |
| 205 << status; | |
| 206 SampleErrorStatus(status); | |
| 207 | |
| 208 switch (status) { | |
| 209 case DM_STATUS_SUCCESS: { | |
| 210 const em::DeviceRegisterResponse& register_response = | |
| 211 response.register_response(); | |
| 212 if (register_response.has_device_management_token()) { | |
| 213 UMA_HISTOGRAM_ENUMERATION(kMetricToken, kMetricTokenFetchOK, | |
| 214 kMetricTokenSize); | |
| 215 | |
| 216 if (data_store_->policy_register_type() == | |
| 217 em::DeviceRegisterRequest::DEVICE) { | |
| 218 DeviceMode mode = DEVICE_MODE_ENTERPRISE; | |
| 219 if (register_response.has_enrollment_type()) { | |
| 220 mode = TranslateProtobufDeviceMode( | |
| 221 register_response.enrollment_type()); | |
| 222 } | |
| 223 if (mode == DEVICE_MODE_PENDING) { | |
| 224 LOG(ERROR) << "Enrollment mode missing or unknown!"; | |
| 225 SetState(STATE_BAD_ENROLLMENT_MODE); | |
| 226 return; | |
| 227 } | |
| 228 data_store_->set_device_mode(mode); | |
| 229 } | |
| 230 data_store_->SetDeviceToken(register_response.device_management_token(), | |
| 231 false); | |
| 232 SetState(STATE_TOKEN_AVAILABLE); | |
| 233 } else { | |
| 234 NOTREACHED(); | |
| 235 UMA_HISTOGRAM_ENUMERATION(kMetricToken, kMetricTokenFetchBadResponse, | |
| 236 kMetricTokenSize); | |
| 237 SetState(STATE_ERROR); | |
| 238 } | |
| 239 return; | |
| 240 } | |
| 241 case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED: | |
| 242 SetUnmanagedState(); | |
| 243 return; | |
| 244 case DM_STATUS_REQUEST_FAILED: | |
| 245 case DM_STATUS_TEMPORARY_UNAVAILABLE: | |
| 246 case DM_STATUS_SERVICE_DEVICE_NOT_FOUND: | |
| 247 case DM_STATUS_SERVICE_DEVICE_ID_CONFLICT: | |
| 248 SetState(STATE_TEMPORARY_ERROR); | |
| 249 return; | |
| 250 case DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID: | |
| 251 // Most probably the GAIA auth cookie has expired. We can not do anything | |
| 252 // until the user logs-in again. | |
| 253 SetState(STATE_BAD_AUTH); | |
| 254 return; | |
| 255 case DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER: | |
| 256 SetSerialNumberInvalidState(); | |
| 257 return; | |
| 258 case DM_STATUS_SERVICE_MISSING_LICENSES: | |
| 259 SetMissingLicensesState(); | |
| 260 return; | |
| 261 case DM_STATUS_REQUEST_INVALID: | |
| 262 case DM_STATUS_HTTP_STATUS_ERROR: | |
| 263 case DM_STATUS_RESPONSE_DECODING_ERROR: | |
| 264 case DM_STATUS_SERVICE_ACTIVATION_PENDING: | |
| 265 case DM_STATUS_SERVICE_POLICY_NOT_FOUND: | |
| 266 SetState(STATE_ERROR); | |
| 267 return; | |
| 268 } | |
| 269 NOTREACHED(); | |
| 270 SetState(STATE_ERROR); | |
| 271 } | |
| 272 | |
| 273 void DeviceTokenFetcher::SetState(FetcherState state) { | |
| 274 state_ = state; | |
| 275 if (state_ != STATE_TEMPORARY_ERROR) | |
| 276 effective_token_fetch_error_delay_ms_ = kTokenFetchErrorDelayMilliseconds; | |
| 277 | |
| 278 request_job_.reset(); // Stop any pending requests. | |
| 279 | |
| 280 base::Time delayed_work_at; | |
| 281 switch (state_) { | |
| 282 case STATE_INACTIVE: | |
| 283 notifier_->Inform(CloudPolicySubsystem::UNENROLLED, | |
| 284 CloudPolicySubsystem::NO_DETAILS, | |
| 285 PolicyNotifier::TOKEN_FETCHER); | |
| 286 break; | |
| 287 case STATE_TOKEN_AVAILABLE: | |
| 288 notifier_->Inform(CloudPolicySubsystem::SUCCESS, | |
| 289 CloudPolicySubsystem::NO_DETAILS, | |
| 290 PolicyNotifier::TOKEN_FETCHER); | |
| 291 break; | |
| 292 case STATE_BAD_SERIAL: | |
| 293 notifier_->Inform(CloudPolicySubsystem::UNENROLLED, | |
| 294 CloudPolicySubsystem::BAD_SERIAL_NUMBER, | |
| 295 PolicyNotifier::TOKEN_FETCHER); | |
| 296 break; | |
| 297 case STATE_BAD_ENROLLMENT_MODE: | |
| 298 notifier_->Inform(CloudPolicySubsystem::UNENROLLED, | |
| 299 CloudPolicySubsystem::BAD_ENROLLMENT_MODE, | |
| 300 PolicyNotifier::TOKEN_FETCHER); | |
| 301 break; | |
| 302 case STATE_MISSING_LICENSES: | |
| 303 notifier_->Inform(CloudPolicySubsystem::UNENROLLED, | |
| 304 CloudPolicySubsystem::MISSING_LICENSES, | |
| 305 PolicyNotifier::TOKEN_FETCHER); | |
| 306 break; | |
| 307 case STATE_UNMANAGED: | |
| 308 delayed_work_at = cache_->last_policy_refresh_time() + | |
| 309 base::TimeDelta::FromMilliseconds( | |
| 310 kUnmanagedDeviceRefreshRateMilliseconds); | |
| 311 notifier_->Inform(CloudPolicySubsystem::UNMANAGED, | |
| 312 CloudPolicySubsystem::NO_DETAILS, | |
| 313 PolicyNotifier::TOKEN_FETCHER); | |
| 314 break; | |
| 315 case STATE_TEMPORARY_ERROR: | |
| 316 delayed_work_at = base::Time::Now() + | |
| 317 base::TimeDelta::FromMilliseconds( | |
| 318 effective_token_fetch_error_delay_ms_); | |
| 319 effective_token_fetch_error_delay_ms_ = | |
| 320 std::min(effective_token_fetch_error_delay_ms_ * 2, | |
| 321 kTokenFetchErrorMaxDelayMilliseconds); | |
| 322 notifier_->Inform(CloudPolicySubsystem::NETWORK_ERROR, | |
| 323 CloudPolicySubsystem::DMTOKEN_NETWORK_ERROR, | |
| 324 PolicyNotifier::TOKEN_FETCHER); | |
| 325 break; | |
| 326 case STATE_ERROR: | |
| 327 effective_token_fetch_error_delay_ms_ = | |
| 328 kTokenFetchErrorMaxDelayMilliseconds; | |
| 329 delayed_work_at = base::Time::Now() + | |
| 330 base::TimeDelta::FromMilliseconds( | |
| 331 effective_token_fetch_error_delay_ms_); | |
| 332 notifier_->Inform(CloudPolicySubsystem::NETWORK_ERROR, | |
| 333 CloudPolicySubsystem::DMTOKEN_NETWORK_ERROR, | |
| 334 PolicyNotifier::TOKEN_FETCHER); | |
| 335 break; | |
| 336 case STATE_BAD_AUTH: | |
| 337 // Can't do anything, need to wait for new credentials. | |
| 338 notifier_->Inform(CloudPolicySubsystem::BAD_GAIA_TOKEN, | |
| 339 CloudPolicySubsystem::NO_DETAILS, | |
| 340 PolicyNotifier::TOKEN_FETCHER); | |
| 341 break; | |
| 342 } | |
| 343 | |
| 344 scheduler_->CancelDelayedWork(); | |
| 345 if (!delayed_work_at.is_null()) { | |
| 346 base::Time now(base::Time::Now()); | |
| 347 int64 delay = std::max<int64>((delayed_work_at - now).InMilliseconds(), 0); | |
| 348 scheduler_->PostDelayedWork( | |
| 349 base::Bind(&DeviceTokenFetcher::DoWork, base::Unretained(this)), delay); | |
| 350 } | |
| 351 | |
| 352 // Inform the cache if a token fetch attempt has failed. | |
| 353 if (state_ != STATE_INACTIVE && state_ != STATE_TOKEN_AVAILABLE) | |
| 354 cache_->SetFetchingDone(); | |
| 355 } | |
| 356 | |
| 357 void DeviceTokenFetcher::DoWork() { | |
| 358 switch (state_) { | |
| 359 case STATE_INACTIVE: | |
| 360 case STATE_TOKEN_AVAILABLE: | |
| 361 case STATE_BAD_SERIAL: | |
| 362 case STATE_BAD_ENROLLMENT_MODE: | |
| 363 case STATE_MISSING_LICENSES: | |
| 364 break; | |
| 365 case STATE_UNMANAGED: | |
| 366 case STATE_ERROR: | |
| 367 case STATE_TEMPORARY_ERROR: | |
| 368 case STATE_BAD_AUTH: | |
| 369 FetchTokenInternal(); | |
| 370 break; | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 } // namespace policy | |
| OLD | NEW |