Home | History | Annotate | Download | only in login
      1 // Copyright (c) 2011 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 <string>
      8 #include <vector>
      9 
     10 #include "base/file_path.h"
     11 #include "base/file_util.h"
     12 #include "base/logging.h"
     13 #include "base/path_service.h"
     14 #include "base/string_util.h"
     15 #include "base/synchronization/lock.h"
     16 #include "crypto/third_party/nss/blapi.h"
     17 #include "crypto/third_party/nss/sha256.h"
     18 #include "chrome/browser/chromeos/cros/cryptohome_library.h"
     19 #include "chrome/browser/chromeos/login/auth_response_handler.h"
     20 #include "chrome/browser/chromeos/login/authentication_notification_details.h"
     21 #include "chrome/browser/chromeos/login/login_status_consumer.h"
     22 #include "chrome/browser/chromeos/login/ownership_service.h"
     23 #include "chrome/browser/chromeos/login/user_manager.h"
     24 #include "chrome/browser/profiles/profile.h"
     25 #include "chrome/browser/profiles/profile_manager.h"
     26 #include "chrome/common/chrome_paths.h"
     27 #include "chrome/common/net/gaia/gaia_auth_fetcher.h"
     28 #include "chrome/common/net/gaia/gaia_constants.h"
     29 #include "content/browser/browser_thread.h"
     30 #include "content/common/notification_service.h"
     31 #include "net/base/load_flags.h"
     32 #include "net/base/net_errors.h"
     33 #include "net/url_request/url_request_status.h"
     34 #include "third_party/libjingle/source/talk/base/urlencode.h"
     35 
     36 using base::Time;
     37 using base::TimeDelta;
     38 using file_util::GetFileSize;
     39 using file_util::PathExists;
     40 using file_util::ReadFile;
     41 using file_util::ReadFileToString;
     42 
     43 namespace chromeos {
     44 
     45 // static
     46 const char ParallelAuthenticator::kLocalaccountFile[] = "localaccount";
     47 
     48 // static
     49 const int ParallelAuthenticator::kClientLoginTimeoutMs = 10000;
     50 // static
     51 const int ParallelAuthenticator::kLocalaccountRetryIntervalMs = 20;
     52 
     53 const int kPassHashLen = 32;
     54 
     55 ParallelAuthenticator::ParallelAuthenticator(LoginStatusConsumer* consumer)
     56     : Authenticator(consumer),
     57       already_reported_success_(false),
     58       checked_for_localaccount_(false) {
     59   CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
     60   // If not already owned, this is a no-op.  If it is, this loads the owner's
     61   // public key off of disk.
     62   OwnershipService::GetSharedInstance()->StartLoadOwnerKeyAttempt();
     63 }
     64 
     65 ParallelAuthenticator::~ParallelAuthenticator() {}
     66 
     67 bool ParallelAuthenticator::AuthenticateToLogin(
     68     Profile* profile,
     69     const std::string& username,
     70     const std::string& password,
     71     const std::string& login_token,
     72     const std::string& login_captcha) {
     73   std::string canonicalized = Authenticator::Canonicalize(username);
     74   current_state_.reset(
     75       new AuthAttemptState(canonicalized,
     76                            password,
     77                            HashPassword(password),
     78                            login_token,
     79                            login_captcha,
     80                            !UserManager::Get()->IsKnownUser(canonicalized)));
     81   mounter_ = CryptohomeOp::CreateMountAttempt(current_state_.get(),
     82                                               this,
     83                                               false /* don't create */);
     84   current_online_ = new OnlineAttempt(current_state_.get(), this);
     85   // Sadly, this MUST be on the UI thread due to sending DBus traffic :-/
     86   BrowserThread::PostTask(
     87       BrowserThread::UI, FROM_HERE,
     88       NewRunnableMethod(mounter_.get(), &CryptohomeOp::Initiate));
     89   current_online_->Initiate(profile);
     90   BrowserThread::PostTask(
     91       BrowserThread::FILE, FROM_HERE,
     92       NewRunnableMethod(this,
     93                         &ParallelAuthenticator::LoadLocalaccount,
     94                         std::string(kLocalaccountFile)));
     95   return true;
     96 }
     97 
     98 bool ParallelAuthenticator::AuthenticateToUnlock(const std::string& username,
     99                                                  const std::string& password) {
    100   current_state_.reset(
    101       new AuthAttemptState(Authenticator::Canonicalize(username),
    102                            HashPassword(password)));
    103   BrowserThread::PostTask(
    104       BrowserThread::FILE, FROM_HERE,
    105       NewRunnableMethod(this,
    106                         &ParallelAuthenticator::LoadLocalaccount,
    107                         std::string(kLocalaccountFile)));
    108   key_checker_ = CryptohomeOp::CreateCheckKeyAttempt(current_state_.get(),
    109                                                      this);
    110   // Sadly, this MUST be on the UI thread due to sending DBus traffic :-/
    111   BrowserThread::PostTask(
    112       BrowserThread::UI, FROM_HERE,
    113       NewRunnableMethod(key_checker_.get(), &CryptohomeOp::Initiate));
    114   return true;
    115 }
    116 
    117 void ParallelAuthenticator::LoginOffTheRecord() {
    118   current_state_.reset(new AuthAttemptState("", "", "", "", "", false));
    119   guest_mounter_ =
    120       CryptohomeOp::CreateMountGuestAttempt(current_state_.get(), this);
    121   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    122   guest_mounter_->Initiate();
    123 }
    124 
    125 void ParallelAuthenticator::OnLoginSuccess(
    126     const GaiaAuthConsumer::ClientLoginResult& credentials,
    127     bool request_pending) {
    128   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    129   VLOG(1) << "Login success";
    130   // Send notification of success
    131   AuthenticationNotificationDetails details(true);
    132   NotificationService::current()->Notify(
    133       NotificationType::LOGIN_AUTHENTICATION,
    134       NotificationService::AllSources(),
    135       Details<AuthenticationNotificationDetails>(&details));
    136   {
    137     base::AutoLock for_this_block(success_lock_);
    138     already_reported_success_ = true;
    139   }
    140   consumer_->OnLoginSuccess(current_state_->username,
    141                             current_state_->password,
    142                             credentials,
    143                             request_pending);
    144 }
    145 
    146 void ParallelAuthenticator::OnOffTheRecordLoginSuccess() {
    147   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    148   // Send notification of success
    149   AuthenticationNotificationDetails details(true);
    150   NotificationService::current()->Notify(
    151       NotificationType::LOGIN_AUTHENTICATION,
    152       NotificationService::AllSources(),
    153       Details<AuthenticationNotificationDetails>(&details));
    154   consumer_->OnOffTheRecordLoginSuccess();
    155 }
    156 
    157 void ParallelAuthenticator::OnPasswordChangeDetected(
    158     const GaiaAuthConsumer::ClientLoginResult& credentials) {
    159   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    160   consumer_->OnPasswordChangeDetected(credentials);
    161 }
    162 
    163 void ParallelAuthenticator::CheckLocalaccount(const LoginFailure& error) {
    164   {
    165     base::AutoLock for_this_block(localaccount_lock_);
    166     VLOG(2) << "Checking localaccount";
    167     if (!checked_for_localaccount_) {
    168       BrowserThread::PostDelayedTask(
    169           BrowserThread::FILE, FROM_HERE,
    170           NewRunnableMethod(this,
    171                             &ParallelAuthenticator::CheckLocalaccount,
    172                             error),
    173           kLocalaccountRetryIntervalMs);
    174       return;
    175     }
    176   }
    177 
    178   if (!localaccount_.empty() && localaccount_ == current_state_->username) {
    179     // Success.  Go mount a tmpfs for the profile, if necessary.
    180     if (!current_state_->unlock) {
    181       guest_mounter_ =
    182           CryptohomeOp::CreateMountGuestAttempt(current_state_.get(), this);
    183       BrowserThread::PostTask(
    184           BrowserThread::UI, FROM_HERE,
    185           NewRunnableMethod(guest_mounter_.get(), &CryptohomeOp::Initiate));
    186     } else {
    187       BrowserThread::PostTask(
    188           BrowserThread::UI, FROM_HERE,
    189           NewRunnableMethod(this, &ParallelAuthenticator::OnLoginSuccess,
    190                             GaiaAuthConsumer::ClientLoginResult(), false));
    191     }
    192   } else {
    193     // Not the localaccount.  Fail, passing along cached error info.
    194     BrowserThread::PostTask(
    195         BrowserThread::UI, FROM_HERE,
    196         NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure, error));
    197   }
    198 }
    199 
    200 void ParallelAuthenticator::OnLoginFailure(const LoginFailure& error) {
    201   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    202   // Send notification of failure
    203   AuthenticationNotificationDetails details(false);
    204   NotificationService::current()->Notify(
    205       NotificationType::LOGIN_AUTHENTICATION,
    206       NotificationService::AllSources(),
    207       Details<AuthenticationNotificationDetails>(&details));
    208   LOG(WARNING) << "Login failed: " << error.GetErrorString();
    209   consumer_->OnLoginFailure(error);
    210 }
    211 
    212 void ParallelAuthenticator::RecoverEncryptedData(
    213     const std::string& old_password,
    214     const GaiaAuthConsumer::ClientLoginResult& credentials) {
    215   std::string old_hash = HashPassword(old_password);
    216   key_migrator_ = CryptohomeOp::CreateMigrateAttempt(current_state_.get(),
    217                                                      this,
    218                                                      true,
    219                                                      old_hash);
    220   BrowserThread::PostTask(
    221       BrowserThread::IO, FROM_HERE,
    222       NewRunnableMethod(this,
    223                         &ParallelAuthenticator::ResyncRecoverHelper,
    224                         key_migrator_));
    225 }
    226 
    227 void ParallelAuthenticator::ResyncEncryptedData(
    228     const GaiaAuthConsumer::ClientLoginResult& credentials) {
    229   data_remover_ =
    230       CryptohomeOp::CreateRemoveAttempt(current_state_.get(), this);
    231   BrowserThread::PostTask(
    232       BrowserThread::IO, FROM_HERE,
    233       NewRunnableMethod(this,
    234                         &ParallelAuthenticator::ResyncRecoverHelper,
    235                         data_remover_));
    236 }
    237 
    238 void ParallelAuthenticator::ResyncRecoverHelper(CryptohomeOp* to_initiate) {
    239   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    240   current_state_->ResetCryptohomeStatus();
    241   BrowserThread::PostTask(
    242       BrowserThread::UI, FROM_HERE,
    243       NewRunnableMethod(to_initiate, &CryptohomeOp::Initiate));
    244 }
    245 
    246 void ParallelAuthenticator::RetryAuth(Profile* profile,
    247                                       const std::string& username,
    248                                       const std::string& password,
    249                                       const std::string& login_token,
    250                                       const std::string& login_captcha) {
    251   reauth_state_.reset(
    252       new AuthAttemptState(Authenticator::Canonicalize(username),
    253                            password,
    254                            HashPassword(password),
    255                            login_token,
    256                            login_captcha,
    257                            false /* not a new user */));
    258   current_online_ = new OnlineAttempt(reauth_state_.get(), this);
    259   current_online_->Initiate(profile);
    260 }
    261 
    262 void ParallelAuthenticator::Resolve() {
    263   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    264   bool request_pending = false;
    265   bool create = false;
    266   ParallelAuthenticator::AuthState state = ResolveState();
    267   VLOG(1) << "Resolved state to: " << state;
    268   switch (state) {
    269     case CONTINUE:
    270     case POSSIBLE_PW_CHANGE:
    271     case NO_MOUNT:
    272       // These are intermediate states; we need more info from a request that
    273       // is still pending.
    274       break;
    275     case FAILED_MOUNT:
    276       // In this case, whether login succeeded or not, we can't log
    277       // the user in because their data is horked.  So, override with
    278       // the appropriate failure.
    279       BrowserThread::PostTask(
    280           BrowserThread::UI, FROM_HERE,
    281           NewRunnableMethod(
    282               this,
    283               &ParallelAuthenticator::OnLoginFailure,
    284               LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME)));
    285       break;
    286     case FAILED_REMOVE:
    287       // In this case, we tried to remove the user's old cryptohome at her
    288       // request, and the remove failed.
    289       BrowserThread::PostTask(
    290           BrowserThread::UI, FROM_HERE,
    291           NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure,
    292                             LoginFailure(LoginFailure::DATA_REMOVAL_FAILED)));
    293       break;
    294     case FAILED_TMPFS:
    295       // In this case, we tried to mount a tmpfs for BWSI or the localaccount
    296       // user and failed.
    297       BrowserThread::PostTask(
    298           BrowserThread::UI, FROM_HERE,
    299           NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure,
    300                             LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)));
    301       break;
    302     case CREATE_NEW:
    303       create = true;
    304     case RECOVER_MOUNT:
    305       current_state_->ResetCryptohomeStatus();
    306       mounter_ = CryptohomeOp::CreateMountAttempt(current_state_.get(),
    307                                                   this,
    308                                                   create);
    309       BrowserThread::PostTask(
    310           BrowserThread::UI, FROM_HERE,
    311           NewRunnableMethod(mounter_.get(), &CryptohomeOp::Initiate));
    312       break;
    313     case NEED_OLD_PW:
    314       BrowserThread::PostTask(
    315           BrowserThread::UI, FROM_HERE,
    316           NewRunnableMethod(this,
    317                             &ParallelAuthenticator::OnPasswordChangeDetected,
    318                             current_state_->credentials()));
    319       break;
    320     case ONLINE_FAILED:
    321       // In this case, we know online login was rejected because the account
    322       // is disabled or something similarly fatal.  Sending the user through
    323       // the same path they get when their password is rejected is cleaner
    324       // for now.
    325       // TODO(cmasone): optimize this so that we don't send the user through
    326       // the 'changed password' path when we know doing so won't succeed.
    327     case NEED_NEW_PW:
    328       {
    329         base::AutoLock for_this_block(success_lock_);
    330         if (!already_reported_success_) {
    331           // This allows us to present the same behavior for "online:
    332           // fail, offline: ok", regardless of the order in which we
    333           // receive the results.  There will be cases in which we get
    334           // the online failure some time after the offline success,
    335           // so we just force all cases in this category to present like this:
    336           // OnLoginSuccess(..., ..., true) -> OnLoginFailure().
    337           BrowserThread::PostTask(
    338               BrowserThread::UI, FROM_HERE,
    339               NewRunnableMethod(this, &ParallelAuthenticator::OnLoginSuccess,
    340                                 current_state_->credentials(), true));
    341         }
    342       }
    343       BrowserThread::PostTask(
    344           BrowserThread::UI, FROM_HERE,
    345           NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure,
    346                             (reauth_state_.get() ?
    347                              reauth_state_->online_outcome() :
    348                              current_state_->online_outcome())));
    349       break;
    350     case HAVE_NEW_PW:
    351       key_migrator_ =
    352           CryptohomeOp::CreateMigrateAttempt(reauth_state_.get(),
    353                                              this,
    354                                              true,
    355                                              current_state_->ascii_hash);
    356       BrowserThread::PostTask(
    357           BrowserThread::UI, FROM_HERE,
    358           NewRunnableMethod(key_migrator_.get(), &CryptohomeOp::Initiate));
    359       break;
    360     case OFFLINE_LOGIN:
    361       VLOG(2) << "Offline login";
    362       request_pending = !current_state_->online_complete();
    363       // Fall through.
    364     case UNLOCK:
    365       // Fall through.
    366     case ONLINE_LOGIN:
    367       VLOG(2) << "Online login";
    368       BrowserThread::PostTask(
    369           BrowserThread::UI, FROM_HERE,
    370           NewRunnableMethod(this, &ParallelAuthenticator::OnLoginSuccess,
    371                             current_state_->credentials(), request_pending));
    372       break;
    373     case LOCAL_LOGIN:
    374       BrowserThread::PostTask(
    375           BrowserThread::UI, FROM_HERE,
    376           NewRunnableMethod(
    377               this,
    378               &ParallelAuthenticator::OnOffTheRecordLoginSuccess));
    379       break;
    380     case LOGIN_FAILED:
    381       current_state_->ResetCryptohomeStatus();
    382       BrowserThread::PostTask(
    383           BrowserThread::FILE, FROM_HERE,
    384           NewRunnableMethod(this, &ParallelAuthenticator::CheckLocalaccount,
    385                             current_state_->online_outcome()));
    386       break;
    387     default:
    388       NOTREACHED();
    389       break;
    390   }
    391 }
    392 
    393 ParallelAuthenticator::AuthState ParallelAuthenticator::ResolveState() {
    394   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    395   // If we haven't mounted the user's home dir yet, we can't be done.
    396   // We never get past here if a cryptohome op is still pending.
    397   // This is an important invariant.
    398   if (!current_state_->cryptohome_complete())
    399     return CONTINUE;
    400 
    401   AuthState state = (reauth_state_.get() ? ResolveReauthState() : CONTINUE);
    402   if (state != CONTINUE)
    403     return state;
    404 
    405   if (current_state_->cryptohome_outcome())
    406     state = ResolveCryptohomeSuccessState();
    407   else
    408     state = ResolveCryptohomeFailureState();
    409 
    410   DCHECK(current_state_->cryptohome_complete());  // Ensure invariant holds.
    411   key_migrator_ = NULL;
    412   data_remover_ = NULL;
    413   guest_mounter_ = NULL;
    414   key_checker_ = NULL;
    415 
    416   if (state != POSSIBLE_PW_CHANGE &&
    417       state != NO_MOUNT &&
    418       state != OFFLINE_LOGIN)
    419     return state;
    420 
    421   if (current_state_->online_complete()) {
    422     if (current_state_->online_outcome().reason() == LoginFailure::NONE) {
    423       // Online attempt succeeded as well, so combine the results.
    424       return ResolveOnlineSuccessState(state);
    425     }
    426     // Online login attempt was rejected or failed to occur.
    427     return ResolveOnlineFailureState(state);
    428   }
    429   // if online isn't complete yet, just return the offline result.
    430   return state;
    431 }
    432 
    433 ParallelAuthenticator::AuthState
    434 ParallelAuthenticator::ResolveReauthState() {
    435   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    436   if (reauth_state_->cryptohome_complete()) {
    437     if (!reauth_state_->cryptohome_outcome()) {
    438       // If we've tried to migrate and failed, log the error and just wait
    439       // til next time the user logs in to migrate their cryptohome key.
    440       LOG(ERROR) << "Failed to migrate cryptohome key: "
    441                  << reauth_state_->cryptohome_code();
    442     }
    443     reauth_state_.reset(NULL);
    444     return ONLINE_LOGIN;
    445   }
    446   // Haven't tried the migrate yet, must be processing the online auth attempt.
    447   if (!reauth_state_->online_complete()) {
    448     NOTREACHED();  // Shouldn't be here at all, if online reauth isn't done!
    449     return CONTINUE;
    450   }
    451   return (reauth_state_->online_outcome().reason() == LoginFailure::NONE) ?
    452       HAVE_NEW_PW : NEED_NEW_PW;
    453 }
    454 
    455 ParallelAuthenticator::AuthState
    456 ParallelAuthenticator::ResolveCryptohomeFailureState() {
    457   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    458   if (data_remover_.get())
    459     return FAILED_REMOVE;
    460   if (guest_mounter_.get())
    461     return FAILED_TMPFS;
    462   if (key_migrator_.get())
    463     return NEED_OLD_PW;
    464   if (key_checker_.get())
    465     return LOGIN_FAILED;
    466   if (current_state_->cryptohome_code() ==
    467       chromeos::kCryptohomeMountErrorKeyFailure) {
    468     // If we tried a mount but they used the wrong key, we may need to
    469     // ask the user for her old password.  We'll only know once we've
    470     // done the online check.
    471     return POSSIBLE_PW_CHANGE;
    472   }
    473   if (current_state_->cryptohome_code() ==
    474       chromeos::kCryptohomeMountErrorUserDoesNotExist) {
    475     // If we tried a mount but the user did not exist, then we should wait
    476     // for online login to succeed and try again with the "create" flag set.
    477     return NO_MOUNT;
    478   }
    479   return FAILED_MOUNT;
    480 }
    481 
    482 ParallelAuthenticator::AuthState
    483 ParallelAuthenticator::ResolveCryptohomeSuccessState() {
    484   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    485   if (data_remover_.get())
    486     return CREATE_NEW;
    487   if (guest_mounter_.get())
    488     return LOCAL_LOGIN;
    489   if (key_migrator_.get())
    490     return RECOVER_MOUNT;
    491   if (key_checker_.get())
    492     return UNLOCK;
    493   return OFFLINE_LOGIN;
    494 }
    495 
    496 ParallelAuthenticator::AuthState
    497 ParallelAuthenticator::ResolveOnlineFailureState(
    498     ParallelAuthenticator::AuthState offline_state) {
    499   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    500   if (offline_state == OFFLINE_LOGIN) {
    501     if (current_state_->online_outcome().error().state() ==
    502         GoogleServiceAuthError::CONNECTION_FAILED) {
    503       // Couldn't do an online check, so just go with the offline result.
    504       return OFFLINE_LOGIN;
    505     }
    506     // Otherwise, online login was rejected!
    507     if (current_state_->online_outcome().error().state() ==
    508         GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
    509       return NEED_NEW_PW;
    510     }
    511     return ONLINE_FAILED;
    512   }
    513   return LOGIN_FAILED;
    514 }
    515 
    516 ParallelAuthenticator::AuthState
    517 ParallelAuthenticator::ResolveOnlineSuccessState(
    518     ParallelAuthenticator::AuthState offline_state) {
    519   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    520   switch (offline_state) {
    521     case POSSIBLE_PW_CHANGE:
    522       return NEED_OLD_PW;
    523     case NO_MOUNT:
    524       return CREATE_NEW;
    525     case OFFLINE_LOGIN:
    526       return ONLINE_LOGIN;
    527     default:
    528       NOTREACHED();
    529       return offline_state;
    530   }
    531 }
    532 
    533 void ParallelAuthenticator::LoadSystemSalt() {
    534   if (!system_salt_.empty())
    535     return;
    536   system_salt_ = CrosLibrary::Get()->GetCryptohomeLibrary()->GetSystemSalt();
    537   CHECK(!system_salt_.empty());
    538   CHECK_EQ(system_salt_.size() % 2, 0U);
    539 }
    540 
    541 void ParallelAuthenticator::LoadLocalaccount(const std::string& filename) {
    542   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    543   {
    544     base::AutoLock for_this_block(localaccount_lock_);
    545     if (checked_for_localaccount_)
    546       return;
    547   }
    548   FilePath localaccount_file;
    549   std::string localaccount;
    550   if (PathService::Get(base::DIR_EXE, &localaccount_file)) {
    551     localaccount_file = localaccount_file.Append(filename);
    552     VLOG(2) << "Looking for localaccount in " << localaccount_file.value();
    553 
    554     ReadFileToString(localaccount_file, &localaccount);
    555     TrimWhitespaceASCII(localaccount, TRIM_TRAILING, &localaccount);
    556     VLOG(1) << "Loading localaccount: " << localaccount;
    557   } else {
    558     VLOG(1) << "Assuming no localaccount";
    559   }
    560   SetLocalaccount(localaccount);
    561 }
    562 
    563 void ParallelAuthenticator::SetLocalaccount(const std::string& new_name) {
    564   localaccount_ = new_name;
    565   {  // extra braces for clarity about AutoLock scope.
    566     base::AutoLock for_this_block(localaccount_lock_);
    567     checked_for_localaccount_ = true;
    568   }
    569 }
    570 
    571 
    572 std::string ParallelAuthenticator::HashPassword(const std::string& password) {
    573   // Get salt, ascii encode, update sha with that, then update with ascii
    574   // of password, then end.
    575   std::string ascii_salt = SaltAsAscii();
    576   unsigned char passhash_buf[kPassHashLen];
    577   char ascii_buf[kPassHashLen + 1];
    578 
    579   // Hash salt and password
    580   SHA256Context ctx;
    581   SHA256_Begin(&ctx);
    582   SHA256_Update(&ctx,
    583                 reinterpret_cast<const unsigned char*>(ascii_salt.data()),
    584                 static_cast<unsigned int>(ascii_salt.length()));
    585   SHA256_Update(&ctx,
    586                 reinterpret_cast<const unsigned char*>(password.data()),
    587                 static_cast<unsigned int>(password.length()));
    588   SHA256_End(&ctx,
    589              passhash_buf,
    590              NULL,
    591              static_cast<unsigned int>(sizeof(passhash_buf)));
    592 
    593   std::vector<unsigned char> passhash(passhash_buf,
    594                                       passhash_buf + sizeof(passhash_buf));
    595   BinaryToHex(passhash,
    596               passhash.size() / 2,  // only want top half, at least for now.
    597               ascii_buf,
    598               sizeof(ascii_buf));
    599   return std::string(ascii_buf, sizeof(ascii_buf) - 1);
    600 }
    601 
    602 std::string ParallelAuthenticator::SaltAsAscii() {
    603   LoadSystemSalt();  // no-op if it's already loaded.
    604   unsigned int salt_len = system_salt_.size();
    605   char ascii_salt[2 * salt_len + 1];
    606   if (ParallelAuthenticator::BinaryToHex(system_salt_,
    607                                        salt_len,
    608                                        ascii_salt,
    609                                        sizeof(ascii_salt))) {
    610     return std::string(ascii_salt, sizeof(ascii_salt) - 1);
    611   }
    612   return std::string();
    613 }
    614 
    615 // static
    616 bool ParallelAuthenticator::BinaryToHex(
    617     const std::vector<unsigned char>& binary,
    618     const unsigned int binary_len,
    619     char* hex_string,
    620     const unsigned int len) {
    621   if (len < 2*binary_len)
    622     return false;
    623   memset(hex_string, 0, len);
    624   for (uint i = 0, j = 0; i < binary_len; i++, j+=2)
    625     snprintf(hex_string + j, len - j, "%02x", binary[i]);
    626   return true;
    627 }
    628 
    629 }  // namespace chromeos
    630