OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/parallel_authenticator.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/command_line.h" | |
9 #include "base/files/file_path.h" | |
10 #include "base/logging.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "base/strings/string_util.h" | |
13 #include "chrome/browser/chrome_notification_types.h" | |
14 #include "chrome/browser/chromeos/boot_times_loader.h" | |
15 #include "chrome/browser/chromeos/login/authentication_notification_details.h" | |
16 #include "chrome/browser/chromeos/login/login_status_consumer.h" | |
17 #include "chrome/browser/chromeos/login/user.h" | |
18 #include "chrome/browser/chromeos/login/user_manager.h" | |
19 #include "chrome/browser/chromeos/settings/cros_settings.h" | |
20 #include "chrome/common/chrome_switches.h" | |
21 #include "chromeos/cryptohome/async_method_caller.h" | |
22 #include "chromeos/cryptohome/system_salt_getter.h" | |
23 #include "chromeos/dbus/cryptohome_client.h" | |
24 #include "chromeos/dbus/dbus_thread_manager.h" | |
25 #include "chromeos/login/login_state.h" | |
26 #include "content/public/browser/browser_thread.h" | |
27 #include "content/public/browser/notification_service.h" | |
28 #include "crypto/sha2.h" | |
29 #include "google_apis/gaia/gaia_auth_util.h" | |
30 #include "third_party/cros_system_api/dbus/service_constants.h" | |
31 | |
32 using content::BrowserThread; | |
33 | |
34 namespace chromeos { | |
35 | |
36 namespace { | |
37 | |
38 // Length of password hashed with SHA-256. | |
39 const int kPasswordHashLength = 32; | |
40 | |
41 // Records status and calls resolver->Resolve(). | |
42 void TriggerResolve(AuthAttemptState* attempt, | |
43 scoped_refptr<ParallelAuthenticator> resolver, | |
44 bool success, | |
45 cryptohome::MountError return_code) { | |
46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
47 attempt->RecordCryptohomeStatus(success, return_code); | |
48 resolver->Resolve(); | |
49 } | |
50 | |
51 // Records get hash status and calls resolver->Resolve(). | |
52 void TriggerResolveHash(AuthAttemptState* attempt, | |
53 scoped_refptr<ParallelAuthenticator> resolver, | |
54 bool success, | |
55 const std::string& username_hash) { | |
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
57 if (success) | |
58 attempt->RecordUsernameHash(username_hash); | |
59 else | |
60 attempt->RecordUsernameHashFailed(); | |
61 resolver->Resolve(); | |
62 } | |
63 | |
64 // Calls TriggerResolve while adding login time marker. | |
65 void TriggerResolveWithLoginTimeMarker( | |
66 const std::string& marker_name, | |
67 AuthAttemptState* attempt, | |
68 scoped_refptr<ParallelAuthenticator> resolver, | |
69 bool success, | |
70 cryptohome::MountError return_code) { | |
71 chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(marker_name, false); | |
72 TriggerResolve(attempt, resolver, success, return_code); | |
73 } | |
74 | |
75 // Calls cryptohome's mount method. | |
76 void Mount(AuthAttemptState* attempt, | |
77 scoped_refptr<ParallelAuthenticator> resolver, | |
78 int flags, | |
79 const std::string& system_salt) { | |
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
81 chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( | |
82 "CryptohomeMount-Start", false); | |
83 // Set state that username_hash is requested here so that test implementation | |
84 // that returns directly would not generate 2 OnLoginSucces() calls. | |
85 attempt->UsernameHashRequested(); | |
86 cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount( | |
87 attempt->user_context.GetUserID(), | |
88 ParallelAuthenticator::HashPassword(attempt->user_context.GetPassword(), | |
89 system_salt), | |
90 flags, | |
91 base::Bind(&TriggerResolveWithLoginTimeMarker, | |
92 "CryptohomeMount-End", | |
93 attempt, | |
94 resolver)); | |
95 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( | |
96 attempt->user_context.GetUserID(), | |
97 base::Bind(&TriggerResolveHash, | |
98 attempt, | |
99 resolver)); | |
100 } | |
101 | |
102 // Calls cryptohome's mount method for guest. | |
103 void MountGuest(AuthAttemptState* attempt, | |
104 scoped_refptr<ParallelAuthenticator> resolver) { | |
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
106 cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountGuest( | |
107 base::Bind(&TriggerResolveWithLoginTimeMarker, | |
108 "CryptohomeMount-End", | |
109 attempt, | |
110 resolver)); | |
111 } | |
112 | |
113 // Calls cryptohome's mount method for guest and also get the user hash from | |
114 // cryptohome. | |
115 void MountGuestAndGetHash(AuthAttemptState* attempt, | |
116 scoped_refptr<ParallelAuthenticator> resolver) { | |
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
118 attempt->UsernameHashRequested(); | |
119 cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountGuest( | |
120 base::Bind(&TriggerResolveWithLoginTimeMarker, | |
121 "CryptohomeMount-End", | |
122 attempt, | |
123 resolver)); | |
124 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( | |
125 attempt->user_context.GetUserID(), | |
126 base::Bind(&TriggerResolveHash, | |
127 attempt, | |
128 resolver)); | |
129 } | |
130 | |
131 // Calls cryptohome's MountPublic method | |
132 void MountPublic(AuthAttemptState* attempt, | |
133 scoped_refptr<ParallelAuthenticator> resolver, | |
134 int flags) { | |
135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
136 cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic( | |
137 attempt->user_context.GetUserID(), | |
138 flags, | |
139 base::Bind(&TriggerResolveWithLoginTimeMarker, | |
140 "CryptohomeMountPublic-End", | |
141 attempt, | |
142 resolver)); | |
143 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( | |
144 attempt->user_context.GetUserID(), | |
145 base::Bind(&TriggerResolveHash, | |
146 attempt, | |
147 resolver)); | |
148 } | |
149 | |
150 // Calls cryptohome's key migration method. | |
151 void Migrate(AuthAttemptState* attempt, | |
152 scoped_refptr<ParallelAuthenticator> resolver, | |
153 bool passing_old_hash, | |
154 const std::string& old_password, | |
155 const std::string& system_salt) { | |
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
157 chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( | |
158 "CryptohomeMigrate-Start", false); | |
159 cryptohome::AsyncMethodCaller* caller = | |
160 cryptohome::AsyncMethodCaller::GetInstance(); | |
161 if (passing_old_hash) { | |
162 caller->AsyncMigrateKey( | |
163 attempt->user_context.GetUserID(), | |
164 ParallelAuthenticator::HashPassword(old_password, system_salt), | |
165 ParallelAuthenticator::HashPassword(attempt->user_context.GetPassword(), | |
166 system_salt), | |
167 base::Bind(&TriggerResolveWithLoginTimeMarker, | |
168 "CryptohomeMount-End", | |
169 attempt, | |
170 resolver)); | |
171 } else { | |
172 caller->AsyncMigrateKey( | |
173 attempt->user_context.GetUserID(), | |
174 ParallelAuthenticator::HashPassword(attempt->user_context.GetPassword(), | |
175 system_salt), | |
176 ParallelAuthenticator::HashPassword(old_password, system_salt), | |
177 base::Bind(&TriggerResolveWithLoginTimeMarker, | |
178 "CryptohomeMount-End", | |
179 attempt, | |
180 resolver)); | |
181 } | |
182 } | |
183 | |
184 // Calls cryptohome's remove method. | |
185 void Remove(AuthAttemptState* attempt, | |
186 scoped_refptr<ParallelAuthenticator> resolver) { | |
187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
188 chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( | |
189 "CryptohomeRemove-Start", false); | |
190 cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove( | |
191 attempt->user_context.GetUserID(), | |
192 base::Bind(&TriggerResolveWithLoginTimeMarker, | |
193 "CryptohomeRemove-End", | |
194 attempt, | |
195 resolver)); | |
196 } | |
197 | |
198 // Calls cryptohome's key check method. | |
199 void CheckKey(AuthAttemptState* attempt, | |
200 scoped_refptr<ParallelAuthenticator> resolver, | |
201 const std::string& system_salt) { | |
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
203 cryptohome::AsyncMethodCaller::GetInstance()->AsyncCheckKey( | |
204 attempt->user_context.GetUserID(), | |
205 ParallelAuthenticator::HashPassword(attempt->user_context.GetPassword(), | |
206 system_salt), | |
207 base::Bind(&TriggerResolve, attempt, resolver)); | |
208 } | |
209 | |
210 } // namespace | |
211 | |
212 ParallelAuthenticator::ParallelAuthenticator(LoginStatusConsumer* consumer) | |
213 : Authenticator(consumer), | |
214 migrate_attempted_(false), | |
215 remove_attempted_(false), | |
216 resync_attempted_(false), | |
217 ephemeral_mount_attempted_(false), | |
218 check_key_attempted_(false), | |
219 already_reported_success_(false), | |
220 owner_is_verified_(false), | |
221 user_can_login_(false), | |
222 remove_user_data_on_failure_(false), | |
223 delayed_login_failure_(NULL) { | |
224 } | |
225 | |
226 void ParallelAuthenticator::AuthenticateToLogin( | |
227 Profile* profile, | |
228 const UserContext& user_context) { | |
229 std::string canonicalized = gaia::CanonicalizeEmail(user_context.GetUserID()); | |
230 authentication_profile_ = profile; | |
231 current_state_.reset( | |
232 new AuthAttemptState( | |
233 UserContext(canonicalized, | |
234 user_context.GetPassword(), | |
235 user_context.GetAuthCode()), | |
236 std::string(), // login_token, not used. | |
237 std::string(), // login_captcha, not used. | |
238 User::USER_TYPE_REGULAR, | |
239 !UserManager::Get()->IsKnownUser(canonicalized))); | |
240 // Reset the verified flag. | |
241 owner_is_verified_ = false; | |
242 | |
243 SystemSaltGetter::Get()->GetSystemSalt( | |
244 base::Bind(&Mount, | |
245 current_state_.get(), | |
246 scoped_refptr<ParallelAuthenticator>(this), | |
247 cryptohome::MOUNT_FLAGS_NONE)); | |
248 } | |
249 | |
250 void ParallelAuthenticator::CompleteLogin(Profile* profile, | |
251 const UserContext& user_context) { | |
252 std::string canonicalized = gaia::CanonicalizeEmail(user_context.GetUserID()); | |
253 authentication_profile_ = profile; | |
254 current_state_.reset( | |
255 new AuthAttemptState( | |
256 UserContext(canonicalized, | |
257 user_context.GetPassword(), | |
258 user_context.GetAuthCode(), | |
259 user_context.GetUserIDHash(), | |
260 user_context.IsUsingOAuth(), | |
261 user_context.GetAuthFlow()), | |
262 !UserManager::Get()->IsKnownUser(canonicalized))); | |
263 | |
264 // Reset the verified flag. | |
265 owner_is_verified_ = false; | |
266 | |
267 SystemSaltGetter::Get()->GetSystemSalt( | |
268 base::Bind(&Mount, | |
269 current_state_.get(), | |
270 scoped_refptr<ParallelAuthenticator>(this), | |
271 cryptohome::MOUNT_FLAGS_NONE)); | |
272 | |
273 // For login completion from extension, we just need to resolve the current | |
274 // auth attempt state, the rest of OAuth related tasks will be done in | |
275 // parallel. | |
276 BrowserThread::PostTask( | |
277 BrowserThread::UI, FROM_HERE, | |
278 base::Bind(&ParallelAuthenticator::ResolveLoginCompletionStatus, this)); | |
279 } | |
280 | |
281 void ParallelAuthenticator::AuthenticateToUnlock( | |
282 const UserContext& user_context) { | |
283 current_state_.reset( | |
284 new AuthAttemptState( | |
285 gaia::CanonicalizeEmail(user_context.GetUserID()), | |
286 user_context.GetPassword())); | |
287 remove_user_data_on_failure_ = false; | |
288 check_key_attempted_ = true; | |
289 SystemSaltGetter::Get()->GetSystemSalt( | |
290 base::Bind(&CheckKey, | |
291 current_state_.get(), | |
292 scoped_refptr<ParallelAuthenticator>(this))); | |
293 } | |
294 | |
295 void ParallelAuthenticator::LoginAsLocallyManagedUser( | |
296 const UserContext& user_context) { | |
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
298 // TODO(nkostylev): Pass proper value for |user_is_new| or remove (not used). | |
299 current_state_.reset( | |
300 new AuthAttemptState(user_context, | |
301 "", // login_token | |
302 "", // login_captcha | |
303 User::USER_TYPE_LOCALLY_MANAGED, | |
304 false)); | |
305 remove_user_data_on_failure_ = false; | |
306 SystemSaltGetter::Get()->GetSystemSalt( | |
307 base::Bind(&Mount, | |
308 current_state_.get(), | |
309 scoped_refptr<ParallelAuthenticator>(this), | |
310 cryptohome::MOUNT_FLAGS_NONE)); | |
311 } | |
312 | |
313 void ParallelAuthenticator::LoginRetailMode() { | |
314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
315 // Note: |kRetailModeUserEMail| is used in other places to identify a retail | |
316 // mode session. | |
317 current_state_.reset(new AuthAttemptState( | |
318 UserContext(UserManager::kRetailModeUserName, | |
319 std::string(), // password | |
320 std::string()), // auth_code | |
321 std::string(), // login_token | |
322 std::string(), // login_captcha | |
323 User::USER_TYPE_RETAIL_MODE, | |
324 false)); | |
325 remove_user_data_on_failure_ = false; | |
326 ephemeral_mount_attempted_ = true; | |
327 MountGuest(current_state_.get(), | |
328 scoped_refptr<ParallelAuthenticator>(this)); | |
329 } | |
330 | |
331 void ParallelAuthenticator::LoginOffTheRecord() { | |
332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
333 current_state_.reset(new AuthAttemptState( | |
334 UserContext(UserManager::kGuestUserName, // username | |
335 std::string(), // password | |
336 std::string()), // auth_code | |
337 std::string(), // login_token | |
338 std::string(), // login_captcha | |
339 User::USER_TYPE_GUEST, | |
340 false)); | |
341 remove_user_data_on_failure_ = false; | |
342 ephemeral_mount_attempted_ = true; | |
343 MountGuest(current_state_.get(), | |
344 scoped_refptr<ParallelAuthenticator>(this)); | |
345 } | |
346 | |
347 void ParallelAuthenticator::LoginAsPublicAccount(const std::string& username) { | |
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
349 current_state_.reset(new AuthAttemptState( | |
350 UserContext(username, | |
351 std::string(), // password | |
352 std::string()), // auth_code | |
353 std::string(), // login_token | |
354 std::string(), // login_captcha | |
355 User::USER_TYPE_PUBLIC_ACCOUNT, | |
356 false)); | |
357 remove_user_data_on_failure_ = false; | |
358 ephemeral_mount_attempted_ = true; | |
359 SystemSaltGetter::Get()->GetSystemSalt( | |
360 base::Bind(&Mount, | |
361 current_state_.get(), | |
362 scoped_refptr<ParallelAuthenticator>(this), | |
363 cryptohome::CREATE_IF_MISSING | cryptohome::ENSURE_EPHEMERAL)); | |
364 } | |
365 | |
366 void ParallelAuthenticator::LoginAsKioskAccount( | |
367 const std::string& app_user_id, | |
368 bool use_guest_mount) { | |
369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
370 | |
371 const std::string user_id = | |
372 use_guest_mount ? UserManager::kGuestUserName : app_user_id; | |
373 current_state_.reset(new AuthAttemptState( | |
374 UserContext(user_id, | |
375 std::string(), // password | |
376 std::string()), // auth_code | |
377 std::string(), // login_token | |
378 std::string(), // login_captcha | |
379 User::USER_TYPE_KIOSK_APP, | |
380 false)); | |
381 | |
382 remove_user_data_on_failure_ = true; | |
383 if (!use_guest_mount) { | |
384 MountPublic(current_state_.get(), | |
385 scoped_refptr<ParallelAuthenticator>(this), | |
386 cryptohome::CREATE_IF_MISSING); | |
387 } else { | |
388 ephemeral_mount_attempted_ = true; | |
389 MountGuestAndGetHash(current_state_.get(), | |
390 scoped_refptr<ParallelAuthenticator>(this)); | |
391 } | |
392 } | |
393 | |
394 void ParallelAuthenticator::OnRetailModeLoginSuccess() { | |
395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
396 VLOG(1) << "Retail mode login success"; | |
397 // Send notification of success | |
398 AuthenticationNotificationDetails details(true); | |
399 content::NotificationService::current()->Notify( | |
400 chrome::NOTIFICATION_LOGIN_AUTHENTICATION, | |
401 content::NotificationService::AllSources(), | |
402 content::Details<AuthenticationNotificationDetails>(&details)); | |
403 if (consumer_) | |
404 consumer_->OnRetailModeLoginSuccess(current_state_->user_context); | |
405 } | |
406 | |
407 void ParallelAuthenticator::OnLoginSuccess() { | |
408 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
409 VLOG(1) << "Login success"; | |
410 // Send notification of success | |
411 AuthenticationNotificationDetails details(true); | |
412 content::NotificationService::current()->Notify( | |
413 chrome::NOTIFICATION_LOGIN_AUTHENTICATION, | |
414 content::NotificationService::AllSources(), | |
415 content::Details<AuthenticationNotificationDetails>(&details)); | |
416 { | |
417 base::AutoLock for_this_block(success_lock_); | |
418 already_reported_success_ = true; | |
419 } | |
420 if (consumer_) | |
421 consumer_->OnLoginSuccess(current_state_->user_context); | |
422 } | |
423 | |
424 void ParallelAuthenticator::OnOffTheRecordLoginSuccess() { | |
425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
426 // Send notification of success | |
427 AuthenticationNotificationDetails details(true); | |
428 content::NotificationService::current()->Notify( | |
429 chrome::NOTIFICATION_LOGIN_AUTHENTICATION, | |
430 content::NotificationService::AllSources(), | |
431 content::Details<AuthenticationNotificationDetails>(&details)); | |
432 if (consumer_) | |
433 consumer_->OnOffTheRecordLoginSuccess(); | |
434 } | |
435 | |
436 void ParallelAuthenticator::OnPasswordChangeDetected() { | |
437 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
438 if (consumer_) | |
439 consumer_->OnPasswordChangeDetected(); | |
440 } | |
441 | |
442 void ParallelAuthenticator::OnLoginFailure(const LoginFailure& error) { | |
443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
444 | |
445 // OnLoginFailure will be called again with the same |error| | |
446 // after the cryptohome has been removed. | |
447 if (remove_user_data_on_failure_) { | |
448 delayed_login_failure_ = &error; | |
449 RemoveEncryptedData(); | |
450 return; | |
451 } | |
452 | |
453 // Send notification of failure | |
454 AuthenticationNotificationDetails details(false); | |
455 content::NotificationService::current()->Notify( | |
456 chrome::NOTIFICATION_LOGIN_AUTHENTICATION, | |
457 content::NotificationService::AllSources(), | |
458 content::Details<AuthenticationNotificationDetails>(&details)); | |
459 LOG(WARNING) << "Login failed: " << error.GetErrorString(); | |
460 if (consumer_) | |
461 consumer_->OnLoginFailure(error); | |
462 } | |
463 | |
464 void ParallelAuthenticator::RecoverEncryptedData( | |
465 const std::string& old_password) { | |
466 migrate_attempted_ = true; | |
467 current_state_->ResetCryptohomeStatus(); | |
468 SystemSaltGetter::Get()->GetSystemSalt( | |
469 base::Bind(&Migrate, | |
470 current_state_.get(), | |
471 scoped_refptr<ParallelAuthenticator>(this), | |
472 true, | |
473 old_password)); | |
474 } | |
475 | |
476 void ParallelAuthenticator::RemoveEncryptedData() { | |
477 remove_attempted_ = true; | |
478 current_state_->ResetCryptohomeStatus(); | |
479 BrowserThread::PostTask( | |
480 BrowserThread::UI, FROM_HERE, | |
481 base::Bind(&Remove, | |
482 current_state_.get(), | |
483 scoped_refptr<ParallelAuthenticator>(this))); | |
484 } | |
485 | |
486 void ParallelAuthenticator::ResyncEncryptedData() { | |
487 resync_attempted_ = true; | |
488 current_state_->ResetCryptohomeStatus(); | |
489 BrowserThread::PostTask( | |
490 BrowserThread::UI, FROM_HERE, | |
491 base::Bind(&Remove, | |
492 current_state_.get(), | |
493 scoped_refptr<ParallelAuthenticator>(this))); | |
494 } | |
495 | |
496 bool ParallelAuthenticator::VerifyOwner() { | |
497 if (owner_is_verified_) | |
498 return true; | |
499 // Check if policy data is fine and continue in safe mode if needed. | |
500 bool is_safe_mode = false; | |
501 CrosSettings::Get()->GetBoolean(kPolicyMissingMitigationMode, &is_safe_mode); | |
502 if (!is_safe_mode) { | |
503 // Now we can continue with the login and report mount success. | |
504 user_can_login_ = true; | |
505 owner_is_verified_ = true; | |
506 return true; | |
507 } | |
508 // Now we can continue reading the private key. | |
509 DeviceSettingsService::Get()->SetUsername( | |
510 current_state_->user_context.GetUserID()); | |
511 // This should trigger certificate loading, which is needed in order to | |
512 // correctly determine if the current user is the owner. | |
513 if (LoginState::IsInitialized()) { | |
514 LoginState::Get()->SetLoggedInState(LoginState::LOGGED_IN_SAFE_MODE, | |
515 LoginState::LOGGED_IN_USER_NONE); | |
516 } | |
517 DeviceSettingsService::Get()->IsCurrentUserOwnerAsync( | |
518 base::Bind(&ParallelAuthenticator::OnOwnershipChecked, this)); | |
519 return false; | |
520 } | |
521 | |
522 void ParallelAuthenticator::OnOwnershipChecked(bool is_owner) { | |
523 // Now we can check if this user is the owner. | |
524 user_can_login_ = is_owner; | |
525 owner_is_verified_ = true; | |
526 Resolve(); | |
527 } | |
528 | |
529 void ParallelAuthenticator::Resolve() { | |
530 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
531 int mount_flags = cryptohome::MOUNT_FLAGS_NONE; | |
532 ParallelAuthenticator::AuthState state = ResolveState(); | |
533 VLOG(1) << "Resolved state to: " << state; | |
534 switch (state) { | |
535 case CONTINUE: | |
536 case POSSIBLE_PW_CHANGE: | |
537 case NO_MOUNT: | |
538 // These are intermediate states; we need more info from a request that | |
539 // is still pending. | |
540 break; | |
541 case FAILED_MOUNT: | |
542 // In this case, whether login succeeded or not, we can't log | |
543 // the user in because their data is horked. So, override with | |
544 // the appropriate failure. | |
545 BrowserThread::PostTask( | |
546 BrowserThread::UI, FROM_HERE, | |
547 base::Bind(&ParallelAuthenticator::OnLoginFailure, this, | |
548 LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME))); | |
549 break; | |
550 case FAILED_REMOVE: | |
551 // In this case, we tried to remove the user's old cryptohome at her | |
552 // request, and the remove failed. | |
553 remove_user_data_on_failure_ = false; | |
554 BrowserThread::PostTask( | |
555 BrowserThread::UI, FROM_HERE, | |
556 base::Bind(&ParallelAuthenticator::OnLoginFailure, this, | |
557 LoginFailure(LoginFailure::DATA_REMOVAL_FAILED))); | |
558 break; | |
559 case FAILED_TMPFS: | |
560 // In this case, we tried to mount a tmpfs for guest and failed. | |
561 BrowserThread::PostTask( | |
562 BrowserThread::UI, FROM_HERE, | |
563 base::Bind(&ParallelAuthenticator::OnLoginFailure, this, | |
564 LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS))); | |
565 break; | |
566 case FAILED_TPM: | |
567 // In this case, we tried to create/mount cryptohome and failed | |
568 // because of the critical TPM error. | |
569 // Chrome will notify user and request reboot. | |
570 BrowserThread::PostTask( | |
571 BrowserThread::UI, FROM_HERE, | |
572 base::Bind(&ParallelAuthenticator::OnLoginFailure, this, | |
573 LoginFailure(LoginFailure::TPM_ERROR))); | |
574 break; | |
575 case FAILED_USERNAME_HASH: | |
576 // In this case, we failed the GetSanitizedUsername request to | |
577 // cryptohomed. This can happen for any login attempt. | |
578 BrowserThread::PostTask( | |
579 BrowserThread::UI, FROM_HERE, | |
580 base::Bind(&ParallelAuthenticator::OnLoginFailure, this, | |
581 LoginFailure(LoginFailure::USERNAME_HASH_FAILED))); | |
582 break; | |
583 case REMOVED_DATA_AFTER_FAILURE: | |
584 remove_user_data_on_failure_ = false; | |
585 BrowserThread::PostTask( | |
586 BrowserThread::UI, FROM_HERE, | |
587 base::Bind(&ParallelAuthenticator::OnLoginFailure, this, | |
588 *delayed_login_failure_)); | |
589 break; | |
590 case CREATE_NEW: | |
591 mount_flags |= cryptohome::CREATE_IF_MISSING; | |
592 case RECOVER_MOUNT: | |
593 current_state_->ResetCryptohomeStatus(); | |
594 SystemSaltGetter::Get()->GetSystemSalt( | |
595 base::Bind(&Mount, | |
596 current_state_.get(), | |
597 scoped_refptr<ParallelAuthenticator>(this), | |
598 mount_flags)); | |
599 break; | |
600 case NEED_OLD_PW: | |
601 BrowserThread::PostTask( | |
602 BrowserThread::UI, FROM_HERE, | |
603 base::Bind(&ParallelAuthenticator::OnPasswordChangeDetected, this)); | |
604 break; | |
605 case ONLINE_FAILED: | |
606 case NEED_NEW_PW: | |
607 case HAVE_NEW_PW: | |
608 NOTREACHED() << "Using obsolete ClientLogin code path."; | |
609 break; | |
610 case OFFLINE_LOGIN: | |
611 VLOG(2) << "Offline login"; | |
612 // Fall through. | |
613 case UNLOCK: | |
614 VLOG(2) << "Unlock"; | |
615 // Fall through. | |
616 case ONLINE_LOGIN: | |
617 VLOG(2) << "Online login"; | |
618 BrowserThread::PostTask( | |
619 BrowserThread::UI, FROM_HERE, | |
620 base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); | |
621 break; | |
622 case DEMO_LOGIN: | |
623 VLOG(2) << "Retail mode login"; | |
624 current_state_->user_context.SetIsUsingOAuth(false); | |
625 BrowserThread::PostTask( | |
626 BrowserThread::UI, FROM_HERE, | |
627 base::Bind(&ParallelAuthenticator::OnRetailModeLoginSuccess, this)); | |
628 break; | |
629 case GUEST_LOGIN: | |
630 BrowserThread::PostTask( | |
631 BrowserThread::UI, FROM_HERE, | |
632 base::Bind(&ParallelAuthenticator::OnOffTheRecordLoginSuccess, this)); | |
633 break; | |
634 case KIOSK_ACCOUNT_LOGIN: | |
635 case PUBLIC_ACCOUNT_LOGIN: | |
636 current_state_->user_context.SetIsUsingOAuth(false); | |
637 BrowserThread::PostTask( | |
638 BrowserThread::UI, FROM_HERE, | |
639 base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); | |
640 break; | |
641 case LOCALLY_MANAGED_USER_LOGIN: | |
642 current_state_->user_context.SetIsUsingOAuth(false); | |
643 BrowserThread::PostTask( | |
644 BrowserThread::UI, FROM_HERE, | |
645 base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); | |
646 break; | |
647 case LOGIN_FAILED: | |
648 current_state_->ResetCryptohomeStatus(); | |
649 BrowserThread::PostTask(BrowserThread::UI, | |
650 FROM_HERE, | |
651 base::Bind( | |
652 &ParallelAuthenticator::OnLoginFailure, | |
653 this, | |
654 current_state_->online_outcome())); | |
655 break; | |
656 case OWNER_REQUIRED: { | |
657 current_state_->ResetCryptohomeStatus(); | |
658 bool success = false; | |
659 DBusThreadManager::Get()->GetCryptohomeClient()->Unmount(&success); | |
660 if (!success) { | |
661 // Maybe we should reboot immediately here? | |
662 LOG(ERROR) << "Couldn't unmount users home!"; | |
663 } | |
664 BrowserThread::PostTask(BrowserThread::UI, | |
665 FROM_HERE, | |
666 base::Bind( | |
667 &ParallelAuthenticator::OnLoginFailure, | |
668 this, | |
669 LoginFailure(LoginFailure::OWNER_REQUIRED))); | |
670 break; | |
671 } | |
672 default: | |
673 NOTREACHED(); | |
674 break; | |
675 } | |
676 } | |
677 | |
678 // static. | |
679 std::string ParallelAuthenticator::HashPassword(const std::string& password, | |
680 const std::string& ascii_salt) { | |
681 // Update sha with ascii encoded salt, then update with ascii of password, | |
682 // then end. | |
683 // TODO(stevenjb/nkostylev): Handle empty system salt gracefully. | |
684 CHECK(!ascii_salt.empty()); | |
685 char passhash_buf[kPasswordHashLength]; | |
686 | |
687 // Hash salt and password | |
688 crypto::SHA256HashString(ascii_salt + password, | |
689 &passhash_buf, sizeof(passhash_buf)); | |
690 | |
691 // Only want the top half for 'weak' hashing so that the passphrase is not | |
692 // immediately exposed even if the output is reversed. | |
693 const int encoded_length = sizeof(passhash_buf) / 2; | |
694 | |
695 return StringToLowerASCII(base::HexEncode( | |
696 reinterpret_cast<const void*>(passhash_buf), encoded_length)); | |
697 } | |
698 | |
699 ParallelAuthenticator::~ParallelAuthenticator() {} | |
700 | |
701 ParallelAuthenticator::AuthState ParallelAuthenticator::ResolveState() { | |
702 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
703 // If we haven't mounted the user's home dir yet or | |
704 // haven't got sanitized username value, we can't be done. | |
705 // We never get past here if any of these two cryptohome ops is still pending. | |
706 // This is an important invariant. | |
707 if (!current_state_->cryptohome_complete() || | |
708 !current_state_->username_hash_obtained()) { | |
709 return CONTINUE; | |
710 } | |
711 | |
712 AuthState state = CONTINUE; | |
713 | |
714 if (current_state_->cryptohome_outcome() && | |
715 current_state_->username_hash_valid()) { | |
716 state = ResolveCryptohomeSuccessState(); | |
717 } else { | |
718 state = ResolveCryptohomeFailureState(); | |
719 } | |
720 | |
721 DCHECK(current_state_->cryptohome_complete()); // Ensure invariant holds. | |
722 migrate_attempted_ = false; | |
723 remove_attempted_ = false; | |
724 resync_attempted_ = false; | |
725 ephemeral_mount_attempted_ = false; | |
726 check_key_attempted_ = false; | |
727 | |
728 if (state != POSSIBLE_PW_CHANGE && | |
729 state != NO_MOUNT && | |
730 state != OFFLINE_LOGIN) | |
731 return state; | |
732 | |
733 if (current_state_->online_complete()) { | |
734 if (current_state_->online_outcome().reason() == LoginFailure::NONE) { | |
735 // Online attempt succeeded as well, so combine the results. | |
736 return ResolveOnlineSuccessState(state); | |
737 } | |
738 NOTREACHED() << "Using obsolete ClientLogin code path."; | |
739 } | |
740 // if online isn't complete yet, just return the offline result. | |
741 return state; | |
742 } | |
743 | |
744 ParallelAuthenticator::AuthState | |
745 ParallelAuthenticator::ResolveCryptohomeFailureState() { | |
746 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
747 if (remove_attempted_ || resync_attempted_) | |
748 return FAILED_REMOVE; | |
749 if (ephemeral_mount_attempted_) | |
750 return FAILED_TMPFS; | |
751 if (migrate_attempted_) | |
752 return NEED_OLD_PW; | |
753 if (check_key_attempted_) | |
754 return LOGIN_FAILED; | |
755 | |
756 if (current_state_->cryptohome_code() == | |
757 cryptohome::MOUNT_ERROR_TPM_NEEDS_REBOOT) { | |
758 // Critical TPM error detected, reboot needed. | |
759 return FAILED_TPM; | |
760 } | |
761 | |
762 // Return intermediate states in the following case: | |
763 // when there is an online result to use; | |
764 // This is the case after user finishes Gaia login; | |
765 if (current_state_->online_complete()) { | |
766 if (current_state_->cryptohome_code() == | |
767 cryptohome::MOUNT_ERROR_KEY_FAILURE) { | |
768 // If we tried a mount but they used the wrong key, we may need to | |
769 // ask the user for her old password. We'll only know once we've | |
770 // done the online check. | |
771 return POSSIBLE_PW_CHANGE; | |
772 } | |
773 if (current_state_->cryptohome_code() == | |
774 cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST) { | |
775 // If we tried a mount but the user did not exist, then we should wait | |
776 // for online login to succeed and try again with the "create" flag set. | |
777 return NO_MOUNT; | |
778 } | |
779 } | |
780 | |
781 if (!current_state_->username_hash_valid()) | |
782 return FAILED_USERNAME_HASH; | |
783 | |
784 return FAILED_MOUNT; | |
785 } | |
786 | |
787 ParallelAuthenticator::AuthState | |
788 ParallelAuthenticator::ResolveCryptohomeSuccessState() { | |
789 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
790 if (resync_attempted_) | |
791 return CREATE_NEW; | |
792 if (remove_attempted_) | |
793 return REMOVED_DATA_AFTER_FAILURE; | |
794 if (migrate_attempted_) | |
795 return RECOVER_MOUNT; | |
796 if (check_key_attempted_) | |
797 return UNLOCK; | |
798 | |
799 if (current_state_->user_type == User::USER_TYPE_GUEST) | |
800 return GUEST_LOGIN; | |
801 if (current_state_->user_type == User::USER_TYPE_RETAIL_MODE) | |
802 return DEMO_LOGIN; | |
803 if (current_state_->user_type == User::USER_TYPE_PUBLIC_ACCOUNT) | |
804 return PUBLIC_ACCOUNT_LOGIN; | |
805 if (current_state_->user_type == User::USER_TYPE_KIOSK_APP) | |
806 return KIOSK_ACCOUNT_LOGIN; | |
807 if (current_state_->user_type == User::USER_TYPE_LOCALLY_MANAGED) | |
808 return LOCALLY_MANAGED_USER_LOGIN; | |
809 | |
810 if (!VerifyOwner()) | |
811 return CONTINUE; | |
812 return user_can_login_ ? OFFLINE_LOGIN : OWNER_REQUIRED; | |
813 } | |
814 | |
815 ParallelAuthenticator::AuthState | |
816 ParallelAuthenticator::ResolveOnlineSuccessState( | |
817 ParallelAuthenticator::AuthState offline_state) { | |
818 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
819 switch (offline_state) { | |
820 case POSSIBLE_PW_CHANGE: | |
821 return NEED_OLD_PW; | |
822 case NO_MOUNT: | |
823 return CREATE_NEW; | |
824 case OFFLINE_LOGIN: | |
825 return ONLINE_LOGIN; | |
826 default: | |
827 NOTREACHED(); | |
828 return offline_state; | |
829 } | |
830 } | |
831 | |
832 void ParallelAuthenticator::ResolveLoginCompletionStatus() { | |
833 // Shortcut online state resolution process. | |
834 current_state_->RecordOnlineLoginStatus(LoginFailure::LoginFailureNone()); | |
835 Resolve(); | |
836 } | |
837 | |
838 void ParallelAuthenticator::SetOwnerState(bool owner_check_finished, | |
839 bool check_result) { | |
840 owner_is_verified_ = owner_check_finished; | |
841 user_can_login_ = check_result; | |
842 } | |
843 | |
844 } // namespace chromeos | |
OLD | NEW |