1 // Copyright 2017 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/tpm_firmware_update.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/callback_helpers.h"
12 #include "base/feature_list.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_path_watcher.h"
15 #include "base/files/file_util.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/path_service.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/task/post_task.h"
20 #include "base/task/thread_pool.h"
21 #include "base/task_runner_util.h"
22 #include "base/threading/sequenced_task_runner_handle.h"
23 #include "base/values.h"
24 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/browser_process_platform_part.h"
26 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h"
27 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
28 #include "chrome/browser/chromeos/settings/cros_settings.h"
29 #include "chrome/common/chrome_features.h"
30 #include "chrome/common/chrome_paths.h"
31 #include "components/policy/proto/chrome_device_policy.pb.h"
32
33 namespace chromeos {
34 namespace tpm_firmware_update {
35
36 namespace {
37
38 // Decodes a |settings| dictionary into a set of allowed update modes.
GetModesFromSetting(const base::Value * settings)39 std::set<Mode> GetModesFromSetting(const base::Value* settings) {
40 std::set<Mode> modes;
41 if (!settings)
42 return modes;
43
44 const base::Value* const allow_powerwash = settings->FindKeyOfType(
45 kSettingsKeyAllowPowerwash, base::Value::Type::BOOLEAN);
46 if (allow_powerwash && allow_powerwash->GetBool()) {
47 modes.insert(Mode::kPowerwash);
48 }
49 const base::Value* const allow_preserve_device_state =
50 settings->FindKeyOfType(kSettingsKeyAllowPreserveDeviceState,
51 base::Value::Type::BOOLEAN);
52 if (allow_preserve_device_state && allow_preserve_device_state->GetBool()) {
53 modes.insert(Mode::kPreserveDeviceState);
54 }
55
56 return modes;
57 }
58
59 } // namespace
60
61 const char kSettingsKeyAllowPowerwash[] = "allow-user-initiated-powerwash";
62 const char kSettingsKeyAllowPreserveDeviceState[] =
63 "allow-user-initiated-preserve-device-state";
64 const char kSettingsKeyAutoUpdateMode[] = "auto-update-mode";
65
DecodeSettingsProto(const enterprise_management::TPMFirmwareUpdateSettingsProto & settings)66 std::unique_ptr<base::DictionaryValue> DecodeSettingsProto(
67 const enterprise_management::TPMFirmwareUpdateSettingsProto& settings) {
68 std::unique_ptr<base::DictionaryValue> result =
69 std::make_unique<base::DictionaryValue>();
70
71 if (settings.has_allow_user_initiated_powerwash()) {
72 result->SetKey(kSettingsKeyAllowPowerwash,
73 base::Value(settings.allow_user_initiated_powerwash()));
74 }
75 if (settings.has_allow_user_initiated_preserve_device_state()) {
76 result->SetKey(
77 kSettingsKeyAllowPreserveDeviceState,
78 base::Value(settings.allow_user_initiated_preserve_device_state()));
79 }
80
81 if (settings.has_auto_update_mode()) {
82 result->SetKey(kSettingsKeyAutoUpdateMode,
83 base::Value(settings.auto_update_mode()));
84 }
85
86 return result;
87 }
88
89 // AvailabilityChecker tracks TPM firmware update availability information
90 // exposed by the system via the /run/tpm_firmware_update file. There are three
91 // states:
92 // 1. The file isn't present - availability check is still pending.
93 // 2. The file is present, but empty - no update available.
94 // 3. The file is present, non-empty - update binary path is in the file.
95 //
96 // AvailabilityChecker employs a FilePathWatcher to watch the file and hides
97 // away all the gory threading details.
98 class AvailabilityChecker {
99 public:
100 struct Status {
101 bool update_available = false;
102 bool srk_vulnerable_roca = false;
103 };
104 using ResponseCallback = base::OnceCallback<void(const Status&)>;
105
~AvailabilityChecker()106 ~AvailabilityChecker() { Cancel(); }
107
Start(ResponseCallback callback,base::TimeDelta timeout)108 static void Start(ResponseCallback callback, base::TimeDelta timeout) {
109 // Schedule a task to run when the timeout expires. The task also owns
110 // |checker| and thus takes care of eventual deletion.
111 base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
112 FROM_HERE,
113 base::BindOnce(
114 &AvailabilityChecker::OnTimeout,
115 std::make_unique<AvailabilityChecker>(std::move(callback))),
116 timeout);
117 }
118
119 // Don't call this directly, but use Start().
AvailabilityChecker(ResponseCallback callback)120 explicit AvailabilityChecker(ResponseCallback callback)
121 : callback_(std::move(callback)),
122 background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
123 {base::MayBlock(), base::TaskPriority::USER_VISIBLE})),
124 watcher_(new base::FilePathWatcher()) {
125 auto watch_callback = base::BindRepeating(
126 &AvailabilityChecker::OnFilePathChanged,
127 base::SequencedTaskRunnerHandle::Get(), weak_ptr_factory_.GetWeakPtr());
128 background_task_runner_->PostTask(
129 FROM_HERE, base::BindOnce(&AvailabilityChecker::StartOnBackgroundThread,
130 watcher_.get(), watch_callback));
131 }
132
133 private:
GetUpdateLocationFilePath()134 static base::FilePath GetUpdateLocationFilePath() {
135 base::FilePath update_location_file;
136 CHECK(base::PathService::Get(
137 chrome::FILE_CHROME_OS_TPM_FIRMWARE_UPDATE_LOCATION,
138 &update_location_file));
139 return update_location_file;
140 }
141
CheckAvailabilityStatus(Status * status)142 static bool CheckAvailabilityStatus(Status* status) {
143 int64_t size;
144 if (!base::GetFileSize(GetUpdateLocationFilePath(), &size)) {
145 // File doesn't exist or error - can't determine availability status.
146 return false;
147 }
148 status->update_available = size > 0;
149 base::FilePath srk_vulnerable_roca_file;
150 CHECK(base::PathService::Get(
151 chrome::FILE_CHROME_OS_TPM_FIRMWARE_UPDATE_SRK_VULNERABLE_ROCA,
152 &srk_vulnerable_roca_file));
153 status->srk_vulnerable_roca = base::PathExists(srk_vulnerable_roca_file);
154 return true;
155 }
156
StartOnBackgroundThread(base::FilePathWatcher * watcher,base::FilePathWatcher::Callback watch_callback)157 static void StartOnBackgroundThread(
158 base::FilePathWatcher* watcher,
159 base::FilePathWatcher::Callback watch_callback) {
160 watcher->Watch(GetUpdateLocationFilePath(), false /* recursive */,
161 watch_callback);
162 watch_callback.Run(base::FilePath(), false /* error */);
163 }
164
OnFilePathChanged(scoped_refptr<base::SequencedTaskRunner> origin_task_runner,base::WeakPtr<AvailabilityChecker> checker,const base::FilePath & target,bool error)165 static void OnFilePathChanged(
166 scoped_refptr<base::SequencedTaskRunner> origin_task_runner,
167 base::WeakPtr<AvailabilityChecker> checker,
168 const base::FilePath& target,
169 bool error) {
170 Status status;
171 if (CheckAvailabilityStatus(&status) || error) {
172 origin_task_runner->PostTask(
173 FROM_HERE,
174 base::BindOnce(&AvailabilityChecker::Resolve, checker, status));
175 }
176 }
177
Resolve(const Status & status)178 void Resolve(const Status& status) {
179 Cancel();
180 if (callback_) {
181 std::move(callback_).Run(status);
182 }
183 }
184
Cancel()185 void Cancel() {
186 // Neutralize further callbacks from |watcher_| or due to timeout.
187 weak_ptr_factory_.InvalidateWeakPtrs();
188 background_task_runner_->DeleteSoon(FROM_HERE, std::move(watcher_));
189 }
190
OnTimeout()191 void OnTimeout() {
192 // If |callback_| hasn't been triggered when the timeout task fires, perform
193 // a last check and wire the result into a |callback_| execution to make
194 // sure a result is delivered in all cases. Note that |OnTimeout()| gets run
195 // via a callback that owns |this|, so the object will be destructed after
196 // this function terminates. Thus, the final check needs to run independent
197 // of |this| and takes |callback_| ownership.
198 if (callback_) {
199 base::PostTaskAndReplyWithResult(background_task_runner_.get(), FROM_HERE,
200 base::BindOnce([]() {
201 Status status;
202 CheckAvailabilityStatus(&status);
203 return status;
204 }),
205 std::move(callback_));
206 }
207 }
208
209 ResponseCallback callback_;
210 scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
211 std::unique_ptr<base::FilePathWatcher> watcher_;
212 base::WeakPtrFactory<AvailabilityChecker> weak_ptr_factory_{this};
213
214 DISALLOW_COPY_AND_ASSIGN(AvailabilityChecker);
215 };
216
GetAvailableUpdateModes(base::OnceCallback<void (const std::set<Mode> &)> completion,base::TimeDelta timeout)217 void GetAvailableUpdateModes(
218 base::OnceCallback<void(const std::set<Mode>&)> completion,
219 base::TimeDelta timeout) {
220 // Wrap |completion| in a RepeatingCallback. This is necessary to cater to the
221 // somewhat awkward PrepareTrustedValues interface, which for some return
222 // values invokes the callback passed to it, and for others requires the code
223 // here to do so.
224 base::RepeatingCallback<void(const std::set<Mode>&)> callback(
225 base::AdaptCallbackForRepeating(std::move(completion)));
226
227 if (!base::FeatureList::IsEnabled(features::kTPMFirmwareUpdate)) {
228 callback.Run(std::set<Mode>());
229 return;
230 }
231
232 std::set<Mode> modes;
233 if (g_browser_process->platform_part()
234 ->browser_policy_connector_chromeos()
235 ->IsEnterpriseManaged()) {
236 // For enterprise-managed devices, always honor the device setting.
237 CrosSettings* const cros_settings = CrosSettings::Get();
238 switch (cros_settings->PrepareTrustedValues(
239 base::BindOnce(&GetAvailableUpdateModes, callback, timeout))) {
240 case CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
241 // Retry happens via the callback registered above.
242 return;
243 case CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
244 // No device settings? Default to disallow.
245 callback.Run(std::set<Mode>());
246 return;
247 case CrosSettingsProvider::TRUSTED:
248 // Setting is present and trusted so respect its value.
249 modes = GetModesFromSetting(
250 cros_settings->GetPref(kTPMFirmwareUpdateSettings));
251 break;
252 }
253 } else {
254 // Consumer device or still in OOBE. If FRE is required, enterprise
255 // enrollment might still be pending, in which case TPM firmware updates are
256 // disallowed until FRE determines that the device is not remotely managed
257 // or it does get enrolled and the admin allows TPM firmware updates.
258 const AutoEnrollmentController::FRERequirement requirement =
259 AutoEnrollmentController::GetFRERequirement();
260 if (requirement ==
261 AutoEnrollmentController::FRERequirement::kExplicitlyRequired) {
262 callback.Run(std::set<Mode>());
263 return;
264 }
265
266 // All modes are available for consumer devices.
267 modes.insert(Mode::kPowerwash);
268 modes.insert(Mode::kPreserveDeviceState);
269 }
270
271 // No need to check for availability if no update modes are allowed.
272 if (modes.empty()) {
273 callback.Run(std::set<Mode>());
274 return;
275 }
276
277 // Some TPM firmware update modes are allowed. Last thing to check is whether
278 // there actually is a pending update.
279 AvailabilityChecker::Start(
280 base::BindOnce(
281 [](std::set<Mode> modes,
282 base::OnceCallback<void(const std::set<Mode>&)> callback,
283 const AvailabilityChecker::Status& status) {
284 DCHECK_LT(0U, modes.size());
285 DCHECK_EQ(0U, modes.count(Mode::kCleanup));
286 if (status.update_available) {
287 std::move(callback).Run(modes);
288 return;
289 }
290
291 // If there is no update, but the SRK is vulnerable, allow cleanup
292 // to take place. Note that at least one allowed actual mode is
293 // allowed, which is taken to imply cleanup is also allowed.
294 if (status.srk_vulnerable_roca) {
295 std::move(callback).Run(std::set<Mode>({Mode::kCleanup}));
296 return;
297 }
298
299 std::move(callback).Run(std::set<Mode>());
300 },
301 std::move(modes), std::move(callback)),
302 timeout);
303 }
304
UpdateAvailable(base::OnceCallback<void (bool)> completion,base::TimeDelta timeout)305 void UpdateAvailable(base::OnceCallback<void(bool)> completion,
306 base::TimeDelta timeout) {
307 // Verify if we have updates pending.
308 AvailabilityChecker::Start(
309 base::BindOnce(
310 [](base::OnceCallback<void(bool)> completion,
311 const AvailabilityChecker::Status& status) {
312 std::move(completion).Run(status.update_available);
313 },
314 std::move(completion)),
315 timeout);
316 }
317
318 } // namespace tpm_firmware_update
319 } // namespace chromeos
320