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 |