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/google_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/boot_times_loader.h" 19 #include "chrome/browser/chromeos/cros/cryptohome_library.h" 20 #include "chrome/browser/chromeos/login/auth_response_handler.h" 21 #include "chrome/browser/chromeos/login/authentication_notification_details.h" 22 #include "chrome/browser/chromeos/login/login_status_consumer.h" 23 #include "chrome/browser/chromeos/login/ownership_service.h" 24 #include "chrome/browser/chromeos/login/user_manager.h" 25 #include "chrome/browser/profiles/profile.h" 26 #include "chrome/browser/profiles/profile_manager.h" 27 #include "chrome/common/chrome_paths.h" 28 #include "chrome/common/net/gaia/gaia_auth_fetcher.h" 29 #include "chrome/common/net/gaia/gaia_constants.h" 30 #include "content/browser/browser_thread.h" 31 #include "content/common/notification_service.h" 32 #include "net/base/load_flags.h" 33 #include "net/base/net_errors.h" 34 #include "net/url_request/url_request_status.h" 35 #include "third_party/libjingle/source/talk/base/urlencode.h" 36 37 using base::Time; 38 using base::TimeDelta; 39 using file_util::GetFileSize; 40 using file_util::PathExists; 41 using file_util::ReadFile; 42 using file_util::ReadFileToString; 43 44 namespace chromeos { 45 46 // static 47 const char GoogleAuthenticator::kLocalaccountFile[] = "localaccount"; 48 49 // static 50 const int GoogleAuthenticator::kClientLoginTimeoutMs = 10000; 51 // static 52 const int GoogleAuthenticator::kLocalaccountRetryIntervalMs = 20; 53 54 const int kPassHashLen = 32; 55 56 GoogleAuthenticator::GoogleAuthenticator(LoginStatusConsumer* consumer) 57 : Authenticator(consumer), 58 user_manager_(UserManager::Get()), 59 hosted_policy_(GaiaAuthFetcher::HostedAccountsAllowed), 60 unlock_(false), 61 try_again_(true), 62 checked_for_localaccount_(false) { 63 CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded()); 64 // If not already owned, this is a no-op. If it is, this loads the owner's 65 // public key off of disk. 66 OwnershipService::GetSharedInstance()->StartLoadOwnerKeyAttempt(); 67 } 68 69 GoogleAuthenticator::~GoogleAuthenticator() {} 70 71 void GoogleAuthenticator::CancelClientLogin() { 72 if (gaia_authenticator_->HasPendingFetch()) { 73 VLOG(1) << "Canceling ClientLogin attempt."; 74 gaia_authenticator_->CancelRequest(); 75 76 BrowserThread::PostTask( 77 BrowserThread::FILE, FROM_HERE, 78 NewRunnableMethod(this, 79 &GoogleAuthenticator::LoadLocalaccount, 80 std::string(kLocalaccountFile))); 81 82 CheckOffline(LoginFailure(LoginFailure::LOGIN_TIMED_OUT)); 83 } 84 } 85 86 void GoogleAuthenticator::TryClientLogin() { 87 gaia_authenticator_->StartClientLogin( 88 username_, 89 password_, 90 GaiaConstants::kContactsService, 91 login_token_, 92 login_captcha_, 93 hosted_policy_); 94 95 BrowserThread::PostDelayedTask( 96 BrowserThread::UI, 97 FROM_HERE, 98 NewRunnableMethod(this, 99 &GoogleAuthenticator::CancelClientLogin), 100 kClientLoginTimeoutMs); 101 } 102 103 void GoogleAuthenticator::PrepareClientLoginAttempt( 104 const std::string& password, 105 const std::string& token, 106 const std::string& captcha) { 107 108 // Save so we can retry. 109 password_.assign(password); 110 login_token_.assign(token); 111 login_captcha_.assign(captcha); 112 } 113 114 void GoogleAuthenticator::ClearClientLoginAttempt() { 115 // Not clearing the password, because we may need to pass it to the 116 // sync service if login is successful. 117 login_token_.clear(); 118 login_captcha_.clear(); 119 } 120 121 bool GoogleAuthenticator::AuthenticateToLogin( 122 Profile* profile, 123 const std::string& username, 124 const std::string& password, 125 const std::string& login_token, 126 const std::string& login_captcha) { 127 unlock_ = false; 128 129 // TODO(cmasone): Figure out how to parallelize fetch, username/password 130 // processing without impacting testability. 131 username_.assign(Canonicalize(username)); 132 ascii_hash_.assign(HashPassword(password)); 133 134 gaia_authenticator_.reset( 135 new GaiaAuthFetcher(this, 136 GaiaConstants::kChromeOSSource, 137 profile->GetRequestContext())); 138 // Will be used for retries. 139 PrepareClientLoginAttempt(password, login_token, login_captcha); 140 TryClientLogin(); 141 return true; 142 } 143 144 bool GoogleAuthenticator::AuthenticateToUnlock(const std::string& username, 145 const std::string& password) { 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 147 username_.assign(Canonicalize(username)); 148 ascii_hash_.assign(HashPassword(password)); 149 unlock_ = true; 150 BrowserThread::PostTask( 151 BrowserThread::FILE, FROM_HERE, 152 NewRunnableMethod(this, 153 &GoogleAuthenticator::LoadLocalaccount, 154 std::string(kLocalaccountFile))); 155 BrowserThread::PostTask( 156 BrowserThread::UI, FROM_HERE, 157 NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline, 158 LoginFailure(LoginFailure::UNLOCK_FAILED))); 159 return true; 160 } 161 162 void GoogleAuthenticator::LoginOffTheRecord() { 163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 164 int mount_error = chromeos::kCryptohomeMountErrorNone; 165 if (CrosLibrary::Get()->GetCryptohomeLibrary()->MountForBwsi(&mount_error)) { 166 AuthenticationNotificationDetails details(true); 167 NotificationService::current()->Notify( 168 NotificationType::LOGIN_AUTHENTICATION, 169 NotificationService::AllSources(), 170 Details<AuthenticationNotificationDetails>(&details)); 171 consumer_->OnOffTheRecordLoginSuccess(); 172 } else { 173 LOG(ERROR) << "Could not mount tmpfs: " << mount_error; 174 consumer_->OnLoginFailure( 175 LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)); 176 } 177 } 178 179 void GoogleAuthenticator::OnClientLoginSuccess( 180 const GaiaAuthConsumer::ClientLoginResult& credentials) { 181 182 VLOG(1) << "Online login successful!"; 183 ClearClientLoginAttempt(); 184 185 if (hosted_policy_ == GaiaAuthFetcher::HostedAccountsAllowed && 186 !user_manager_->IsKnownUser(username_)) { 187 // First time user, and we don't know if the account is HOSTED or not. 188 // Since we don't allow HOSTED accounts to log in, we need to try 189 // again, without allowing HOSTED accounts. 190 // 191 // NOTE: we used to do this in the opposite order, so that we'd only 192 // try the HOSTED pathway if GOOGLE-only failed. This breaks CAPTCHA 193 // handling, though. 194 hosted_policy_ = GaiaAuthFetcher::HostedAccountsNotAllowed; 195 TryClientLogin(); 196 return; 197 } 198 BrowserThread::PostTask( 199 BrowserThread::UI, FROM_HERE, 200 NewRunnableMethod(this, 201 &GoogleAuthenticator::OnLoginSuccess, 202 credentials, false)); 203 } 204 205 void GoogleAuthenticator::OnClientLoginFailure( 206 const GoogleServiceAuthError& error) { 207 if (error.state() == GoogleServiceAuthError::REQUEST_CANCELED) { 208 if (try_again_) { 209 try_again_ = false; 210 LOG(ERROR) << "Login attempt canceled!?!? Trying again."; 211 TryClientLogin(); 212 return; 213 } 214 LOG(ERROR) << "Login attempt canceled again? Already retried..."; 215 } 216 217 if (error.state() == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS && 218 !user_manager_->IsKnownUser(username_) && 219 hosted_policy_ != GaiaAuthFetcher::HostedAccountsAllowed) { 220 // This was a first-time login, we already tried allowing HOSTED accounts 221 // and succeeded. That we've failed with INVALID_GAIA_CREDENTIALS now 222 // indicates that the account is HOSTED. 223 LoginFailure failure_details = 224 LoginFailure::FromNetworkAuthFailure( 225 GoogleServiceAuthError( 226 GoogleServiceAuthError::HOSTED_NOT_ALLOWED)); 227 BrowserThread::PostTask( 228 BrowserThread::UI, FROM_HERE, 229 NewRunnableMethod(this, 230 &GoogleAuthenticator::OnLoginFailure, 231 failure_details)); 232 LOG(WARNING) << "Rejecting valid HOSTED account."; 233 hosted_policy_ = GaiaAuthFetcher::HostedAccountsNotAllowed; 234 return; 235 } 236 237 ClearClientLoginAttempt(); 238 239 if (error.state() == GoogleServiceAuthError::TWO_FACTOR) { 240 LOG(WARNING) << "Two factor authenticated. Sync will not work."; 241 GaiaAuthConsumer::ClientLoginResult result; 242 result.two_factor = true; 243 OnClientLoginSuccess(result); 244 return; 245 } 246 247 BrowserThread::PostTask( 248 BrowserThread::FILE, FROM_HERE, 249 NewRunnableMethod(this, 250 &GoogleAuthenticator::LoadLocalaccount, 251 std::string(kLocalaccountFile))); 252 253 LoginFailure failure_details = LoginFailure::FromNetworkAuthFailure(error); 254 255 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) { 256 // The fetch failed for network reasons, try offline login. 257 BrowserThread::PostTask( 258 BrowserThread::UI, FROM_HERE, 259 NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline, 260 failure_details)); 261 return; 262 } 263 264 // The fetch succeeded, but ClientLogin said no, or we exhausted retries. 265 BrowserThread::PostTask( 266 BrowserThread::UI, FROM_HERE, 267 NewRunnableMethod(this, 268 &GoogleAuthenticator::CheckLocalaccount, 269 failure_details)); 270 } 271 272 void GoogleAuthenticator::OnLoginSuccess( 273 const GaiaAuthConsumer::ClientLoginResult& credentials, 274 bool request_pending) { 275 // Send notification of success 276 AuthenticationNotificationDetails details(true); 277 NotificationService::current()->Notify( 278 NotificationType::LOGIN_AUTHENTICATION, 279 NotificationService::AllSources(), 280 Details<AuthenticationNotificationDetails>(&details)); 281 282 int mount_error = chromeos::kCryptohomeMountErrorNone; 283 BootTimesLoader::Get()->AddLoginTimeMarker("CryptohomeMounting", false); 284 if (unlock_ || 285 (CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(username_.c_str(), 286 ascii_hash_.c_str(), 287 &mount_error))) { 288 BootTimesLoader::Get()->AddLoginTimeMarker("CryptohomeMounted", true); 289 consumer_->OnLoginSuccess(username_, 290 password_, 291 credentials, 292 request_pending); 293 } else if (!unlock_ && 294 mount_error == chromeos::kCryptohomeMountErrorKeyFailure) { 295 consumer_->OnPasswordChangeDetected(credentials); 296 } else { 297 OnLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME)); 298 } 299 } 300 301 void GoogleAuthenticator::CheckOffline(const LoginFailure& error) { 302 VLOG(1) << "Attempting offline login"; 303 if (CrosLibrary::Get()->GetCryptohomeLibrary()->CheckKey( 304 username_.c_str(), 305 ascii_hash_.c_str())) { 306 // The fetch didn't succeed, but offline login did. 307 VLOG(1) << "Offline login successful!"; 308 OnLoginSuccess(GaiaAuthConsumer::ClientLoginResult(), false); 309 } else { 310 // We couldn't hit the network, and offline login failed. 311 GoogleAuthenticator::CheckLocalaccount(error); 312 } 313 } 314 315 void GoogleAuthenticator::CheckLocalaccount(const LoginFailure& error) { 316 { 317 base::AutoLock for_this_block(localaccount_lock_); 318 VLOG(1) << "Checking localaccount"; 319 if (!checked_for_localaccount_) { 320 BrowserThread::PostDelayedTask( 321 BrowserThread::UI, 322 FROM_HERE, 323 NewRunnableMethod(this, 324 &GoogleAuthenticator::CheckLocalaccount, 325 error), 326 kLocalaccountRetryIntervalMs); 327 return; 328 } 329 } 330 int mount_error = chromeos::kCryptohomeMountErrorNone; 331 if (!localaccount_.empty() && localaccount_ == username_) { 332 if (CrosLibrary::Get()->GetCryptohomeLibrary()->MountForBwsi( 333 &mount_error)) { 334 LOG(WARNING) << "Logging in with localaccount: " << localaccount_; 335 consumer_->OnLoginSuccess(username_, 336 std::string(), 337 GaiaAuthConsumer::ClientLoginResult(), 338 false); 339 } else { 340 LOG(ERROR) << "Could not mount tmpfs for local account: " << mount_error; 341 OnLoginFailure( 342 LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)); 343 } 344 } else { 345 OnLoginFailure(error); 346 } 347 } 348 349 void GoogleAuthenticator::OnLoginFailure(const LoginFailure& error) { 350 // Send notification of failure 351 AuthenticationNotificationDetails details(false); 352 NotificationService::current()->Notify( 353 NotificationType::LOGIN_AUTHENTICATION, 354 NotificationService::AllSources(), 355 Details<AuthenticationNotificationDetails>(&details)); 356 LOG(WARNING) << "Login failed: " << error.GetErrorString(); 357 consumer_->OnLoginFailure(error); 358 } 359 360 void GoogleAuthenticator::RecoverEncryptedData(const std::string& old_password, 361 const GaiaAuthConsumer::ClientLoginResult& credentials) { 362 363 std::string old_hash = HashPassword(old_password); 364 if (CrosLibrary::Get()->GetCryptohomeLibrary()->MigrateKey(username_, 365 old_hash, 366 ascii_hash_)) { 367 OnLoginSuccess(credentials, false); 368 return; 369 } 370 // User seems to have given us the wrong old password... 371 consumer_->OnPasswordChangeDetected(credentials); 372 } 373 374 void GoogleAuthenticator::ResyncEncryptedData( 375 const GaiaAuthConsumer::ClientLoginResult& credentials) { 376 377 if (CrosLibrary::Get()->GetCryptohomeLibrary()->Remove(username_)) { 378 OnLoginSuccess(credentials, false); 379 } else { 380 OnLoginFailure(LoginFailure(LoginFailure::DATA_REMOVAL_FAILED)); 381 } 382 } 383 384 void GoogleAuthenticator::RetryAuth(Profile* profile, 385 const std::string& username, 386 const std::string& password, 387 const std::string& login_token, 388 const std::string& login_captcha) { 389 NOTIMPLEMENTED(); 390 } 391 392 void GoogleAuthenticator::LoadSystemSalt() { 393 if (!system_salt_.empty()) 394 return; 395 system_salt_ = CrosLibrary::Get()->GetCryptohomeLibrary()->GetSystemSalt(); 396 CHECK(!system_salt_.empty()); 397 CHECK_EQ(system_salt_.size() % 2, 0U); 398 } 399 400 void GoogleAuthenticator::LoadLocalaccount(const std::string& filename) { 401 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 402 { 403 base::AutoLock for_this_block(localaccount_lock_); 404 if (checked_for_localaccount_) 405 return; 406 } 407 FilePath localaccount_file; 408 std::string localaccount; 409 if (PathService::Get(base::DIR_EXE, &localaccount_file)) { 410 localaccount_file = localaccount_file.Append(filename); 411 VLOG(1) << "Looking for localaccount in " << localaccount_file.value(); 412 413 ReadFileToString(localaccount_file, &localaccount); 414 TrimWhitespaceASCII(localaccount, TRIM_TRAILING, &localaccount); 415 VLOG(1) << "Loading localaccount: " << localaccount; 416 } else { 417 VLOG(1) << "Assuming no localaccount"; 418 } 419 SetLocalaccount(localaccount); 420 } 421 422 void GoogleAuthenticator::SetLocalaccount(const std::string& new_name) { 423 localaccount_ = new_name; 424 { // extra braces for clarity about AutoLock scope. 425 base::AutoLock for_this_block(localaccount_lock_); 426 checked_for_localaccount_ = true; 427 } 428 } 429 430 431 std::string GoogleAuthenticator::HashPassword(const std::string& password) { 432 // Get salt, ascii encode, update sha with that, then update with ascii 433 // of password, then end. 434 std::string ascii_salt = SaltAsAscii(); 435 unsigned char passhash_buf[kPassHashLen]; 436 char ascii_buf[kPassHashLen + 1]; 437 438 // Hash salt and password 439 SHA256Context ctx; 440 SHA256_Begin(&ctx); 441 SHA256_Update(&ctx, 442 reinterpret_cast<const unsigned char*>(ascii_salt.data()), 443 static_cast<unsigned int>(ascii_salt.length())); 444 SHA256_Update(&ctx, 445 reinterpret_cast<const unsigned char*>(password.data()), 446 static_cast<unsigned int>(password.length())); 447 SHA256_End(&ctx, 448 passhash_buf, 449 NULL, 450 static_cast<unsigned int>(sizeof(passhash_buf))); 451 452 std::vector<unsigned char> passhash(passhash_buf, 453 passhash_buf + sizeof(passhash_buf)); 454 BinaryToHex(passhash, 455 passhash.size() / 2, // only want top half, at least for now. 456 ascii_buf, 457 sizeof(ascii_buf)); 458 return std::string(ascii_buf, sizeof(ascii_buf) - 1); 459 } 460 461 std::string GoogleAuthenticator::SaltAsAscii() { 462 LoadSystemSalt(); // no-op if it's already loaded. 463 unsigned int salt_len = system_salt_.size(); 464 char ascii_salt[2 * salt_len + 1]; 465 if (GoogleAuthenticator::BinaryToHex(system_salt_, 466 salt_len, 467 ascii_salt, 468 sizeof(ascii_salt))) { 469 return std::string(ascii_salt, sizeof(ascii_salt) - 1); 470 } else { 471 return std::string(); 472 } 473 } 474 475 // static 476 bool GoogleAuthenticator::BinaryToHex(const std::vector<unsigned char>& binary, 477 const unsigned int binary_len, 478 char* hex_string, 479 const unsigned int len) { 480 if (len < 2*binary_len) 481 return false; 482 memset(hex_string, 0, len); 483 for (uint i = 0, j = 0; i < binary_len; i++, j+=2) 484 snprintf(hex_string + j, len - j, "%02x", binary[i]); 485 return true; 486 } 487 488 } // namespace chromeos 489