1 // Copyright 2014 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.
5 #include "chrome/browser/chromeos/login/supervised/supervised_user_authenticator.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_util.h"
10 #include "chrome/browser/chromeos/boot_times_recorder.h"
11 #include "chromeos/cryptohome/async_method_caller.h"
12 #include "chromeos/cryptohome/cryptohome_parameters.h"
13 #include "chromeos/cryptohome/system_salt_getter.h"
14 #include "chromeos/dbus/cryptohome_client.h"
15 #include "chromeos/dbus/dbus_thread_manager.h"
16 #include "chromeos/login/auth/key.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "crypto/sha2.h"
19 #include "google_apis/gaia/gaia_auth_util.h"
20 #include "third_party/cros_system_api/dbus/service_constants.h"
22 using content::BrowserThread
;
28 // Records status and calls resolver->Resolve().
29 void TriggerResolve(SupervisedUserAuthenticator::AuthAttempt
* attempt
,
30 scoped_refptr
<SupervisedUserAuthenticator
> resolver
,
32 cryptohome::MountError return_code
) {
33 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
34 attempt
->RecordCryptohomeStatus(success
, return_code
);
38 // Records status and calls resolver->Resolve().
39 void TriggerResolveResult(SupervisedUserAuthenticator::AuthAttempt
* attempt
,
40 scoped_refptr
<SupervisedUserAuthenticator
> resolver
,
42 const std::string
& result
) {
43 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
44 attempt
->RecordHash(result
);
48 // Calls TriggerResolve while adding login time marker.
49 void TriggerResolveWithLoginTimeMarker(
50 const std::string
& marker_name
,
51 SupervisedUserAuthenticator::AuthAttempt
* attempt
,
52 scoped_refptr
<SupervisedUserAuthenticator
> resolver
,
54 cryptohome::MountError return_code
) {
55 chromeos::BootTimesRecorder::Get()->AddLoginTimeMarker(marker_name
, false);
56 TriggerResolve(attempt
, resolver
, success
, return_code
);
59 // Calls cryptohome's mount method.
60 void Mount(SupervisedUserAuthenticator::AuthAttempt
* attempt
,
61 scoped_refptr
<SupervisedUserAuthenticator
> resolver
,
63 const std::string
& system_salt
) {
64 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
65 chromeos::BootTimesRecorder::Get()->AddLoginTimeMarker(
66 "CryptohomeMount-LMU-Start", false);
68 Key
key(attempt
->password
);
69 key
.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF
, system_salt
);
70 cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount(
74 base::Bind(&TriggerResolveWithLoginTimeMarker
,
75 "CryptohomeMount-LMU-End",
79 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
81 base::Bind(&TriggerResolveResult
, attempt
, resolver
));
84 // Calls cryptohome's addKey method.
85 void AddKey(SupervisedUserAuthenticator::AuthAttempt
* attempt
,
86 scoped_refptr
<SupervisedUserAuthenticator
> resolver
,
87 const std::string
& plain_text_master_key
,
88 const std::string
& system_salt
) {
89 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
90 chromeos::BootTimesRecorder::Get()->AddLoginTimeMarker(
91 "CryptohomeAddKey-LMU-Start", false);
93 Key
user_key(attempt
->password
);
94 user_key
.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF
, system_salt
);
95 Key
master_key(plain_text_master_key
);
96 master_key
.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF
, system_salt
);
97 cryptohome::AsyncMethodCaller::GetInstance()->AsyncAddKey(
100 master_key
.GetSecret(),
101 base::Bind(&TriggerResolveWithLoginTimeMarker
,
102 "CryptohomeAddKey-LMU-End",
109 SupervisedUserAuthenticator::SupervisedUserAuthenticator(
110 AuthStatusConsumer
* consumer
)
111 : consumer_(consumer
) {}
113 void SupervisedUserAuthenticator::AuthenticateToMount(
114 const std::string
& username
,
115 const std::string
& password
) {
116 std::string canonicalized
= gaia::CanonicalizeEmail(username
);
118 current_state_
.reset(new SupervisedUserAuthenticator::AuthAttempt(
119 canonicalized
, password
, false));
121 SystemSaltGetter::Get()->GetSystemSalt(
123 current_state_
.get(),
124 scoped_refptr
<SupervisedUserAuthenticator
>(this),
125 cryptohome::MOUNT_FLAGS_NONE
));
128 void SupervisedUserAuthenticator::AuthenticateToCreate(
129 const std::string
& username
,
130 const std::string
& password
) {
131 std::string canonicalized
= gaia::CanonicalizeEmail(username
);
133 current_state_
.reset(new SupervisedUserAuthenticator::AuthAttempt(
134 canonicalized
, password
, false));
136 SystemSaltGetter::Get()->GetSystemSalt(
138 current_state_
.get(),
139 scoped_refptr
<SupervisedUserAuthenticator
>(this),
140 cryptohome::CREATE_IF_MISSING
));
143 void SupervisedUserAuthenticator::AddMasterKey(
144 const std::string
& username
,
145 const std::string
& password
,
146 const std::string
& master_key
) {
147 std::string canonicalized
= gaia::CanonicalizeEmail(username
);
149 current_state_
.reset(new SupervisedUserAuthenticator::AuthAttempt(
150 canonicalized
, password
, true));
152 SystemSaltGetter::Get()->GetSystemSalt(
154 current_state_
.get(),
155 scoped_refptr
<SupervisedUserAuthenticator
>(this),
159 void SupervisedUserAuthenticator::OnAuthenticationSuccess(
160 const std::string
& mount_hash
,
162 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
163 VLOG(1) << "Supervised user authentication success";
166 consumer_
->OnAddKeySuccess();
168 consumer_
->OnMountSuccess(mount_hash
);
172 void SupervisedUserAuthenticator::OnAuthenticationFailure(
173 SupervisedUserAuthenticator::AuthState state
) {
174 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
175 LOG(WARNING
) << "Supervised user authentication failure";
177 consumer_
->OnAuthenticationFailure(state
);
180 void SupervisedUserAuthenticator::Resolve() {
181 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
182 SupervisedUserAuthenticator::AuthState state
= ResolveState();
183 VLOG(1) << "Resolved state to: " << state
;
186 // These are intermediate states; we need more info from a request that
190 // In this case, whether login succeeded or not, we can't log
191 // the user in because their data is horked. So, override with
192 // the appropriate failure.
193 BrowserThread::PostTask(
196 base::Bind(&SupervisedUserAuthenticator::OnAuthenticationFailure
,
201 // In this case, whether login succeeded or not, we can't log
202 // the user in because no data exist. So, override with
203 // the appropriate failure.
204 BrowserThread::PostTask(
207 base::Bind(&SupervisedUserAuthenticator::OnAuthenticationFailure
,
212 // In this case, we tried to create/mount cryptohome and failed
213 // because of the critical TPM error.
214 // Chrome will notify user and request reboot.
215 BrowserThread::PostTask(
218 base::Bind(&SupervisedUserAuthenticator::OnAuthenticationFailure
,
223 VLOG(2) << "Supervised user login";
224 BrowserThread::PostTask(
227 base::Bind(&SupervisedUserAuthenticator::OnAuthenticationSuccess
,
229 current_state_
->hash(),
230 current_state_
->add_key
));
238 SupervisedUserAuthenticator::~SupervisedUserAuthenticator() {}
240 SupervisedUserAuthenticator::AuthState
241 SupervisedUserAuthenticator::ResolveState() {
242 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
243 // If we haven't mounted the user's home dir yet, we can't be done.
244 // We never get past here if a cryptohome op is still pending.
245 // This is an important invariant.
246 if (!current_state_
->cryptohome_complete())
248 if (!current_state_
->add_key
&& !current_state_
->hash_obtained())
253 if (current_state_
->cryptohome_outcome())
254 state
= ResolveCryptohomeSuccessState();
256 state
= ResolveCryptohomeFailureState();
258 DCHECK(current_state_
->cryptohome_complete());
259 DCHECK(current_state_
->hash_obtained() || current_state_
->add_key
);
263 SupervisedUserAuthenticator::AuthState
264 SupervisedUserAuthenticator::ResolveCryptohomeFailureState() {
265 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
266 LOG(ERROR
) << "Failed to authenticate supervised user, code: "
267 << current_state_
->cryptohome_code();
268 if (current_state_
->cryptohome_code() ==
269 cryptohome::MOUNT_ERROR_TPM_NEEDS_REBOOT
) {
270 // Critical TPM error detected, reboot needed.
274 if (current_state_
->cryptohome_code() ==
275 cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST
) {
276 // If we tried a mount but the user did not exist, then we should wait
277 // for online login to succeed and try again with the "create" flag set.
284 SupervisedUserAuthenticator::AuthState
285 SupervisedUserAuthenticator::ResolveCryptohomeSuccessState() {
286 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
290 SupervisedUserAuthenticator::AuthAttempt::AuthAttempt(
291 const std::string
& username
,
292 const std::string
& password
,
293 bool add_key_attempt
)
294 : username(username
),
296 add_key(add_key_attempt
),
297 cryptohome_complete_(false),
298 cryptohome_outcome_(false),
299 hash_obtained_(false),
300 cryptohome_code_(cryptohome::MOUNT_ERROR_NONE
) {}
302 SupervisedUserAuthenticator::AuthAttempt::~AuthAttempt() {}
304 void SupervisedUserAuthenticator::AuthAttempt::RecordCryptohomeStatus(
305 bool cryptohome_outcome
,
306 cryptohome::MountError cryptohome_code
) {
307 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
308 cryptohome_complete_
= true;
309 cryptohome_outcome_
= cryptohome_outcome
;
310 cryptohome_code_
= cryptohome_code
;
313 void SupervisedUserAuthenticator::AuthAttempt::RecordHash(
314 const std::string
& hash
) {
315 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
316 hash_obtained_
= true;
320 bool SupervisedUserAuthenticator::AuthAttempt::cryptohome_complete() {
321 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
322 return cryptohome_complete_
;
325 bool SupervisedUserAuthenticator::AuthAttempt::cryptohome_outcome() {
326 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
327 return cryptohome_outcome_
;
330 cryptohome::MountError
331 SupervisedUserAuthenticator::AuthAttempt::cryptohome_code() {
332 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
333 return cryptohome_code_
;
336 bool SupervisedUserAuthenticator::AuthAttempt::hash_obtained() {
337 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
338 return hash_obtained_
;
341 std::string
SupervisedUserAuthenticator::AuthAttempt::hash() {
342 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
346 } // namespace chromeos