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/chromeos/policy/enterprise_install_attributes.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <utility> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/files/file_util.h" | |
13 #include "base/location.h" | |
14 #include "base/logging.h" | |
15 #include "base/macros.h" | |
16 #include "base/metrics/histogram_base.h" | |
17 #include "base/metrics/histogram_macros.h" | |
18 #include "base/single_thread_task_runner.h" | |
19 #include "base/threading/thread_task_runner_handle.h" | |
20 #include "base/time/time.h" | |
21 #include "chrome/browser/chromeos/policy/proto/install_attributes.pb.h" | |
22 #include "chromeos/cryptohome/cryptohome_util.h" | |
23 #include "chromeos/dbus/dbus_thread_manager.h" | |
24 #include "google_apis/gaia/gaia_auth_util.h" | |
25 | |
26 namespace policy { | |
27 | |
28 namespace cryptohome_util = chromeos::cryptohome_util; | |
29 | |
30 namespace { | |
31 | |
32 // Number of TPM lock state query retries during consistency check. | |
33 int kDbusRetryCount = 12; | |
34 | |
35 // Interval of TPM lock state query retries during consistency check. | |
36 int kDbusRetryIntervalInSeconds = 5; | |
37 | |
38 bool ReadMapKey(const std::map<std::string, std::string>& map, | |
39 const std::string& key, | |
40 std::string* value) { | |
41 std::map<std::string, std::string>::const_iterator entry = map.find(key); | |
42 if (entry == map.end()) | |
43 return false; | |
44 | |
45 *value = entry->second; | |
46 return true; | |
47 } | |
48 | |
49 } // namespace | |
50 | |
51 // static | |
52 std::string | |
53 EnterpriseInstallAttributes::GetEnterpriseOwnedInstallAttributesBlobForTesting( | |
54 const std::string& user_name) { | |
55 cryptohome::SerializedInstallAttributes install_attrs_proto; | |
56 cryptohome::SerializedInstallAttributes::Attribute* attribute = NULL; | |
57 | |
58 attribute = install_attrs_proto.add_attributes(); | |
59 attribute->set_name(EnterpriseInstallAttributes::kAttrEnterpriseOwned); | |
60 attribute->set_value("true"); | |
61 | |
62 attribute = install_attrs_proto.add_attributes(); | |
63 attribute->set_name(EnterpriseInstallAttributes::kAttrEnterpriseUser); | |
64 attribute->set_value(user_name); | |
65 | |
66 return install_attrs_proto.SerializeAsString(); | |
67 } | |
68 | |
69 EnterpriseInstallAttributes::EnterpriseInstallAttributes( | |
70 chromeos::CryptohomeClient* cryptohome_client) | |
71 : device_locked_(false), | |
72 consistency_check_running_(false), | |
73 device_lock_running_(false), | |
74 registration_mode_(DEVICE_MODE_PENDING), | |
75 cryptohome_client_(cryptohome_client), | |
76 weak_ptr_factory_(this) { | |
77 } | |
78 | |
79 EnterpriseInstallAttributes::~EnterpriseInstallAttributes() {} | |
80 | |
81 void EnterpriseInstallAttributes::Init(const base::FilePath& cache_file) { | |
82 DCHECK(!device_locked_); | |
83 | |
84 // Mark the consistency check as running to ensure that LockDevice() is | |
85 // blocked, but wait for the cryptohome service to be available before | |
86 // actually calling TriggerConsistencyCheck(). | |
87 consistency_check_running_ = true; | |
88 cryptohome_client_->WaitForServiceToBeAvailable(base::Bind( | |
89 &EnterpriseInstallAttributes::OnCryptohomeServiceInitiallyAvailable, | |
90 weak_ptr_factory_.GetWeakPtr())); | |
91 | |
92 if (!base::PathExists(cache_file)) | |
93 return; | |
94 | |
95 device_locked_ = true; | |
96 | |
97 char buf[16384]; | |
98 int len = base::ReadFile(cache_file, buf, sizeof(buf)); | |
99 if (len == -1 || len >= static_cast<int>(sizeof(buf))) { | |
100 PLOG(ERROR) << "Failed to read " << cache_file.value(); | |
101 return; | |
102 } | |
103 | |
104 cryptohome::SerializedInstallAttributes install_attrs_proto; | |
105 if (!install_attrs_proto.ParseFromArray(buf, len)) { | |
106 LOG(ERROR) << "Failed to parse install attributes cache."; | |
107 return; | |
108 } | |
109 | |
110 google::protobuf::RepeatedPtrField< | |
111 const cryptohome::SerializedInstallAttributes::Attribute>::iterator entry; | |
112 std::map<std::string, std::string> attr_map; | |
113 for (entry = install_attrs_proto.attributes().begin(); | |
114 entry != install_attrs_proto.attributes().end(); | |
115 ++entry) { | |
116 // The protobuf values unfortunately contain terminating null characters, so | |
117 // we have to sanitize the value here. | |
118 attr_map.insert(std::make_pair(entry->name(), | |
119 std::string(entry->value().c_str()))); | |
120 } | |
121 | |
122 DecodeInstallAttributes(attr_map); | |
123 } | |
124 | |
125 void EnterpriseInstallAttributes::ReadImmutableAttributes( | |
126 const base::Closure& callback) { | |
127 if (device_locked_) { | |
128 callback.Run(); | |
129 return; | |
130 } | |
131 | |
132 cryptohome_client_->InstallAttributesIsReady( | |
133 base::Bind(&EnterpriseInstallAttributes::ReadAttributesIfReady, | |
134 weak_ptr_factory_.GetWeakPtr(), | |
135 callback)); | |
136 } | |
137 | |
138 void EnterpriseInstallAttributes::ReadAttributesIfReady( | |
139 const base::Closure& callback, | |
140 chromeos::DBusMethodCallStatus call_status, | |
141 bool result) { | |
142 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS && result) { | |
143 registration_mode_ = DEVICE_MODE_NOT_SET; | |
144 if (!cryptohome_util::InstallAttributesIsInvalid() && | |
145 !cryptohome_util::InstallAttributesIsFirstInstall()) { | |
146 device_locked_ = true; | |
147 | |
148 static const char* const kEnterpriseAttributes[] = { | |
149 kAttrEnterpriseDeviceId, | |
150 kAttrEnterpriseDomain, | |
151 kAttrEnterpriseMode, | |
152 kAttrEnterpriseOwned, | |
153 kAttrEnterpriseUser, | |
154 kAttrConsumerKioskEnabled, | |
155 }; | |
156 std::map<std::string, std::string> attr_map; | |
157 for (size_t i = 0; i < arraysize(kEnterpriseAttributes); ++i) { | |
158 std::string value; | |
159 if (cryptohome_util::InstallAttributesGet(kEnterpriseAttributes[i], | |
160 &value)) | |
161 attr_map[kEnterpriseAttributes[i]] = value; | |
162 } | |
163 | |
164 DecodeInstallAttributes(attr_map); | |
165 } | |
166 } | |
167 callback.Run(); | |
168 } | |
169 | |
170 void EnterpriseInstallAttributes::LockDevice( | |
171 const std::string& user, | |
172 DeviceMode device_mode, | |
173 const std::string& device_id, | |
174 const LockResultCallback& callback) { | |
175 DCHECK(!callback.is_null()); | |
176 CHECK_EQ(device_lock_running_, false); | |
177 CHECK_NE(device_mode, DEVICE_MODE_PENDING); | |
178 CHECK_NE(device_mode, DEVICE_MODE_NOT_SET); | |
179 | |
180 // Check for existing lock first. | |
181 if (device_locked_) { | |
182 if (device_mode != registration_mode_) { | |
183 callback.Run(LOCK_WRONG_MODE); | |
184 return; | |
185 } | |
186 | |
187 switch (registration_mode_) { | |
188 case DEVICE_MODE_ENTERPRISE: | |
189 case DEVICE_MODE_LEGACY_RETAIL_MODE: { | |
190 // Check domain match for enterprise devices. | |
191 std::string domain = gaia::ExtractDomainName(user); | |
192 if (registration_domain_.empty() || domain != registration_domain_) { | |
193 callback.Run(LOCK_WRONG_DOMAIN); | |
194 return; | |
195 } | |
196 break; | |
197 } | |
198 case DEVICE_MODE_NOT_SET: | |
199 case DEVICE_MODE_PENDING: | |
200 // This case can't happen due to the CHECK_NE asserts above. | |
201 NOTREACHED(); | |
202 break; | |
203 case DEVICE_MODE_CONSUMER: | |
204 case DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH: | |
205 // The user parameter is ignored for consumer devices. | |
206 break; | |
207 } | |
208 | |
209 // Already locked in the right mode, signal success. | |
210 callback.Run(LOCK_SUCCESS); | |
211 return; | |
212 } | |
213 | |
214 // In case the consistency check is still running, postpone the device locking | |
215 // until it has finished. This should not introduce additional delay since | |
216 // device locking must wait for TPM initialization anyways. | |
217 if (consistency_check_running_) { | |
218 CHECK(post_check_action_.is_null()); | |
219 post_check_action_ = base::Bind(&EnterpriseInstallAttributes::LockDevice, | |
220 weak_ptr_factory_.GetWeakPtr(), | |
221 user, | |
222 device_mode, | |
223 device_id, | |
224 callback); | |
225 return; | |
226 } | |
227 | |
228 device_lock_running_ = true; | |
229 cryptohome_client_->InstallAttributesIsReady( | |
230 base::Bind(&EnterpriseInstallAttributes::LockDeviceIfAttributesIsReady, | |
231 weak_ptr_factory_.GetWeakPtr(), | |
232 user, | |
233 device_mode, | |
234 device_id, | |
235 callback)); | |
236 } | |
237 | |
238 void EnterpriseInstallAttributes::LockDeviceIfAttributesIsReady( | |
239 const std::string& user, | |
240 DeviceMode device_mode, | |
241 const std::string& device_id, | |
242 const LockResultCallback& callback, | |
243 chromeos::DBusMethodCallStatus call_status, | |
244 bool result) { | |
245 if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS || !result) { | |
246 device_lock_running_ = false; | |
247 callback.Run(LOCK_NOT_READY); | |
248 return; | |
249 } | |
250 | |
251 // Clearing the TPM password seems to be always a good deal. | |
252 if (cryptohome_util::TpmIsEnabled() && | |
253 !cryptohome_util::TpmIsBeingOwned() && | |
254 cryptohome_util::TpmIsOwned()) { | |
255 cryptohome_client_->CallTpmClearStoredPasswordAndBlock(); | |
256 } | |
257 | |
258 // Make sure we really have a working InstallAttrs. | |
259 if (cryptohome_util::InstallAttributesIsInvalid()) { | |
260 LOG(ERROR) << "Install attributes invalid."; | |
261 device_lock_running_ = false; | |
262 callback.Run(LOCK_BACKEND_INVALID); | |
263 return; | |
264 } | |
265 | |
266 if (!cryptohome_util::InstallAttributesIsFirstInstall()) { | |
267 LOG(ERROR) << "Install attributes already installed."; | |
268 device_lock_running_ = false; | |
269 callback.Run(LOCK_ALREADY_LOCKED); | |
270 return; | |
271 } | |
272 | |
273 std::string mode = GetDeviceModeString(device_mode); | |
274 std::string registration_user; | |
275 if (!user.empty()) | |
276 registration_user = gaia::CanonicalizeEmail(user); | |
277 | |
278 if (device_mode == DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH) { | |
279 // Set values in the InstallAttrs and lock it. | |
280 if (!cryptohome_util::InstallAttributesSet(kAttrConsumerKioskEnabled, | |
281 "true")) { | |
282 LOG(ERROR) << "Failed writing attributes."; | |
283 device_lock_running_ = false; | |
284 callback.Run(LOCK_SET_ERROR); | |
285 return; | |
286 } | |
287 } else { | |
288 std::string domain = gaia::ExtractDomainName(registration_user); | |
289 // Set values in the InstallAttrs and lock it. | |
290 if (!cryptohome_util::InstallAttributesSet(kAttrEnterpriseOwned, "true") || | |
291 !cryptohome_util::InstallAttributesSet(kAttrEnterpriseUser, | |
292 registration_user) || | |
293 !cryptohome_util::InstallAttributesSet(kAttrEnterpriseDomain, | |
294 domain) || | |
295 !cryptohome_util::InstallAttributesSet(kAttrEnterpriseMode, mode) || | |
296 !cryptohome_util::InstallAttributesSet(kAttrEnterpriseDeviceId, | |
297 device_id)) { | |
298 LOG(ERROR) << "Failed writing attributes."; | |
299 device_lock_running_ = false; | |
300 callback.Run(LOCK_SET_ERROR); | |
301 return; | |
302 } | |
303 } | |
304 | |
305 if (!cryptohome_util::InstallAttributesFinalize() || | |
306 cryptohome_util::InstallAttributesIsFirstInstall()) { | |
307 LOG(ERROR) << "Failed locking."; | |
308 device_lock_running_ = false; | |
309 callback.Run(LOCK_FINALIZE_ERROR); | |
310 return; | |
311 } | |
312 | |
313 ReadImmutableAttributes( | |
314 base::Bind(&EnterpriseInstallAttributes::OnReadImmutableAttributes, | |
315 weak_ptr_factory_.GetWeakPtr(), | |
316 registration_user, | |
317 callback)); | |
318 } | |
319 | |
320 void EnterpriseInstallAttributes::OnReadImmutableAttributes( | |
321 const std::string& registration_user, | |
322 const LockResultCallback& callback) { | |
323 | |
324 if (GetRegistrationUser() != registration_user) { | |
325 LOG(ERROR) << "Locked data doesn't match."; | |
326 device_lock_running_ = false; | |
327 callback.Run(LOCK_READBACK_ERROR); | |
328 return; | |
329 } | |
330 | |
331 device_lock_running_ = false; | |
332 callback.Run(LOCK_SUCCESS); | |
333 } | |
334 | |
335 bool EnterpriseInstallAttributes::IsEnterpriseDevice() const { | |
336 return device_locked_ && !registration_user_.empty(); | |
337 } | |
338 | |
339 bool EnterpriseInstallAttributes::IsConsumerKioskDeviceWithAutoLaunch() { | |
340 return device_locked_ && | |
341 registration_mode_ == DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH; | |
342 } | |
343 | |
344 std::string EnterpriseInstallAttributes::GetDomain() const { | |
345 if (!IsEnterpriseDevice()) | |
346 return std::string(); | |
347 | |
348 return registration_domain_; | |
349 } | |
350 | |
351 std::string EnterpriseInstallAttributes::GetDeviceId() { | |
352 if (!IsEnterpriseDevice()) | |
353 return std::string(); | |
354 | |
355 return registration_device_id_; | |
356 } | |
357 | |
358 DeviceMode EnterpriseInstallAttributes::GetMode() { | |
359 return registration_mode_; | |
360 } | |
361 | |
362 void EnterpriseInstallAttributes::TriggerConsistencyCheck(int dbus_retries) { | |
363 cryptohome_client_->TpmIsOwned( | |
364 base::Bind(&EnterpriseInstallAttributes::OnTpmOwnerCheckCompleted, | |
365 weak_ptr_factory_.GetWeakPtr(), | |
366 dbus_retries)); | |
367 } | |
368 | |
369 void EnterpriseInstallAttributes::OnTpmOwnerCheckCompleted( | |
370 int dbus_retries_remaining, | |
371 chromeos::DBusMethodCallStatus call_status, | |
372 bool result) { | |
373 if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS && | |
374 dbus_retries_remaining) { | |
375 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
376 FROM_HERE, | |
377 base::Bind(&EnterpriseInstallAttributes::TriggerConsistencyCheck, | |
378 weak_ptr_factory_.GetWeakPtr(), dbus_retries_remaining - 1), | |
379 base::TimeDelta::FromSeconds(kDbusRetryIntervalInSeconds)); | |
380 return; | |
381 } | |
382 | |
383 base::HistogramBase::Sample state = device_locked_; | |
384 state |= 0x2 * (registration_mode_ == DEVICE_MODE_ENTERPRISE); | |
385 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS) | |
386 state |= 0x4 * result; | |
387 else | |
388 state = 0x8; // This case is not a bit mask. | |
389 UMA_HISTOGRAM_ENUMERATION("Enterprise.AttributesTPMConsistency", state, 9); | |
390 | |
391 // Run any action (LockDevice call) that might have queued behind the | |
392 // consistency check. | |
393 consistency_check_running_ = false; | |
394 if (!post_check_action_.is_null()) { | |
395 post_check_action_.Run(); | |
396 post_check_action_.Reset(); | |
397 } | |
398 } | |
399 | |
400 // Warning: The values for these keys (but not the keys themselves) are stored | |
401 // in the protobuf with a trailing zero. Also note that some of these constants | |
402 // have been copied to login_manager/device_policy_service.cc. Please make sure | |
403 // that all changes to the constants are reflected there as well. | |
404 const char EnterpriseInstallAttributes::kConsumerDeviceMode[] = "consumer"; | |
405 const char EnterpriseInstallAttributes::kEnterpriseDeviceMode[] = "enterprise"; | |
406 const char EnterpriseInstallAttributes::kLegacyRetailDeviceMode[] = "kiosk"; | |
407 const char EnterpriseInstallAttributes::kConsumerKioskDeviceMode[] = | |
408 "consumer_kiosk"; | |
409 const char EnterpriseInstallAttributes::kUnknownDeviceMode[] = "unknown"; | |
410 | |
411 const char EnterpriseInstallAttributes::kAttrEnterpriseDeviceId[] = | |
412 "enterprise.device_id"; | |
413 const char EnterpriseInstallAttributes::kAttrEnterpriseDomain[] = | |
414 "enterprise.domain"; | |
415 const char EnterpriseInstallAttributes::kAttrEnterpriseMode[] = | |
416 "enterprise.mode"; | |
417 const char EnterpriseInstallAttributes::kAttrEnterpriseOwned[] = | |
418 "enterprise.owned"; | |
419 const char EnterpriseInstallAttributes::kAttrEnterpriseUser[] = | |
420 "enterprise.user"; | |
421 const char EnterpriseInstallAttributes::kAttrConsumerKioskEnabled[] = | |
422 "consumer.app_kiosk_enabled"; | |
423 | |
424 void EnterpriseInstallAttributes::OnCryptohomeServiceInitiallyAvailable( | |
425 bool service_is_ready) { | |
426 if (!service_is_ready) | |
427 LOG(ERROR) << "Failed waiting for cryptohome D-Bus service availability."; | |
428 | |
429 // Start the consistency check even if we failed to wait for availability; | |
430 // hopefully the service will become available eventually. | |
431 TriggerConsistencyCheck(kDbusRetryCount); | |
432 } | |
433 | |
434 std::string EnterpriseInstallAttributes::GetDeviceModeString(DeviceMode mode) { | |
435 switch (mode) { | |
436 case DEVICE_MODE_CONSUMER: | |
437 return EnterpriseInstallAttributes::kConsumerDeviceMode; | |
438 case DEVICE_MODE_ENTERPRISE: | |
439 return EnterpriseInstallAttributes::kEnterpriseDeviceMode; | |
440 case DEVICE_MODE_LEGACY_RETAIL_MODE: | |
441 return EnterpriseInstallAttributes::kLegacyRetailDeviceMode; | |
442 case DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH: | |
443 return EnterpriseInstallAttributes::kConsumerKioskDeviceMode; | |
444 case DEVICE_MODE_PENDING: | |
445 case DEVICE_MODE_NOT_SET: | |
446 break; | |
447 } | |
448 NOTREACHED() << "Invalid device mode: " << mode; | |
449 return EnterpriseInstallAttributes::kUnknownDeviceMode; | |
450 } | |
451 | |
452 DeviceMode EnterpriseInstallAttributes::GetDeviceModeFromString( | |
453 const std::string& mode) { | |
454 if (mode == EnterpriseInstallAttributes::kConsumerDeviceMode) | |
455 return DEVICE_MODE_CONSUMER; | |
456 else if (mode == EnterpriseInstallAttributes::kEnterpriseDeviceMode) | |
457 return DEVICE_MODE_ENTERPRISE; | |
458 else if (mode == EnterpriseInstallAttributes::kLegacyRetailDeviceMode) | |
459 return DEVICE_MODE_LEGACY_RETAIL_MODE; | |
460 else if (mode == EnterpriseInstallAttributes::kConsumerKioskDeviceMode) | |
461 return DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH; | |
462 NOTREACHED() << "Unknown device mode string: " << mode; | |
463 return DEVICE_MODE_NOT_SET; | |
464 } | |
465 | |
466 void EnterpriseInstallAttributes::DecodeInstallAttributes( | |
467 const std::map<std::string, std::string>& attr_map) { | |
468 std::string enterprise_owned; | |
469 std::string enterprise_user; | |
470 std::string consumer_kiosk_enabled; | |
471 if (ReadMapKey(attr_map, kAttrEnterpriseOwned, &enterprise_owned) && | |
472 ReadMapKey(attr_map, kAttrEnterpriseUser, &enterprise_user) && | |
473 enterprise_owned == "true" && | |
474 !enterprise_user.empty()) { | |
475 registration_user_ = gaia::CanonicalizeEmail(enterprise_user); | |
476 | |
477 // Initialize the mode to the legacy enterprise mode here and update | |
478 // below if more information is present. | |
479 registration_mode_ = DEVICE_MODE_ENTERPRISE; | |
480 | |
481 // If we could extract basic setting we should try to extract the | |
482 // extended ones too. We try to set these to defaults as good as | |
483 // as possible if present, which could happen for device enrolled in | |
484 // pre 19 revisions of the code, before these new attributes were added. | |
485 if (ReadMapKey(attr_map, kAttrEnterpriseDomain, ®istration_domain_)) | |
486 registration_domain_ = gaia::CanonicalizeDomain(registration_domain_); | |
487 else | |
488 registration_domain_ = gaia::ExtractDomainName(registration_user_); | |
489 | |
490 ReadMapKey(attr_map, kAttrEnterpriseDeviceId, ®istration_device_id_); | |
491 | |
492 std::string mode; | |
493 if (ReadMapKey(attr_map, kAttrEnterpriseMode, &mode)) | |
494 registration_mode_ = GetDeviceModeFromString(mode); | |
495 } else if (ReadMapKey(attr_map, | |
496 kAttrConsumerKioskEnabled, | |
497 &consumer_kiosk_enabled) && | |
498 consumer_kiosk_enabled == "true") { | |
499 registration_mode_ = DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH; | |
500 } else if (enterprise_user.empty() && enterprise_owned != "true") { | |
501 // |registration_user_| is empty on consumer devices. | |
502 registration_mode_ = DEVICE_MODE_CONSUMER; | |
503 } | |
504 } | |
505 | |
506 std::string EnterpriseInstallAttributes::GetRegistrationUser() const { | |
507 if (!device_locked_) | |
508 return std::string(); | |
509 | |
510 return registration_user_; | |
511 } | |
512 | |
513 } // namespace policy | |
OLD | NEW |