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