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