| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/auth/cryptohome_pin_engine.h" |
| |
| #include "ash/constants/ash_pref_names.h" |
| #include "base/check_deref.h" |
| #include "base/check_op.h" |
| #include "base/containers/contains.h" |
| #include "base/memory/raw_ref.h" |
| #include "chrome/browser/ash/login/users/chrome_user_manager_util.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chromeos/ash/components/login/auth/auth_performer.h" |
| #include "components/account_id/account_id.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/user_manager/known_user.h" |
| |
| namespace ash::legacy { |
| namespace { |
| |
| // Possible values for the `kQuickUnlockModeAllowlist` policy. |
| constexpr char kFactorsOptionAll[] = "all"; |
| constexpr char kFactorsOptionPin[] = "PIN"; |
| |
| bool HasPolicyValue(const PrefService& pref_service, |
| CryptohomePinEngine::Purpose purpose, |
| const char* value) { |
| const base::Value::List* factors = nullptr; |
| switch (purpose) { |
| case CryptohomePinEngine::Purpose::kUnlock: |
| factors = &pref_service.GetList(prefs::kQuickUnlockModeAllowlist); |
| break; |
| case CryptohomePinEngine::Purpose::kWebAuthn: |
| factors = &pref_service.GetList(prefs::kWebAuthnFactors); |
| break; |
| default: |
| return false; |
| } |
| return base::Contains(*factors, base::Value(value)); |
| } |
| |
| // Check if pin is disabled for a specific purpose (so not including |
| // kAny) by reading the policy value. |
| bool IsPinDisabledByPolicySinglePurpose(const PrefService& pref_service, |
| CryptohomePinEngine::Purpose purpose) { |
| DCHECK_NE(purpose, CryptohomePinEngine::Purpose::kAny); |
| const bool enabled = |
| HasPolicyValue(pref_service, purpose, kFactorsOptionAll) || |
| HasPolicyValue(pref_service, purpose, kFactorsOptionPin); |
| return !enabled; |
| } |
| |
| // Read the salt from local state. |
| std::string GetUserSalt(PrefService& local_state, const AccountId& account_id) { |
| user_manager::KnownUser known_user(&local_state); |
| if (const std::string* salt = |
| known_user.FindStringPath(account_id, prefs::kQuickUnlockPinSalt)) { |
| return *salt; |
| } |
| return {}; |
| } |
| |
| } // namespace |
| |
| CryptohomePinEngine::CryptohomePinEngine(PrefService* local_state, |
| ash::AuthPerformer* auth_performer) |
| : local_state_(CHECK_DEREF(local_state)), |
| auth_performer_(auth_performer), |
| auth_factor_editor_(ash::UserDataAuthClient::Get()) {} |
| |
| CryptohomePinEngine::~CryptohomePinEngine() = default; |
| |
| std::optional<bool> CryptohomePinEngine::IsCryptohomePinDisabledByPolicy( |
| const AccountId& account_id, |
| CryptohomePinEngine::Purpose purpose) const { |
| Profile* profile = |
| ash::ProfileHelper::Get()->GetProfileByAccountId(account_id); |
| |
| if (!profile) { |
| return std::nullopt; |
| } |
| |
| auto* pref_service = profile->GetPrefs(); |
| |
| if (!pref_service) { |
| return std::nullopt; |
| } |
| |
| if (purpose == CryptohomePinEngine::Purpose::kAny) { |
| return IsPinDisabledByPolicySinglePurpose( |
| *pref_service, CryptohomePinEngine::Purpose::kUnlock) && |
| IsPinDisabledByPolicySinglePurpose( |
| *pref_service, CryptohomePinEngine::Purpose::kWebAuthn); |
| } |
| return IsPinDisabledByPolicySinglePurpose(*pref_service, purpose); |
| } |
| |
| bool CryptohomePinEngine::ShouldSkipSetupBecauseOfPolicy( |
| const AccountId& account_id) const { |
| std::optional<bool> is_pin_disabled = IsCryptohomePinDisabledByPolicy( |
| account_id, CryptohomePinEngine::Purpose::kAny); |
| return is_pin_disabled.value_or(false); |
| } |
| |
| void CryptohomePinEngine::IsPinAuthAvailable( |
| Purpose purpose, |
| std::unique_ptr<UserContext> user_context, |
| IsPinAuthAvailableCallback callback) { |
| auto is_pin_disabled_by_policy = |
| IsCryptohomePinDisabledByPolicy(user_context->GetAccountId(), purpose); |
| |
| if (!is_pin_disabled_by_policy.has_value() || |
| is_pin_disabled_by_policy.value()) { |
| std::move(callback).Run(false, std::move(user_context)); |
| return; |
| } |
| |
| CheckCryptohomePinFactor(std::move(user_context), std::move(callback)); |
| } |
| |
| void CryptohomePinEngine::Authenticate( |
| const cryptohome::RawPin& pin, |
| std::unique_ptr<UserContext> user_context, |
| AuthOperationCallback callback) { |
| auto salt = GetUserSalt(local_state_.get(), user_context->GetAccountId()); |
| auth_performer_->AuthenticateWithPin(*pin, salt, std::move(user_context), |
| std::move(callback)); |
| } |
| |
| void CryptohomePinEngine::CheckCryptohomePinFactor( |
| std::unique_ptr<UserContext> user_context, |
| IsPinAuthAvailableCallback callback) { |
| auth_factor_editor_.GetAuthFactorsConfiguration( |
| std::move(user_context), |
| base::BindOnce(&CryptohomePinEngine::OnGetAuthFactorsConfiguration, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void CryptohomePinEngine::OnGetAuthFactorsConfiguration( |
| IsPinAuthAvailableCallback callback, |
| std::unique_ptr<UserContext> user_context, |
| std::optional<AuthenticationError> error) { |
| if (error.has_value()) { |
| std::move(callback).Run(false, std::move(user_context)); |
| return; |
| } |
| |
| const auto& config = user_context->GetAuthFactorsConfiguration(); |
| const cryptohome::AuthFactor* pin_factor = |
| config.FindFactorByType(cryptohome::AuthFactorType::kPin); |
| |
| if (!pin_factor || pin_factor->GetPinStatus().IsLockedFactor()) { |
| std::move(callback).Run(false, std::move(user_context)); |
| return; |
| } |
| |
| std::move(callback).Run(true, std::move(user_context)); |
| } |
| |
| } // namespace ash::legacy |