| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/policy/tpm_auto_update_mode_policy_handler.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" |
| #include "chrome/browser/chromeos/tpm_firmware_update.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/common/pref_names.h" |
| #include "chromeos/dbus/session_manager/session_manager_client.h" |
| #include "chromeos/settings/cros_settings_names.h" |
| #include "chromeos/settings/cros_settings_provider.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/user_manager/user_manager.h" |
| |
| namespace { |
| |
| // Timeout for the TPM firmware update availability check. |
| const base::TimeDelta kFirmwareAvailabilityCheckerTimeout = |
| base::TimeDelta::FromSeconds(20); |
| |
| const base::TimeDelta kTPMUpdatePlannedNotificationWaitTime = |
| base::TimeDelta::FromDays(1); |
| |
| // Reads the value of the the device setting key |
| // TPMFirmwareUpdateSettings.AutoUpdateMode from a trusted store. If the value |
| // is temporarily untrusted |callback| will be invoked later when trusted values |
| // are available and AutoUpdateMode::kNever will be returned. This value is set |
| // via the device policy TPMFirmwareUpdateSettings. |
| policy::AutoUpdateMode GetTPMAutoUpdateModeSetting( |
| const chromeos::CrosSettings* cros_settings, |
| const base::RepeatingClosure callback) { |
| if (!g_browser_process->platform_part() |
| ->browser_policy_connector_chromeos() |
| ->IsEnterpriseManaged()) { |
| return policy::AutoUpdateMode::kNever; |
| } |
| |
| chromeos::CrosSettingsProvider::TrustedStatus status = |
| cros_settings->PrepareTrustedValues(callback); |
| if (status != chromeos::CrosSettingsProvider::TRUSTED) |
| return policy::AutoUpdateMode::kNever; |
| const base::Value* tpm_settings = |
| cros_settings->GetPref(chromeos::kTPMFirmwareUpdateSettings); |
| |
| if (!tpm_settings) |
| return policy::AutoUpdateMode::kNever; |
| |
| const base::Value* const auto_update_mode = tpm_settings->FindKeyOfType( |
| chromeos::tpm_firmware_update::kSettingsKeyAutoUpdateMode, |
| base::Value::Type::INTEGER); |
| |
| // Policy not set. |
| if (!auto_update_mode || auto_update_mode->GetInt() == 0) |
| return policy::AutoUpdateMode::kNever; |
| |
| // Verify that the value is within range. |
| if (auto_update_mode->GetInt() < |
| static_cast<int>(policy::AutoUpdateMode::kNever) || |
| auto_update_mode->GetInt() > |
| static_cast<int>(policy::AutoUpdateMode::kEnrollment)) { |
| NOTREACHED() << "Invalid value for device policy key " |
| "TPMFirmwareUpdateSettings.AutoUpdateMode"; |
| return policy::AutoUpdateMode::kNever; |
| } |
| |
| return static_cast<policy::AutoUpdateMode>(auto_update_mode->GetInt()); |
| } |
| |
| } // namespace |
| |
| namespace policy { |
| |
| TPMAutoUpdateModePolicyHandler::TPMAutoUpdateModePolicyHandler( |
| chromeos::CrosSettings* cros_settings, |
| PrefService* local_state) |
| : cros_settings_(cros_settings), |
| local_state_(local_state), |
| weak_factory_(this) { |
| DCHECK(local_state_); |
| policy_subscription_ = cros_settings_->AddSettingsObserver( |
| chromeos::kTPMFirmwareUpdateSettings, |
| base::BindRepeating(&TPMAutoUpdateModePolicyHandler::OnPolicyChanged, |
| weak_factory_.GetWeakPtr())); |
| |
| update_checker_callback_ = |
| base::BindRepeating(&TPMAutoUpdateModePolicyHandler::CheckForUpdate, |
| weak_factory_.GetWeakPtr()); |
| |
| show_notfication_callback_ = |
| base::BindRepeating(&chromeos::ShowAutoUpdateNotification); |
| |
| notification_timer_ = std::make_unique<base::OneShotTimer>(); |
| |
| // Fire it once so we're sure we get an invocation on startup. |
| OnPolicyChanged(); |
| } |
| |
| TPMAutoUpdateModePolicyHandler::~TPMAutoUpdateModePolicyHandler() = default; |
| |
| void TPMAutoUpdateModePolicyHandler::OnPolicyChanged() { |
| AutoUpdateMode auto_update_mode = GetTPMAutoUpdateModeSetting( |
| cros_settings_, |
| base::BindRepeating(&TPMAutoUpdateModePolicyHandler::OnPolicyChanged, |
| weak_factory_.GetWeakPtr())); |
| |
| if (auto_update_mode == AutoUpdateMode::kNever || |
| auto_update_mode == AutoUpdateMode::kEnrollment) { |
| if (notification_timer_->IsRunning()) |
| notification_timer_->Stop(); |
| return; |
| } |
| |
| if (auto_update_mode == AutoUpdateMode::kUserAcknowledgment) { |
| if (!WasTPMUpdateOnNextRebootNotificationShown()) { |
| update_checker_callback_.Run(base::BindOnce( |
| &TPMAutoUpdateModePolicyHandler::ShowTPMAutoUpdateNotification, |
| weak_factory_.GetWeakPtr())); |
| return; |
| } |
| } |
| update_checker_callback_.Run(base::BindOnce(&OnUpdateAvailableCheckResult)); |
| } |
| |
| void TPMAutoUpdateModePolicyHandler::CheckForUpdate( |
| base::OnceCallback<void(bool)> callback) { |
| chromeos::tpm_firmware_update::UpdateAvailable( |
| std::move(callback), kFirmwareAvailabilityCheckerTimeout); |
| } |
| |
| // static |
| void TPMAutoUpdateModePolicyHandler::OnUpdateAvailableCheckResult( |
| bool update_available) { |
| if (!update_available) |
| return; |
| |
| chromeos::SessionManagerClient::Get()->StartTPMFirmwareUpdate( |
| "preserve_stateful"); |
| } |
| |
| void TPMAutoUpdateModePolicyHandler::SetUpdateCheckerCallbackForTesting( |
| const UpdateCheckerCallback& callback) { |
| update_checker_callback_ = callback; |
| } |
| |
| void TPMAutoUpdateModePolicyHandler::SetShowNotificationCallbackForTesting( |
| const ShowNotificationCallback& callback) { |
| show_notfication_callback_ = callback; |
| } |
| |
| void TPMAutoUpdateModePolicyHandler::UpdateOnEnrollmentIfNeeded() { |
| AutoUpdateMode auto_update_mode = GetTPMAutoUpdateModeSetting( |
| cros_settings_, |
| base::BindRepeating( |
| &TPMAutoUpdateModePolicyHandler::UpdateOnEnrollmentIfNeeded, |
| weak_factory_.GetWeakPtr())); |
| |
| // If the TPM is set to update with user acknowlegment, always update on |
| // enrollment. The AutoUpdateMode.UserAcknowledgment flow is meant to give a |
| // warning to the user to backup their data. During enrollment there is no |
| // user data on the device. |
| if (auto_update_mode == AutoUpdateMode::kEnrollment || |
| auto_update_mode == AutoUpdateMode::kUserAcknowledgment) { |
| update_checker_callback_.Run(base::BindOnce(&OnUpdateAvailableCheckResult)); |
| } |
| } |
| |
| void TPMAutoUpdateModePolicyHandler::ShowTPMAutoUpdateNotificationIfNeeded() { |
| AutoUpdateMode auto_update_mode = GetTPMAutoUpdateModeSetting( |
| cros_settings_, |
| base::BindRepeating(&TPMAutoUpdateModePolicyHandler::OnPolicyChanged, |
| weak_factory_.GetWeakPtr())); |
| |
| if (auto_update_mode != AutoUpdateMode::kUserAcknowledgment) |
| return; |
| |
| update_checker_callback_.Run(base::BindOnce( |
| &TPMAutoUpdateModePolicyHandler::ShowTPMAutoUpdateNotification, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void TPMAutoUpdateModePolicyHandler::ShowTPMAutoUpdateNotification( |
| bool update_available) { |
| if (!update_available) |
| return; |
| |
| if (!user_manager::UserManager::IsInitialized()) |
| return; |
| |
| const user_manager::UserManager* user_manager = |
| user_manager::UserManager::Get(); |
| if (!user_manager->IsUserLoggedIn() || user_manager->IsLoggedInAsKioskApp()) |
| return; |
| |
| Profile* profile = ProfileManager::GetActiveUserProfile(); |
| if (!profile) |
| return; |
| |
| base::Time notification_shown = |
| local_state_->GetTime(prefs::kTPMUpdatePlannedNotificationShownTime); |
| |
| if (notification_shown == base::Time::Min()) { |
| notification_shown = base::Time::Now(); |
| local_state_->SetTime(prefs::kTPMUpdatePlannedNotificationShownTime, |
| notification_shown); |
| show_notfication_callback_.Run( |
| chromeos::TpmAutoUpdateUserNotification::kPlanned, profile); |
| } |
| |
| // Show update on next reboot notification after |
| // |kTPMUpdatePlannedNotificationWaitTime|. |
| base::Time show_reboot_notification_time = |
| notification_shown + kTPMUpdatePlannedNotificationWaitTime; |
| |
| if (show_reboot_notification_time <= base::Time::Now()) { |
| if (notification_timer_->IsRunning()) |
| notification_timer_->Stop(); |
| ShowTPMUpdateOnNextRebootNotification(); |
| return; |
| } |
| |
| // Set a timer that will display the update on next reboot notification after |
| // 24 hours. |
| if (notification_timer_->IsRunning()) |
| return; |
| notification_timer_->Start( |
| FROM_HERE, show_reboot_notification_time - base::Time::Now(), this, |
| &TPMAutoUpdateModePolicyHandler::ShowTPMUpdateOnNextRebootNotification); |
| } |
| |
| bool TPMAutoUpdateModePolicyHandler:: |
| WasTPMUpdateOnNextRebootNotificationShown() { |
| return local_state_->GetBoolean( |
| prefs::kTPMUpdateOnNextRebootNotificationShown); |
| } |
| |
| // static |
| void TPMAutoUpdateModePolicyHandler::RegisterPrefs( |
| PrefRegistrySimple* registry) { |
| registry->RegisterTimePref(prefs::kTPMUpdatePlannedNotificationShownTime, |
| base::Time::Min()); |
| registry->RegisterBooleanPref(prefs::kTPMUpdateOnNextRebootNotificationShown, |
| false); |
| } |
| |
| void TPMAutoUpdateModePolicyHandler::ShowTPMUpdateOnNextRebootNotification() { |
| Profile* profile = ProfileManager::GetActiveUserProfile(); |
| if (!profile) |
| return; |
| local_state_->SetBoolean(prefs::kTPMUpdateOnNextRebootNotificationShown, |
| true); |
| |
| show_notfication_callback_.Run( |
| chromeos::TpmAutoUpdateUserNotification::kOnNextReboot, profile); |
| } |
| |
| void TPMAutoUpdateModePolicyHandler::SetNotificationTimerForTesting( |
| std::unique_ptr<base::OneShotTimer> timer) { |
| notification_timer_ = std::move(timer); |
| } |
| |
| } // namespace policy |