1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/chromeos/login/supervised/supervised_user_authenticator.h" 6 7#include "base/bind.h" 8#include "base/strings/string_number_conversions.h" 9#include "base/strings/string_util.h" 10#include "chrome/browser/chromeos/boot_times_loader.h" 11#include "chromeos/cryptohome/async_method_caller.h" 12#include "chromeos/cryptohome/cryptohome_parameters.h" 13#include "chromeos/cryptohome/system_salt_getter.h" 14#include "chromeos/dbus/cryptohome_client.h" 15#include "chromeos/dbus/dbus_thread_manager.h" 16#include "chromeos/login/auth/key.h" 17#include "content/public/browser/browser_thread.h" 18#include "crypto/sha2.h" 19#include "google_apis/gaia/gaia_auth_util.h" 20#include "third_party/cros_system_api/dbus/service_constants.h" 21 22using content::BrowserThread; 23 24namespace chromeos { 25 26namespace { 27 28// Records status and calls resolver->Resolve(). 29void TriggerResolve(SupervisedUserAuthenticator::AuthAttempt* attempt, 30 scoped_refptr<SupervisedUserAuthenticator> resolver, 31 bool success, 32 cryptohome::MountError return_code) { 33 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 34 attempt->RecordCryptohomeStatus(success, return_code); 35 resolver->Resolve(); 36} 37 38// Records status and calls resolver->Resolve(). 39void TriggerResolveResult(SupervisedUserAuthenticator::AuthAttempt* attempt, 40 scoped_refptr<SupervisedUserAuthenticator> resolver, 41 bool success, 42 const std::string& result) { 43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 44 attempt->RecordHash(result); 45 resolver->Resolve(); 46} 47 48// Calls TriggerResolve while adding login time marker. 49void TriggerResolveWithLoginTimeMarker( 50 const std::string& marker_name, 51 SupervisedUserAuthenticator::AuthAttempt* attempt, 52 scoped_refptr<SupervisedUserAuthenticator> resolver, 53 bool success, 54 cryptohome::MountError return_code) { 55 chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(marker_name, false); 56 TriggerResolve(attempt, resolver, success, return_code); 57} 58 59// Calls cryptohome's mount method. 60void Mount(SupervisedUserAuthenticator::AuthAttempt* attempt, 61 scoped_refptr<SupervisedUserAuthenticator> resolver, 62 int flags, 63 const std::string& system_salt) { 64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 65 chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( 66 "CryptohomeMount-LMU-Start", false); 67 68 Key key(attempt->password); 69 key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt); 70 cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount( 71 attempt->username, 72 key.GetSecret(), 73 flags, 74 base::Bind(&TriggerResolveWithLoginTimeMarker, 75 "CryptohomeMount-LMU-End", 76 attempt, 77 resolver)); 78 79 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( 80 attempt->username, 81 base::Bind(&TriggerResolveResult, attempt, resolver)); 82} 83 84// Calls cryptohome's addKey method. 85void AddKey(SupervisedUserAuthenticator::AuthAttempt* attempt, 86 scoped_refptr<SupervisedUserAuthenticator> resolver, 87 const std::string& plain_text_master_key, 88 const std::string& system_salt) { 89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 90 chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( 91 "CryptohomeAddKey-LMU-Start", false); 92 93 Key user_key(attempt->password); 94 user_key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt); 95 Key master_key(plain_text_master_key); 96 master_key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt); 97 cryptohome::AsyncMethodCaller::GetInstance()->AsyncAddKey( 98 attempt->username, 99 user_key.GetSecret(), 100 master_key.GetSecret(), 101 base::Bind(&TriggerResolveWithLoginTimeMarker, 102 "CryptohomeAddKey-LMU-End", 103 attempt, 104 resolver)); 105} 106 107} // namespace 108 109SupervisedUserAuthenticator::SupervisedUserAuthenticator( 110 AuthStatusConsumer* consumer) 111 : consumer_(consumer) {} 112 113void SupervisedUserAuthenticator::AuthenticateToMount( 114 const std::string& username, 115 const std::string& password) { 116 std::string canonicalized = gaia::CanonicalizeEmail(username); 117 118 current_state_.reset(new SupervisedUserAuthenticator::AuthAttempt( 119 canonicalized, password, false)); 120 121 SystemSaltGetter::Get()->GetSystemSalt( 122 base::Bind(&Mount, 123 current_state_.get(), 124 scoped_refptr<SupervisedUserAuthenticator>(this), 125 cryptohome::MOUNT_FLAGS_NONE)); 126} 127 128void SupervisedUserAuthenticator::AuthenticateToCreate( 129 const std::string& username, 130 const std::string& password) { 131 std::string canonicalized = gaia::CanonicalizeEmail(username); 132 133 current_state_.reset(new SupervisedUserAuthenticator::AuthAttempt( 134 canonicalized, password, false)); 135 136 SystemSaltGetter::Get()->GetSystemSalt( 137 base::Bind(&Mount, 138 current_state_.get(), 139 scoped_refptr<SupervisedUserAuthenticator>(this), 140 cryptohome::CREATE_IF_MISSING)); 141} 142 143void SupervisedUserAuthenticator::AddMasterKey( 144 const std::string& username, 145 const std::string& password, 146 const std::string& master_key) { 147 std::string canonicalized = gaia::CanonicalizeEmail(username); 148 149 current_state_.reset(new SupervisedUserAuthenticator::AuthAttempt( 150 canonicalized, password, true)); 151 152 SystemSaltGetter::Get()->GetSystemSalt( 153 base::Bind(&AddKey, 154 current_state_.get(), 155 scoped_refptr<SupervisedUserAuthenticator>(this), 156 master_key)); 157} 158 159void SupervisedUserAuthenticator::OnAuthenticationSuccess( 160 const std::string& mount_hash, 161 bool add_key) { 162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 163 VLOG(1) << "Supervised user authentication success"; 164 if (consumer_) { 165 if (add_key) 166 consumer_->OnAddKeySuccess(); 167 else 168 consumer_->OnMountSuccess(mount_hash); 169 } 170} 171 172void SupervisedUserAuthenticator::OnAuthenticationFailure( 173 SupervisedUserAuthenticator::AuthState state) { 174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 175 LOG(WARNING) << "Supervised user authentication failure"; 176 if (consumer_) 177 consumer_->OnAuthenticationFailure(state); 178} 179 180void SupervisedUserAuthenticator::Resolve() { 181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 182 SupervisedUserAuthenticator::AuthState state = ResolveState(); 183 VLOG(1) << "Resolved state to: " << state; 184 switch (state) { 185 case CONTINUE: 186 // These are intermediate states; we need more info from a request that 187 // is still pending. 188 break; 189 case FAILED_MOUNT: 190 // In this case, whether login succeeded or not, we can't log 191 // the user in because their data is horked. So, override with 192 // the appropriate failure. 193 BrowserThread::PostTask( 194 BrowserThread::UI, 195 FROM_HERE, 196 base::Bind(&SupervisedUserAuthenticator::OnAuthenticationFailure, 197 this, 198 state)); 199 break; 200 case NO_MOUNT: 201 // In this case, whether login succeeded or not, we can't log 202 // the user in because no data exist. So, override with 203 // the appropriate failure. 204 BrowserThread::PostTask( 205 BrowserThread::UI, 206 FROM_HERE, 207 base::Bind(&SupervisedUserAuthenticator::OnAuthenticationFailure, 208 this, 209 state)); 210 break; 211 case FAILED_TPM: 212 // In this case, we tried to create/mount cryptohome and failed 213 // because of the critical TPM error. 214 // Chrome will notify user and request reboot. 215 BrowserThread::PostTask( 216 BrowserThread::UI, 217 FROM_HERE, 218 base::Bind(&SupervisedUserAuthenticator::OnAuthenticationFailure, 219 this, 220 state)); 221 break; 222 case SUCCESS: 223 VLOG(2) << "Supervised user login"; 224 BrowserThread::PostTask( 225 BrowserThread::UI, 226 FROM_HERE, 227 base::Bind(&SupervisedUserAuthenticator::OnAuthenticationSuccess, 228 this, 229 current_state_->hash(), 230 current_state_->add_key)); 231 break; 232 default: 233 NOTREACHED(); 234 break; 235 } 236} 237 238SupervisedUserAuthenticator::~SupervisedUserAuthenticator() {} 239 240SupervisedUserAuthenticator::AuthState 241SupervisedUserAuthenticator::ResolveState() { 242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 243 // If we haven't mounted the user's home dir yet, we can't be done. 244 // We never get past here if a cryptohome op is still pending. 245 // This is an important invariant. 246 if (!current_state_->cryptohome_complete()) 247 return CONTINUE; 248 if (!current_state_->add_key && !current_state_->hash_obtained()) 249 return CONTINUE; 250 251 AuthState state; 252 253 if (current_state_->cryptohome_outcome()) 254 state = ResolveCryptohomeSuccessState(); 255 else 256 state = ResolveCryptohomeFailureState(); 257 258 DCHECK(current_state_->cryptohome_complete()); 259 DCHECK(current_state_->hash_obtained() || current_state_->add_key); 260 return state; 261} 262 263SupervisedUserAuthenticator::AuthState 264 SupervisedUserAuthenticator::ResolveCryptohomeFailureState() { 265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 266 LOG(ERROR) << "Failed to authenticate supervised user, code: " 267 << current_state_->cryptohome_code(); 268 if (current_state_->cryptohome_code() == 269 cryptohome::MOUNT_ERROR_TPM_NEEDS_REBOOT) { 270 // Critical TPM error detected, reboot needed. 271 return FAILED_TPM; 272 } 273 274 if (current_state_->cryptohome_code() == 275 cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST) { 276 // If we tried a mount but the user did not exist, then we should wait 277 // for online login to succeed and try again with the "create" flag set. 278 return NO_MOUNT; 279 } 280 281 return FAILED_MOUNT; 282} 283 284SupervisedUserAuthenticator::AuthState 285 SupervisedUserAuthenticator::ResolveCryptohomeSuccessState() { 286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 287 return SUCCESS; 288} 289 290SupervisedUserAuthenticator::AuthAttempt::AuthAttempt( 291 const std::string& username, 292 const std::string& password, 293 bool add_key_attempt) 294 : username(username), 295 password(password), 296 add_key(add_key_attempt), 297 cryptohome_complete_(false), 298 cryptohome_outcome_(false), 299 hash_obtained_(false), 300 cryptohome_code_(cryptohome::MOUNT_ERROR_NONE) {} 301 302SupervisedUserAuthenticator::AuthAttempt::~AuthAttempt() {} 303 304void SupervisedUserAuthenticator::AuthAttempt::RecordCryptohomeStatus( 305 bool cryptohome_outcome, 306 cryptohome::MountError cryptohome_code) { 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 308 cryptohome_complete_ = true; 309 cryptohome_outcome_ = cryptohome_outcome; 310 cryptohome_code_ = cryptohome_code; 311} 312 313void SupervisedUserAuthenticator::AuthAttempt::RecordHash( 314 const std::string& hash) { 315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 316 hash_obtained_ = true; 317 hash_ = hash; 318} 319 320bool SupervisedUserAuthenticator::AuthAttempt::cryptohome_complete() { 321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 322 return cryptohome_complete_; 323} 324 325bool SupervisedUserAuthenticator::AuthAttempt::cryptohome_outcome() { 326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 327 return cryptohome_outcome_; 328} 329 330cryptohome::MountError 331 SupervisedUserAuthenticator::AuthAttempt::cryptohome_code() { 332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 333 return cryptohome_code_; 334} 335 336bool SupervisedUserAuthenticator::AuthAttempt::hash_obtained() { 337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 338 return hash_obtained_; 339} 340 341std::string SupervisedUserAuthenticator::AuthAttempt::hash() { 342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 343 return hash_; 344} 345 346} // namespace chromeos 347