Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chromeos / login / auth / cryptohome_authenticator.cc
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.
4
5 #include "chromeos/login/auth/cryptohome_authenticator.h"
6
7 #include <vector>
8
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/files/file_path.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "chromeos/cryptohome/async_method_caller.h"
15 #include "chromeos/cryptohome/cryptohome_parameters.h"
16 #include "chromeos/cryptohome/homedir_methods.h"
17 #include "chromeos/cryptohome/system_salt_getter.h"
18 #include "chromeos/dbus/cryptohome_client.h"
19 #include "chromeos/dbus/dbus_thread_manager.h"
20 #include "chromeos/login/auth/auth_status_consumer.h"
21 #include "chromeos/login/auth/key.h"
22 #include "chromeos/login/auth/user_context.h"
23 #include "chromeos/login/login_state.h"
24 #include "chromeos/login/user_names.h"
25 #include "chromeos/login_event_recorder.h"
26 #include "components/user_manager/user_type.h"
27 #include "third_party/cros_system_api/dbus/service_constants.h"
28
29 namespace chromeos {
30
31 namespace {
32
33 // The label used for the key derived from the user's GAIA credentials.
34 const char kCryptohomeGAIAKeyLabel[] = "gaia";
35
36 // The name under which the type of key generated from the user's GAIA
37 // credentials is stored.
38 const char kKeyProviderDataTypeName[] = "type";
39
40 // The name under which the salt used to generate a key from the user's GAIA
41 // credentials is stored.
42 const char kKeyProviderDataSaltName[] = "salt";
43
44 // Hashes |key| with |system_salt| if it its type is KEY_TYPE_PASSWORD_PLAIN.
45 // Returns the keys unmodified otherwise.
46 scoped_ptr<Key> TransformKeyIfNeeded(const Key& key,
47                                      const std::string& system_salt) {
48   scoped_ptr<Key> result(new Key(key));
49   if (result->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN)
50     result->Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
51
52   return result.Pass();
53 }
54
55 // Records status and calls resolver->Resolve().
56 void TriggerResolve(AuthAttemptState* attempt,
57                     scoped_refptr<CryptohomeAuthenticator> resolver,
58                     bool success,
59                     cryptohome::MountError return_code) {
60   attempt->RecordCryptohomeStatus(success, return_code);
61   resolver->Resolve();
62 }
63
64 // Records get hash status and calls resolver->Resolve().
65 void TriggerResolveHash(AuthAttemptState* attempt,
66                         scoped_refptr<CryptohomeAuthenticator> resolver,
67                         bool success,
68                         const std::string& username_hash) {
69   if (success)
70     attempt->RecordUsernameHash(username_hash);
71   else
72     attempt->RecordUsernameHashFailed();
73   resolver->Resolve();
74 }
75
76 // Calls TriggerResolve while adding login time marker.
77 void TriggerResolveWithLoginTimeMarker(
78     const std::string& marker_name,
79     AuthAttemptState* attempt,
80     scoped_refptr<CryptohomeAuthenticator> resolver,
81     bool success,
82     cryptohome::MountError return_code) {
83   chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(marker_name, false);
84   TriggerResolve(attempt, resolver, success, return_code);
85 }
86
87 // Records an error in accessing the user's cryptohome with the given key and
88 // calls resolver->Resolve() after adding a login time marker.
89 void RecordKeyErrorAndResolve(AuthAttemptState* attempt,
90                               scoped_refptr<CryptohomeAuthenticator> resolver) {
91   chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End",
92                                                           false);
93   attempt->RecordCryptohomeStatus(false /* success */,
94                                   cryptohome::MOUNT_ERROR_KEY_FAILURE);
95   resolver->Resolve();
96 }
97
98 // Callback invoked when cryptohome's MountEx() method has finished.
99 void OnMount(AuthAttemptState* attempt,
100              scoped_refptr<CryptohomeAuthenticator> resolver,
101              bool success,
102              cryptohome::MountError return_code,
103              const std::string& mount_hash) {
104   chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End",
105                                                           false);
106   attempt->RecordCryptohomeStatus(success, return_code);
107   if (success)
108     attempt->RecordUsernameHash(mount_hash);
109   else
110     attempt->RecordUsernameHashFailed();
111   resolver->Resolve();
112 }
113
114 // Calls cryptohome's MountEx() method. The key in |attempt->user_context| must
115 // not be a plain text password. If the user provided a plain text password,
116 // that password must be transformed to another key type (by salted hashing)
117 // before calling this method.
118 void DoMount(AuthAttemptState* attempt,
119              scoped_refptr<CryptohomeAuthenticator> resolver,
120              bool ephemeral,
121              bool create_if_nonexistent) {
122   const Key* key = attempt->user_context.GetKey();
123   // If the |key| is a plain text password, crash rather than attempting to
124   // mount the cryptohome with a plain text password.
125   CHECK_NE(Key::KEY_TYPE_PASSWORD_PLAIN, key->GetKeyType());
126
127   // Set state that username_hash is requested here so that test implementation
128   // that returns directly would not generate 2 OnLoginSucces() calls.
129   attempt->UsernameHashRequested();
130
131   // Set the authentication's key label to an empty string, which is a wildcard
132   // allowing any key to match. This is necessary because cryptohomes created by
133   // Chrome OS M38 and older will have a legacy key with no label while those
134   // created by Chrome OS M39 and newer will have a key with the label
135   // kCryptohomeGAIAKeyLabel.
136   const cryptohome::KeyDefinition auth_key(key->GetSecret(),
137                                            std::string(),
138                                            cryptohome::PRIV_DEFAULT);
139   cryptohome::MountParameters mount(ephemeral);
140   if (create_if_nonexistent) {
141     mount.create_keys.push_back(cryptohome::KeyDefinition(
142         key->GetSecret(),
143         kCryptohomeGAIAKeyLabel,
144         cryptohome::PRIV_DEFAULT));
145   }
146
147   cryptohome::HomedirMethods::GetInstance()->MountEx(
148       cryptohome::Identification(attempt->user_context.GetUserID()),
149       cryptohome::Authorization(auth_key),
150       mount,
151       base::Bind(&OnMount, attempt, resolver));
152 }
153
154 // Callback invoked when the system salt has been retrieved. Transforms the key
155 // in |attempt->user_context| using Chrome's default hashing algorithm and the
156 // system salt, then calls MountEx().
157 void OnGetSystemSalt(AuthAttemptState* attempt,
158                     scoped_refptr<CryptohomeAuthenticator> resolver,
159                     bool ephemeral,
160                     bool create_if_nonexistent,
161                     const std::string& system_salt) {
162   DCHECK_EQ(Key::KEY_TYPE_PASSWORD_PLAIN,
163             attempt->user_context.GetKey()->GetKeyType());
164
165   attempt->user_context.GetKey()->Transform(
166       Key::KEY_TYPE_SALTED_SHA256_TOP_HALF,
167       system_salt);
168
169   DoMount(attempt, resolver, ephemeral, create_if_nonexistent);
170 }
171
172 // Callback invoked when cryptohome's GetKeyDataEx() method has finished.
173 // * If GetKeyDataEx() returned metadata indicating the hashing algorithm and
174 //   salt that were used to generate the key for this user's cryptohome,
175 //   transforms the key in |attempt->user_context| with the same parameters.
176 // * Otherwise, starts the retrieval of the system salt so that the key in
177 //   |attempt->user_context| can be transformed with Chrome's default hashing
178 //   algorithm and the system salt.
179 // The resulting key is then passed to cryptohome's MountEx().
180 void OnGetKeyDataEx(
181     AuthAttemptState* attempt,
182     scoped_refptr<CryptohomeAuthenticator> resolver,
183     bool ephemeral,
184     bool create_if_nonexistent,
185     bool success,
186     cryptohome::MountError return_code,
187     const std::vector<cryptohome::KeyDefinition>& key_definitions) {
188   if (success) {
189     if (key_definitions.size() == 1) {
190       const cryptohome::KeyDefinition& key_definition = key_definitions.front();
191       DCHECK_EQ(kCryptohomeGAIAKeyLabel, key_definition.label);
192
193       // Extract the key type and salt from |key_definition|, if present.
194       scoped_ptr<int64> type;
195       scoped_ptr<std::string> salt;
196       for (std::vector<cryptohome::KeyDefinition::ProviderData>::
197                const_iterator it = key_definition.provider_data.begin();
198            it != key_definition.provider_data.end(); ++it) {
199         if (it->name == kKeyProviderDataTypeName) {
200           if (it->number)
201             type.reset(new int64(*it->number));
202           else
203             NOTREACHED();
204         } else if (it->name == kKeyProviderDataSaltName) {
205           if (it->bytes)
206             salt.reset(new std::string(*it->bytes));
207           else
208             NOTREACHED();
209         }
210       }
211
212       if (type) {
213         if (*type < 0 || *type >= Key::KEY_TYPE_COUNT) {
214           LOG(ERROR) << "Invalid key type: " << *type;
215           RecordKeyErrorAndResolve(attempt, resolver);
216           return;
217         }
218
219         if (!salt) {
220           LOG(ERROR) << "Missing salt.";
221           RecordKeyErrorAndResolve(attempt, resolver);
222           return;
223         }
224
225         attempt->user_context.GetKey()->Transform(
226             static_cast<Key::KeyType>(*type),
227             *salt);
228         DoMount(attempt, resolver, ephemeral, create_if_nonexistent);
229         return;
230       }
231     } else {
232       LOG(ERROR) << "GetKeyDataEx() returned " << key_definitions.size()
233                  << " entries.";
234     }
235   }
236
237   SystemSaltGetter::Get()->GetSystemSalt(base::Bind(&OnGetSystemSalt,
238                                                     attempt,
239                                                     resolver,
240                                                     ephemeral,
241                                                     create_if_nonexistent));
242 }
243
244 // Starts the process that will mount a user's cryptohome.
245 // * If the key in |attempt->user_context| is not a plain text password,
246 //   cryptohome's MountEx() method is called directly with the key.
247 // * Otherwise, the key must be transformed (by salted hashing) before being
248 //   passed to MountEx(). In that case, cryptohome's GetKeyDataEx() method is
249 //   called to retrieve metadata indicating the hashing algorithm and salt that
250 //   were used to generate the key for this user's cryptohome and the key is
251 //   transformed accordingly before calling MountEx().
252 void StartMount(AuthAttemptState* attempt,
253                 scoped_refptr<CryptohomeAuthenticator> resolver,
254                 bool ephemeral,
255                 bool create_if_nonexistent) {
256   chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
257       "CryptohomeMount-Start", false);
258
259   if (attempt->user_context.GetKey()->GetKeyType() !=
260           Key::KEY_TYPE_PASSWORD_PLAIN) {
261     DoMount(attempt, resolver, ephemeral, create_if_nonexistent);
262     return;
263   }
264
265   cryptohome::HomedirMethods::GetInstance()->GetKeyDataEx(
266       cryptohome::Identification(attempt->user_context.GetUserID()),
267       kCryptohomeGAIAKeyLabel,
268       base::Bind(&OnGetKeyDataEx,
269                  attempt,
270                  resolver,
271                  ephemeral,
272                  create_if_nonexistent));
273 }
274
275 // Calls cryptohome's mount method for guest and also get the user hash from
276 // cryptohome.
277 void MountGuestAndGetHash(AuthAttemptState* attempt,
278                           scoped_refptr<CryptohomeAuthenticator> resolver) {
279   attempt->UsernameHashRequested();
280   cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountGuest(
281       base::Bind(&TriggerResolveWithLoginTimeMarker,
282                  "CryptohomeMount-End",
283                  attempt,
284                  resolver));
285   cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
286       attempt->user_context.GetUserID(),
287       base::Bind(&TriggerResolveHash, attempt, resolver));
288 }
289
290 // Calls cryptohome's MountPublic method
291 void MountPublic(AuthAttemptState* attempt,
292                  scoped_refptr<CryptohomeAuthenticator> resolver,
293                  int flags) {
294   cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic(
295       attempt->user_context.GetUserID(),
296       flags,
297       base::Bind(&TriggerResolveWithLoginTimeMarker,
298                  "CryptohomeMountPublic-End",
299                  attempt,
300                  resolver));
301   cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
302       attempt->user_context.GetUserID(),
303       base::Bind(&TriggerResolveHash, attempt, resolver));
304 }
305
306 // Calls cryptohome's key migration method.
307 void Migrate(AuthAttemptState* attempt,
308              scoped_refptr<CryptohomeAuthenticator> resolver,
309              bool passing_old_hash,
310              const std::string& old_password,
311              const std::string& system_salt) {
312   chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
313       "CryptohomeMigrate-Start", false);
314   cryptohome::AsyncMethodCaller* caller =
315       cryptohome::AsyncMethodCaller::GetInstance();
316
317   // TODO(bartfab): Retrieve the hashing algorithm and salt to use for |old_key|
318   // from cryptohomed.
319   scoped_ptr<Key> old_key =
320       TransformKeyIfNeeded(Key(old_password), system_salt);
321   scoped_ptr<Key> new_key =
322       TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt);
323   if (passing_old_hash) {
324     caller->AsyncMigrateKey(attempt->user_context.GetUserID(),
325                             old_key->GetSecret(),
326                             new_key->GetSecret(),
327                             base::Bind(&TriggerResolveWithLoginTimeMarker,
328                                        "CryptohomeMount-End",
329                                        attempt,
330                                        resolver));
331   } else {
332     caller->AsyncMigrateKey(attempt->user_context.GetUserID(),
333                             new_key->GetSecret(),
334                             old_key->GetSecret(),
335                             base::Bind(&TriggerResolveWithLoginTimeMarker,
336                                        "CryptohomeMount-End",
337                                        attempt,
338                                        resolver));
339   }
340 }
341
342 // Calls cryptohome's remove method.
343 void Remove(AuthAttemptState* attempt,
344             scoped_refptr<CryptohomeAuthenticator> resolver) {
345   chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
346       "CryptohomeRemove-Start", false);
347   cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
348       attempt->user_context.GetUserID(),
349       base::Bind(&TriggerResolveWithLoginTimeMarker,
350                  "CryptohomeRemove-End",
351                  attempt,
352                  resolver));
353 }
354
355 // Calls cryptohome's key check method.
356 void CheckKey(AuthAttemptState* attempt,
357               scoped_refptr<CryptohomeAuthenticator> resolver,
358               const std::string& system_salt) {
359   scoped_ptr<Key> key =
360       TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt);
361   cryptohome::AsyncMethodCaller::GetInstance()->AsyncCheckKey(
362       attempt->user_context.GetUserID(),
363       key->GetSecret(),
364       base::Bind(&TriggerResolve, attempt, resolver));
365 }
366
367 }  // namespace
368
369 CryptohomeAuthenticator::CryptohomeAuthenticator(
370     scoped_refptr<base::TaskRunner> task_runner,
371     AuthStatusConsumer* consumer)
372     : Authenticator(consumer),
373       task_runner_(task_runner),
374       migrate_attempted_(false),
375       remove_attempted_(false),
376       resync_attempted_(false),
377       ephemeral_mount_attempted_(false),
378       check_key_attempted_(false),
379       already_reported_success_(false),
380       owner_is_verified_(false),
381       user_can_login_(false),
382       remove_user_data_on_failure_(false),
383       delayed_login_failure_(NULL) {
384 }
385
386 void CryptohomeAuthenticator::AuthenticateToLogin(
387     Profile* profile,
388     const UserContext& user_context) {
389   authentication_profile_ = profile;
390   current_state_.reset(new AuthAttemptState(user_context,
391                                             user_manager::USER_TYPE_REGULAR,
392                                             false,  // unlock
393                                             false,  // online_complete
394                                             !IsKnownUser(user_context)));
395   // Reset the verified flag.
396   owner_is_verified_ = false;
397
398   StartMount(current_state_.get(),
399              scoped_refptr<CryptohomeAuthenticator>(this),
400              false /* ephemeral */,
401              false /* create_if_nonexistent */);
402 }
403
404 void CryptohomeAuthenticator::CompleteLogin(Profile* profile,
405                                             const UserContext& user_context) {
406   authentication_profile_ = profile;
407   current_state_.reset(new AuthAttemptState(user_context,
408                                             user_manager::USER_TYPE_REGULAR,
409                                             true,   // unlock
410                                             false,  // online_complete
411                                             !IsKnownUser(user_context)));
412
413   // Reset the verified flag.
414   owner_is_verified_ = false;
415
416   StartMount(current_state_.get(),
417              scoped_refptr<CryptohomeAuthenticator>(this),
418              false /* ephemeral */,
419              false /* create_if_nonexistent */);
420
421   // For login completion from extension, we just need to resolve the current
422   // auth attempt state, the rest of OAuth related tasks will be done in
423   // parallel.
424   task_runner_->PostTask(
425       FROM_HERE,
426       base::Bind(&CryptohomeAuthenticator::ResolveLoginCompletionStatus, this));
427 }
428
429 void CryptohomeAuthenticator::AuthenticateToUnlock(
430     const UserContext& user_context) {
431   current_state_.reset(new AuthAttemptState(user_context,
432                                             user_manager::USER_TYPE_REGULAR,
433                                             true,     // unlock
434                                             true,     // online_complete
435                                             false));  // user_is_new
436   remove_user_data_on_failure_ = false;
437   check_key_attempted_ = true;
438   SystemSaltGetter::Get()->GetSystemSalt(
439       base::Bind(&CheckKey,
440                  current_state_.get(),
441                  scoped_refptr<CryptohomeAuthenticator>(this)));
442 }
443
444 void CryptohomeAuthenticator::LoginAsSupervisedUser(
445     const UserContext& user_context) {
446   DCHECK(task_runner_->RunsTasksOnCurrentThread());
447   // TODO(nkostylev): Pass proper value for |user_is_new| or remove (not used).
448   current_state_.reset(new AuthAttemptState(user_context,
449                                             user_manager::USER_TYPE_SUPERVISED,
450                                             false,    // unlock
451                                             false,    // online_complete
452                                             false));  // user_is_new
453   remove_user_data_on_failure_ = false;
454   StartMount(current_state_.get(),
455              scoped_refptr<CryptohomeAuthenticator>(this),
456              false /* ephemeral */,
457              false /* create_if_nonexistent */);
458 }
459
460 void CryptohomeAuthenticator::LoginRetailMode() {
461   DCHECK(task_runner_->RunsTasksOnCurrentThread());
462   // Note: |kRetailModeUserEMail| is used in other places to identify a retail
463   // mode session.
464   current_state_.reset(
465       new AuthAttemptState(UserContext(chromeos::login::kRetailModeUserName),
466                            user_manager::USER_TYPE_RETAIL_MODE,
467                            false,    // unlock
468                            false,    // online_complete
469                            false));  // user_is_new
470   remove_user_data_on_failure_ = false;
471   ephemeral_mount_attempted_ = true;
472   MountGuestAndGetHash(current_state_.get(),
473                        scoped_refptr<CryptohomeAuthenticator>(this));
474 }
475
476 void CryptohomeAuthenticator::LoginOffTheRecord() {
477   DCHECK(task_runner_->RunsTasksOnCurrentThread());
478   current_state_.reset(
479       new AuthAttemptState(UserContext(chromeos::login::kGuestUserName),
480                            user_manager::USER_TYPE_GUEST,
481                            false,    // unlock
482                            false,    // online_complete
483                            false));  // user_is_new
484   remove_user_data_on_failure_ = false;
485   ephemeral_mount_attempted_ = true;
486   MountGuestAndGetHash(current_state_.get(),
487                        scoped_refptr<CryptohomeAuthenticator>(this));
488 }
489
490 void CryptohomeAuthenticator::LoginAsPublicSession(
491     const UserContext& user_context) {
492   DCHECK(task_runner_->RunsTasksOnCurrentThread());
493   current_state_.reset(
494       new AuthAttemptState(user_context,
495                            user_manager::USER_TYPE_PUBLIC_ACCOUNT,
496                            false,    // unlock
497                            false,    // online_complete
498                            false));  // user_is_new
499   remove_user_data_on_failure_ = false;
500   ephemeral_mount_attempted_ = true;
501   StartMount(current_state_.get(),
502              scoped_refptr<CryptohomeAuthenticator>(this),
503              true /* ephemeral */,
504              true /* create_if_nonexistent */);
505 }
506
507 void CryptohomeAuthenticator::LoginAsKioskAccount(
508     const std::string& app_user_id,
509     bool use_guest_mount) {
510   DCHECK(task_runner_->RunsTasksOnCurrentThread());
511
512   const std::string user_id =
513       use_guest_mount ? chromeos::login::kGuestUserName : app_user_id;
514   current_state_.reset(new AuthAttemptState(UserContext(user_id),
515                                             user_manager::USER_TYPE_KIOSK_APP,
516                                             false,    // unlock
517                                             false,    // online_complete
518                                             false));  // user_is_new
519
520   remove_user_data_on_failure_ = true;
521   if (!use_guest_mount) {
522     MountPublic(current_state_.get(),
523                 scoped_refptr<CryptohomeAuthenticator>(this),
524                 cryptohome::CREATE_IF_MISSING);
525   } else {
526     ephemeral_mount_attempted_ = true;
527     MountGuestAndGetHash(current_state_.get(),
528                          scoped_refptr<CryptohomeAuthenticator>(this));
529   }
530 }
531
532 void CryptohomeAuthenticator::OnRetailModeAuthSuccess() {
533   DCHECK(task_runner_->RunsTasksOnCurrentThread());
534   VLOG(1) << "Retail mode login success";
535   chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess();
536   if (consumer_)
537     consumer_->OnRetailModeAuthSuccess(current_state_->user_context);
538 }
539
540 void CryptohomeAuthenticator::OnAuthSuccess() {
541   DCHECK(task_runner_->RunsTasksOnCurrentThread());
542   VLOG(1) << "Login success";
543   // Send notification of success
544   chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess();
545   {
546     base::AutoLock for_this_block(success_lock_);
547     already_reported_success_ = true;
548   }
549   if (consumer_)
550     consumer_->OnAuthSuccess(current_state_->user_context);
551 }
552
553 void CryptohomeAuthenticator::OnOffTheRecordAuthSuccess() {
554   DCHECK(task_runner_->RunsTasksOnCurrentThread());
555   chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess();
556   if (consumer_)
557     consumer_->OnOffTheRecordAuthSuccess();
558 }
559
560 void CryptohomeAuthenticator::OnPasswordChangeDetected() {
561   DCHECK(task_runner_->RunsTasksOnCurrentThread());
562   if (consumer_)
563     consumer_->OnPasswordChangeDetected();
564 }
565
566 void CryptohomeAuthenticator::OnAuthFailure(const AuthFailure& error) {
567   DCHECK(task_runner_->RunsTasksOnCurrentThread());
568
569   // OnAuthFailure will be called again with the same |error|
570   // after the cryptohome has been removed.
571   if (remove_user_data_on_failure_) {
572     delayed_login_failure_ = &error;
573     RemoveEncryptedData();
574     return;
575   }
576   chromeos::LoginEventRecorder::Get()->RecordAuthenticationFailure();
577   LOG(WARNING) << "Login failed: " << error.GetErrorString();
578   if (consumer_)
579     consumer_->OnAuthFailure(error);
580 }
581
582 void CryptohomeAuthenticator::RecoverEncryptedData(
583     const std::string& old_password) {
584   migrate_attempted_ = true;
585   current_state_->ResetCryptohomeStatus();
586   SystemSaltGetter::Get()->GetSystemSalt(
587       base::Bind(&Migrate,
588                  current_state_.get(),
589                  scoped_refptr<CryptohomeAuthenticator>(this),
590                  true,
591                  old_password));
592 }
593
594 void CryptohomeAuthenticator::RemoveEncryptedData() {
595   remove_attempted_ = true;
596   current_state_->ResetCryptohomeStatus();
597   task_runner_->PostTask(
598       FROM_HERE,
599       base::Bind(&Remove,
600                  current_state_.get(),
601                  scoped_refptr<CryptohomeAuthenticator>(this)));
602 }
603
604 void CryptohomeAuthenticator::ResyncEncryptedData() {
605   resync_attempted_ = true;
606   current_state_->ResetCryptohomeStatus();
607   task_runner_->PostTask(
608       FROM_HERE,
609       base::Bind(&Remove,
610                  current_state_.get(),
611                  scoped_refptr<CryptohomeAuthenticator>(this)));
612 }
613
614 bool CryptohomeAuthenticator::VerifyOwner() {
615   if (owner_is_verified_)
616     return true;
617   // Check if policy data is fine and continue in safe mode if needed.
618   if (!IsSafeMode()) {
619     // Now we can continue with the login and report mount success.
620     user_can_login_ = true;
621     owner_is_verified_ = true;
622     return true;
623   }
624
625   CheckSafeModeOwnership(
626       current_state_->user_context,
627       base::Bind(&CryptohomeAuthenticator::OnOwnershipChecked, this));
628   return false;
629 }
630
631 void CryptohomeAuthenticator::OnOwnershipChecked(bool is_owner) {
632   // Now we can check if this user is the owner.
633   user_can_login_ = is_owner;
634   owner_is_verified_ = true;
635   Resolve();
636 }
637
638 void CryptohomeAuthenticator::Resolve() {
639   DCHECK(task_runner_->RunsTasksOnCurrentThread());
640   bool create_if_nonexistent = false;
641   CryptohomeAuthenticator::AuthState state = ResolveState();
642   VLOG(1) << "Resolved state to: " << state;
643   switch (state) {
644     case CONTINUE:
645     case POSSIBLE_PW_CHANGE:
646     case NO_MOUNT:
647       // These are intermediate states; we need more info from a request that
648       // is still pending.
649       break;
650     case FAILED_MOUNT:
651       // In this case, whether login succeeded or not, we can't log
652       // the user in because their data is horked.  So, override with
653       // the appropriate failure.
654       task_runner_->PostTask(
655           FROM_HERE,
656           base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
657                      this,
658                      AuthFailure(AuthFailure::COULD_NOT_MOUNT_CRYPTOHOME)));
659       break;
660     case FAILED_REMOVE:
661       // In this case, we tried to remove the user's old cryptohome at her
662       // request, and the remove failed.
663       remove_user_data_on_failure_ = false;
664       task_runner_->PostTask(
665           FROM_HERE,
666           base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
667                      this,
668                      AuthFailure(AuthFailure::DATA_REMOVAL_FAILED)));
669       break;
670     case FAILED_TMPFS:
671       // In this case, we tried to mount a tmpfs for guest and failed.
672       task_runner_->PostTask(
673           FROM_HERE,
674           base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
675                      this,
676                      AuthFailure(AuthFailure::COULD_NOT_MOUNT_TMPFS)));
677       break;
678     case FAILED_TPM:
679       // In this case, we tried to create/mount cryptohome and failed
680       // because of the critical TPM error.
681       // Chrome will notify user and request reboot.
682       task_runner_->PostTask(FROM_HERE,
683                              base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
684                                         this,
685                                         AuthFailure(AuthFailure::TPM_ERROR)));
686       break;
687     case FAILED_USERNAME_HASH:
688       // In this case, we failed the GetSanitizedUsername request to
689       // cryptohomed. This can happen for any login attempt.
690       task_runner_->PostTask(
691           FROM_HERE,
692           base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
693                      this,
694                      AuthFailure(AuthFailure::USERNAME_HASH_FAILED)));
695       break;
696     case REMOVED_DATA_AFTER_FAILURE:
697       remove_user_data_on_failure_ = false;
698       task_runner_->PostTask(FROM_HERE,
699                              base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
700                                         this,
701                                         *delayed_login_failure_));
702       break;
703     case CREATE_NEW:
704       create_if_nonexistent = true;
705     case RECOVER_MOUNT:
706       current_state_->ResetCryptohomeStatus();
707       StartMount(current_state_.get(),
708                  scoped_refptr<CryptohomeAuthenticator>(this),
709                  false /*ephemeral*/,
710                  create_if_nonexistent);
711       break;
712     case NEED_OLD_PW:
713       task_runner_->PostTask(
714           FROM_HERE,
715           base::Bind(&CryptohomeAuthenticator::OnPasswordChangeDetected, this));
716       break;
717     case ONLINE_FAILED:
718     case NEED_NEW_PW:
719     case HAVE_NEW_PW:
720       NOTREACHED() << "Using obsolete ClientLogin code path.";
721       break;
722     case OFFLINE_LOGIN:
723       VLOG(2) << "Offline login";
724     // Fall through.
725     case UNLOCK:
726       VLOG(2) << "Unlock";
727     // Fall through.
728     case ONLINE_LOGIN:
729       VLOG(2) << "Online login";
730       task_runner_->PostTask(
731           FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthSuccess, this));
732       break;
733     case DEMO_LOGIN:
734       VLOG(2) << "Retail mode login";
735       current_state_->user_context.SetIsUsingOAuth(false);
736       task_runner_->PostTask(
737           FROM_HERE,
738           base::Bind(&CryptohomeAuthenticator::OnRetailModeAuthSuccess, this));
739       break;
740     case GUEST_LOGIN:
741       task_runner_->PostTask(
742           FROM_HERE,
743           base::Bind(&CryptohomeAuthenticator::OnOffTheRecordAuthSuccess,
744                      this));
745       break;
746     case KIOSK_ACCOUNT_LOGIN:
747     case PUBLIC_ACCOUNT_LOGIN:
748       current_state_->user_context.SetIsUsingOAuth(false);
749       task_runner_->PostTask(
750           FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthSuccess, this));
751       break;
752     case SUPERVISED_USER_LOGIN:
753       current_state_->user_context.SetIsUsingOAuth(false);
754       task_runner_->PostTask(
755           FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthSuccess, this));
756       break;
757     case LOGIN_FAILED:
758       current_state_->ResetCryptohomeStatus();
759       task_runner_->PostTask(FROM_HERE,
760                              base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
761                                         this,
762                                         current_state_->online_outcome()));
763       break;
764     case OWNER_REQUIRED: {
765       current_state_->ResetCryptohomeStatus();
766       bool success = false;
767       DBusThreadManager::Get()->GetCryptohomeClient()->Unmount(&success);
768       if (!success) {
769         // Maybe we should reboot immediately here?
770         LOG(ERROR) << "Couldn't unmount users home!";
771       }
772       task_runner_->PostTask(
773           FROM_HERE,
774           base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
775                      this,
776                      AuthFailure(AuthFailure::OWNER_REQUIRED)));
777       break;
778     }
779     default:
780       NOTREACHED();
781       break;
782   }
783 }
784
785 CryptohomeAuthenticator::~CryptohomeAuthenticator() {
786 }
787
788 CryptohomeAuthenticator::AuthState CryptohomeAuthenticator::ResolveState() {
789   DCHECK(task_runner_->RunsTasksOnCurrentThread());
790   // If we haven't mounted the user's home dir yet or
791   // haven't got sanitized username value, we can't be done.
792   // We never get past here if any of these two cryptohome ops is still pending.
793   // This is an important invariant.
794   if (!current_state_->cryptohome_complete() ||
795       !current_state_->username_hash_obtained()) {
796     return CONTINUE;
797   }
798
799   AuthState state = CONTINUE;
800
801   if (current_state_->cryptohome_outcome() &&
802       current_state_->username_hash_valid()) {
803     state = ResolveCryptohomeSuccessState();
804   } else {
805     state = ResolveCryptohomeFailureState();
806   }
807
808   DCHECK(current_state_->cryptohome_complete());  // Ensure invariant holds.
809   migrate_attempted_ = false;
810   remove_attempted_ = false;
811   resync_attempted_ = false;
812   ephemeral_mount_attempted_ = false;
813   check_key_attempted_ = false;
814
815   if (state != POSSIBLE_PW_CHANGE && state != NO_MOUNT &&
816       state != OFFLINE_LOGIN)
817     return state;
818
819   if (current_state_->online_complete()) {
820     if (current_state_->online_outcome().reason() == AuthFailure::NONE) {
821       // Online attempt succeeded as well, so combine the results.
822       return ResolveOnlineSuccessState(state);
823     }
824     NOTREACHED() << "Using obsolete ClientLogin code path.";
825   }
826   // if online isn't complete yet, just return the offline result.
827   return state;
828 }
829
830 CryptohomeAuthenticator::AuthState
831 CryptohomeAuthenticator::ResolveCryptohomeFailureState() {
832   DCHECK(task_runner_->RunsTasksOnCurrentThread());
833   if (remove_attempted_ || resync_attempted_)
834     return FAILED_REMOVE;
835   if (ephemeral_mount_attempted_)
836     return FAILED_TMPFS;
837   if (migrate_attempted_)
838     return NEED_OLD_PW;
839   if (check_key_attempted_)
840     return LOGIN_FAILED;
841
842   if (current_state_->cryptohome_code() ==
843       cryptohome::MOUNT_ERROR_TPM_NEEDS_REBOOT) {
844     // Critical TPM error detected, reboot needed.
845     return FAILED_TPM;
846   }
847
848   // Return intermediate states in the following case:
849   // when there is an online result to use;
850   // This is the case after user finishes Gaia login;
851   if (current_state_->online_complete()) {
852     if (current_state_->cryptohome_code() ==
853         cryptohome::MOUNT_ERROR_KEY_FAILURE) {
854       // If we tried a mount but they used the wrong key, we may need to
855       // ask the user for her old password.  We'll only know once we've
856       // done the online check.
857       return POSSIBLE_PW_CHANGE;
858     }
859     if (current_state_->cryptohome_code() ==
860         cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST) {
861       // If we tried a mount but the user did not exist, then we should wait
862       // for online login to succeed and try again with the "create" flag set.
863       return NO_MOUNT;
864     }
865   }
866
867   if (!current_state_->username_hash_valid())
868     return FAILED_USERNAME_HASH;
869
870   return FAILED_MOUNT;
871 }
872
873 CryptohomeAuthenticator::AuthState
874 CryptohomeAuthenticator::ResolveCryptohomeSuccessState() {
875   DCHECK(task_runner_->RunsTasksOnCurrentThread());
876   if (resync_attempted_)
877     return CREATE_NEW;
878   if (remove_attempted_)
879     return REMOVED_DATA_AFTER_FAILURE;
880   if (migrate_attempted_)
881     return RECOVER_MOUNT;
882   if (check_key_attempted_)
883     return UNLOCK;
884
885   if (current_state_->user_type == user_manager::USER_TYPE_GUEST)
886     return GUEST_LOGIN;
887   if (current_state_->user_type == user_manager::USER_TYPE_RETAIL_MODE)
888     return DEMO_LOGIN;
889   if (current_state_->user_type == user_manager::USER_TYPE_PUBLIC_ACCOUNT)
890     return PUBLIC_ACCOUNT_LOGIN;
891   if (current_state_->user_type == user_manager::USER_TYPE_KIOSK_APP)
892     return KIOSK_ACCOUNT_LOGIN;
893   if (current_state_->user_type == user_manager::USER_TYPE_SUPERVISED)
894     return SUPERVISED_USER_LOGIN;
895
896   if (!VerifyOwner())
897     return CONTINUE;
898   return user_can_login_ ? OFFLINE_LOGIN : OWNER_REQUIRED;
899 }
900
901 CryptohomeAuthenticator::AuthState
902 CryptohomeAuthenticator::ResolveOnlineSuccessState(
903     CryptohomeAuthenticator::AuthState offline_state) {
904   DCHECK(task_runner_->RunsTasksOnCurrentThread());
905   switch (offline_state) {
906     case POSSIBLE_PW_CHANGE:
907       return NEED_OLD_PW;
908     case NO_MOUNT:
909       return CREATE_NEW;
910     case OFFLINE_LOGIN:
911       return ONLINE_LOGIN;
912     default:
913       NOTREACHED();
914       return offline_state;
915   }
916 }
917
918 void CryptohomeAuthenticator::ResolveLoginCompletionStatus() {
919   // Shortcut online state resolution process.
920   current_state_->RecordOnlineLoginStatus(AuthFailure::AuthFailureNone());
921   Resolve();
922 }
923
924 void CryptohomeAuthenticator::SetOwnerState(bool owner_check_finished,
925                                             bool check_result) {
926   owner_is_verified_ = owner_check_finished;
927   user_can_login_ = check_result;
928 }
929
930 }  // namespace chromeos