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 |