Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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/login/quick_unlock/pin_backend.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/threading/thread_task_runner_handle.h" | |
| 9 #include "chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h" | |
| 10 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" | |
| 11 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" | |
| 12 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" | |
| 13 #include "chromeos/cryptohome/cryptohome_parameters.h" | |
| 14 #include "chromeos/cryptohome/homedir_methods.h" | |
| 15 #include "chromeos/cryptohome/system_salt_getter.h" | |
| 16 #include "chromeos/login/auth/user_context.h" | |
| 17 #include "components/signin/core/account_id/account_id.h" | |
| 18 | |
| 19 namespace chromeos { | |
| 20 namespace quick_unlock { | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 constexpr const char* kCryptohomePinLabel = "pin_label"; | |
| 25 | |
| 26 void DoNothingCryptohome(bool success, cryptohome::MountError return_code) {} | |
| 27 | |
| 28 class CryptohomeBackend { | |
| 29 public: | |
| 30 CryptohomeBackend(); | |
| 31 ~CryptohomeBackend(); | |
| 32 | |
| 33 void EnsurePinIsNotInCryptohome(const AccountId& account_id, | |
| 34 const UserContext& user_context) const; | |
| 35 void IsPinSetInCryptohome(const AccountId& account_id, | |
| 36 const PinBackend::BoolCallback& result) const; | |
| 37 void SetPin(const UserContext& user_context, const std::string& pin); | |
| 38 void RemovePin(const UserContext& user_context); | |
| 39 bool NeedsStrongAuth() const; | |
| 40 | |
| 41 private: | |
| 42 void OnSystemSaltObtained(const std::string& system_salt); | |
| 43 // Called when we add or remove a key from cryptohome. | |
| 44 void OnCryptohomeKeyChange(bool success, cryptohome::MountError return_code); | |
| 45 | |
| 46 void SetIsPinSet(bool is_set); | |
| 47 | |
| 48 private: | |
| 49 bool salt_obtained_ = false; | |
| 50 std::string system_salt_; | |
| 51 std::vector<base::Closure> system_salt_callbacks_; | |
| 52 | |
| 53 base::WeakPtrFactory<CryptohomeBackend> weak_factory_; | |
|
achuithb
2017/05/13 01:01:58
You can do weak_factory_{this};
https://cs.chromiu
jdufault
2017/06/06 18:17:06
Done - that's a nice pattern.
| |
| 54 | |
| 55 DISALLOW_COPY_AND_ASSIGN(CryptohomeBackend); | |
| 56 }; | |
| 57 | |
| 58 CryptohomeBackend::CryptohomeBackend() : weak_factory_(this) { | |
| 59 SystemSaltGetter::Get()->GetSystemSalt(base::Bind( | |
| 60 &CryptohomeBackend::OnSystemSaltObtained, weak_factory_.GetWeakPtr())); | |
| 61 } | |
| 62 | |
| 63 CryptohomeBackend::~CryptohomeBackend() {} | |
| 64 | |
| 65 void CryptohomeBackend::IsPinSetInCryptohome( | |
| 66 const AccountId& account_id, | |
| 67 const PinBackend::BoolCallback& callback) const { | |
| 68 auto on_key_data = | |
|
achuithb
2017/05/13 01:01:58
Let's maybe pull this out into a named callback? I
jdufault
2017/06/06 18:17:06
Done.
| |
| 69 [](const PinBackend::BoolCallback& callback, bool success, | |
| 70 cryptohome::MountError return_code, | |
| 71 const std::vector<cryptohome::KeyDefinition>& key_definitions) { | |
| 72 | |
| 73 for (const cryptohome::KeyDefinition& definition : key_definitions) { | |
| 74 if (definition.label == kCryptohomePinLabel) { | |
| 75 callback.Run(true); | |
| 76 return; | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 callback.Run(false); | |
| 81 }; | |
| 82 | |
| 83 cryptohome::HomedirMethods::GetInstance()->GetKeyDataEx( | |
| 84 cryptohome::Identification(account_id), kCryptohomePinLabel, | |
| 85 base::Bind(on_key_data, callback)); | |
| 86 } | |
| 87 | |
| 88 void CryptohomeBackend::SetPin(const UserContext& user_context, | |
| 89 const std::string& pin) { | |
| 90 // Rerun this method only after we have system salt. | |
| 91 if (!salt_obtained_) { | |
| 92 system_salt_callbacks_.push_back(base::Bind(&CryptohomeBackend::SetPin, | |
| 93 weak_factory_.GetWeakPtr(), | |
| 94 user_context, pin)); | |
| 95 return; | |
| 96 } | |
| 97 | |
| 98 const std::string pin_secret = PinBackend::ComputeSecret(pin, system_salt_); | |
| 99 | |
| 100 cryptohome::Identification id(user_context.GetAccountId()); | |
|
achuithb
2017/05/13 01:01:58
nit all const
jdufault
2017/06/06 18:17:06
Done.
| |
| 101 cryptohome::Authorization auth(user_context.GetKey()->GetSecret(), ""); | |
| 102 cryptohome::KeyDefinition key_def(pin_secret, kCryptohomePinLabel, | |
| 103 cryptohome::PRIV_DEFAULT); | |
| 104 cryptohome::HomedirMethods::GetInstance()->AddKeyEx( | |
| 105 id, auth, key_def, true /*replace_existing*/, | |
| 106 base::Bind(&DoNothingCryptohome)); | |
| 107 } | |
| 108 | |
| 109 void CryptohomeBackend::OnSystemSaltObtained(const std::string& system_salt) { | |
| 110 salt_obtained_ = true; | |
| 111 system_salt_ = system_salt; | |
| 112 for (std::vector<base::Closure>::const_iterator it = | |
|
achuithb
2017/05/13 01:01:58
Does range-based loop not work here?
jdufault
2017/06/06 18:17:06
Done - not sure why I wasn't using it.
| |
| 113 system_salt_callbacks_.begin(); | |
| 114 it != system_salt_callbacks_.end(); ++it) { | |
| 115 it->Run(); | |
| 116 } | |
| 117 system_salt_callbacks_.clear(); | |
| 118 } | |
| 119 | |
| 120 void CryptohomeBackend::RemovePin(const UserContext& user_context) { | |
| 121 // Rerun this method only after we have system salt. | |
| 122 if (!salt_obtained_) { | |
| 123 system_salt_callbacks_.push_back(base::Bind(&CryptohomeBackend::RemovePin, | |
| 124 weak_factory_.GetWeakPtr(), | |
| 125 user_context)); | |
| 126 return; | |
| 127 } | |
| 128 | |
| 129 // Remove any PIN data from cryptohome. | |
| 130 cryptohome::Identification id(user_context.GetAccountId()); | |
|
achuithb
2017/05/13 01:01:58
nit const
jdufault
2017/06/06 18:17:06
Done.
| |
| 131 cryptohome::Authorization auth(user_context.GetKey()->GetSecret(), ""); | |
| 132 cryptohome::HomedirMethods::GetInstance()->RemoveKeyEx( | |
| 133 id, auth, kCryptohomePinLabel, base::Bind(&DoNothingCryptohome)); | |
| 134 } | |
| 135 | |
| 136 CryptohomeBackend* g_cryptohome_backend_ = nullptr; | |
| 137 | |
| 138 CryptohomeBackend* GetCryptohomeBackend() { | |
| 139 if (!g_cryptohome_backend_) | |
| 140 g_cryptohome_backend_ = new CryptohomeBackend(); | |
| 141 return g_cryptohome_backend_; | |
|
achuithb
2017/05/13 01:01:58
it's ok to leak this?
Isn't there a singleton cl
jdufault
2017/06/06 18:17:06
I tried converting to a leaky base::Singleton inst
| |
| 142 } | |
| 143 | |
| 144 QuickUnlockStorage* GetPrefsBackend(const AccountId& account_id) { | |
| 145 return QuickUnlockFactory::GetForAccountId(account_id); | |
| 146 } | |
| 147 | |
| 148 void SendResponse(const PinBackend::BoolCallback& result, bool value) { | |
| 149 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | |
| 150 base::Bind(result, value)); | |
| 151 } | |
| 152 | |
| 153 } // namespace | |
| 154 | |
| 155 // static | |
| 156 void PinBackend::IsSet(const AccountId& account_id, | |
| 157 const BoolCallback& result) { | |
| 158 if (GetPinStorageType() == PinStorageType::kCryptohome) { | |
| 159 GetCryptohomeBackend()->IsPinSetInCryptohome(account_id, result); | |
| 160 } else { | |
| 161 QuickUnlockStorage* storage = GetPrefsBackend(account_id); | |
| 162 DCHECK(storage); | |
| 163 SendResponse(result, storage->pin_storage_prefs()->IsPinSet()); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 // static | |
| 168 void PinBackend::Set(const UserContext& user_context, const std::string& pin) { | |
| 169 QuickUnlockStorage* storage = GetPrefsBackend(user_context.GetAccountId()); | |
| 170 DCHECK(storage); | |
| 171 | |
| 172 // Make sure to remove the other storage pin. | |
| 173 if (GetPinStorageType() == PinStorageType::kCryptohome) { | |
| 174 GetCryptohomeBackend()->SetPin(user_context, pin); | |
| 175 storage->pin_storage_prefs()->RemovePin(); | |
| 176 } else { | |
| 177 storage->pin_storage_prefs()->SetPin(pin); | |
| 178 GetCryptohomeBackend()->RemovePin(user_context); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 // static | |
| 183 void PinBackend::Remove(const UserContext& user_context) { | |
| 184 GetCryptohomeBackend()->RemovePin(user_context); | |
| 185 | |
| 186 QuickUnlockStorage* storage = GetPrefsBackend(user_context.GetAccountId()); | |
| 187 DCHECK(storage); | |
| 188 storage->pin_storage_prefs()->RemovePin(); | |
| 189 } | |
| 190 | |
| 191 // static | |
| 192 void PinBackend::CanAuthenticate(const AccountId& account_id, | |
| 193 const BoolCallback& result) { | |
| 194 if (GetPinStorageType() == PinStorageType::kCryptohome) { | |
| 195 GetCryptohomeBackend()->IsPinSetInCryptohome(account_id, result); | |
| 196 } else { | |
| 197 QuickUnlockStorage* storage = GetPrefsBackend(account_id); | |
| 198 if (!storage) { | |
| 199 SendResponse(result, false); | |
| 200 } else { | |
| 201 SendResponse( | |
| 202 result, | |
| 203 storage->HasStrongAuth() && | |
| 204 storage->pin_storage_prefs()->IsPinAuthenticationAvailable()); | |
| 205 } | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 // static | |
| 210 void PinBackend::TryAuthenticate(const AccountId& account_id, | |
| 211 const std::string& pin, | |
| 212 const BoolCallback& result) { | |
| 213 if (GetPinStorageType() == PinStorageType::kCryptohome) { | |
| 214 // TODO(jdufalt): Refactor login auth such that typing a user password does | |
| 215 // not run crypthome check with wildcard key label. That means we will be | |
| 216 // forced to run an authentication check here. | |
| 217 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | |
| 218 base::Bind(result, false)); | |
| 219 } else { | |
| 220 QuickUnlockStorage* storage = GetPrefsBackend(account_id); | |
| 221 DCHECK(storage); | |
| 222 | |
| 223 if (!storage->HasStrongAuth()) { | |
| 224 SendResponse(result, false); | |
| 225 } else { | |
| 226 SendResponse(result, | |
| 227 storage->pin_storage_prefs()->TryAuthenticatePin(pin)); | |
| 228 } | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 // static | |
| 233 void PinBackend::NotifyAuthentication(const AccountId& account_id) { | |
| 234 // Nothing to do for cryptohome backend. | |
| 235 | |
| 236 if (GetPinStorageType() == PinStorageType::kPrefs) { | |
| 237 QuickUnlockStorage* storage = GetPrefsBackend(account_id); | |
| 238 if (!storage) | |
| 239 return; | |
| 240 | |
| 241 storage->pin_storage_prefs()->ResetUnlockAttemptCount(); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 // static | |
| 246 std::string PinBackend::ComputeSecret(const std::string& pin, | |
| 247 const std::string& salt) { | |
| 248 Key key(pin); | |
| 249 key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, salt); | |
| 250 return key.GetSecret(); | |
| 251 } | |
| 252 | |
| 253 // static | |
| 254 void PinBackend::ResetForTesting() { | |
| 255 if (g_cryptohome_backend_) | |
| 256 delete g_cryptohome_backend_; | |
| 257 g_cryptohome_backend_ = nullptr; | |
| 258 } | |
| 259 | |
| 260 } // namespace quick_unlock | |
| 261 } // namespace chromeos | |
| OLD | NEW |