• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ParallelAuthenticator(LoginStatusConsumer * consumer)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 
~ParallelAuthenticator()65 ParallelAuthenticator::~ParallelAuthenticator() {}
66 
AuthenticateToLogin(Profile * profile,const std::string & username,const std::string & password,const std::string & login_token,const std::string & login_captcha)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 
AuthenticateToUnlock(const std::string & username,const std::string & password)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 
LoginOffTheRecord()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 
OnLoginSuccess(const GaiaAuthConsumer::ClientLoginResult & credentials,bool request_pending)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 
OnOffTheRecordLoginSuccess()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 
OnPasswordChangeDetected(const GaiaAuthConsumer::ClientLoginResult & credentials)157 void ParallelAuthenticator::OnPasswordChangeDetected(
158     const GaiaAuthConsumer::ClientLoginResult& credentials) {
159   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
160   consumer_->OnPasswordChangeDetected(credentials);
161 }
162 
CheckLocalaccount(const LoginFailure & error)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 
OnLoginFailure(const LoginFailure & error)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 
RecoverEncryptedData(const std::string & old_password,const GaiaAuthConsumer::ClientLoginResult & credentials)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 
ResyncEncryptedData(const GaiaAuthConsumer::ClientLoginResult & credentials)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 
ResyncRecoverHelper(CryptohomeOp * to_initiate)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 
RetryAuth(Profile * profile,const std::string & username,const std::string & password,const std::string & login_token,const std::string & login_captcha)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 
Resolve()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 
ResolveState()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
ResolveReauthState()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
ResolveCryptohomeFailureState()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
ResolveCryptohomeSuccessState()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
ResolveOnlineFailureState(ParallelAuthenticator::AuthState offline_state)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
ResolveOnlineSuccessState(ParallelAuthenticator::AuthState offline_state)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 
LoadSystemSalt()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 
LoadLocalaccount(const std::string & filename)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 
SetLocalaccount(const std::string & new_name)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 
HashPassword(const std::string & password)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 
SaltAsAscii()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
BinaryToHex(const std::vector<unsigned char> & binary,const unsigned int binary_len,char * hex_string,const unsigned int len)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