// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/proximity_auth/unlock_manager.h" #include "base/bind.h" #include "base/location.h" #include "base/logging.h" #include "base/thread_task_runner_handle.h" #include "base/time/default_tick_clock.h" #include "base/time/time.h" #include "build/build_config.h" #include "components/proximity_auth/logging/logging.h" #include "components/proximity_auth/messenger.h" #include "components/proximity_auth/metrics.h" #include "components/proximity_auth/proximity_auth_client.h" #include "components/proximity_auth/proximity_monitor_impl.h" #include "components/proximity_auth/remote_device.h" #include "components/proximity_auth/secure_context.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #if defined(OS_CHROMEOS) #include "chromeos/dbus/dbus_thread_manager.h" using chromeos::DBusThreadManager; #endif // defined(OS_CHROMEOS) namespace proximity_auth { namespace { // The maximum amount of time, in seconds, that the unlock manager can stay in // the 'waking up' state after resuming from sleep. const int kWakingUpDurationSecs = 15; // The limit, in seconds, on the elapsed time for an auth attempt. If an auth // attempt exceeds this limit, it will time out and be rejected. This is // provided as a failsafe, in case something goes wrong. const int kAuthAttemptTimeoutSecs = 5; // Returns the remote device's security settings state, for metrics, // corresponding to a remote status update. metrics::RemoteSecuritySettingsState GetRemoteSecuritySettingsState( const RemoteStatusUpdate& status_update) { switch (status_update.secure_screen_lock_state) { case SECURE_SCREEN_LOCK_STATE_UNKNOWN: return metrics::RemoteSecuritySettingsState::UNKNOWN; case SECURE_SCREEN_LOCK_DISABLED: switch (status_update.trust_agent_state) { case TRUST_AGENT_UNSUPPORTED: return metrics::RemoteSecuritySettingsState:: SCREEN_LOCK_DISABLED_TRUST_AGENT_UNSUPPORTED; case TRUST_AGENT_DISABLED: return metrics::RemoteSecuritySettingsState:: SCREEN_LOCK_DISABLED_TRUST_AGENT_DISABLED; case TRUST_AGENT_ENABLED: return metrics::RemoteSecuritySettingsState:: SCREEN_LOCK_DISABLED_TRUST_AGENT_ENABLED; } case SECURE_SCREEN_LOCK_ENABLED: switch (status_update.trust_agent_state) { case TRUST_AGENT_UNSUPPORTED: return metrics::RemoteSecuritySettingsState:: SCREEN_LOCK_ENABLED_TRUST_AGENT_UNSUPPORTED; case TRUST_AGENT_DISABLED: return metrics::RemoteSecuritySettingsState:: SCREEN_LOCK_ENABLED_TRUST_AGENT_DISABLED; case TRUST_AGENT_ENABLED: return metrics::RemoteSecuritySettingsState:: SCREEN_LOCK_ENABLED_TRUST_AGENT_ENABLED; } } NOTREACHED(); return metrics::RemoteSecuritySettingsState::UNKNOWN; } } // namespace UnlockManager::UnlockManager( ProximityAuthSystem::ScreenlockType screenlock_type, ProximityAuthClient* proximity_auth_client) : screenlock_type_(screenlock_type), life_cycle_(nullptr), proximity_auth_client_(proximity_auth_client), is_locked_(false), is_attempting_auth_(false), is_waking_up_(false), screenlock_state_(ScreenlockState::INACTIVE), clear_waking_up_state_weak_ptr_factory_(this), reject_auth_attempt_weak_ptr_factory_(this), weak_ptr_factory_(this) { ScreenlockBridge* screenlock_bridge = ScreenlockBridge::Get(); screenlock_bridge->AddObserver(this); OnScreenLockedOrUnlocked(screenlock_bridge->IsLocked()); #if defined(OS_CHROMEOS) DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this); #endif // defined(OS_CHROMEOS) SetWakingUpState(true); if (device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) { device::BluetoothAdapterFactory::GetAdapter( base::Bind(&UnlockManager::OnBluetoothAdapterInitialized, weak_ptr_factory_.GetWeakPtr())); } } UnlockManager::~UnlockManager() { if (GetMessenger()) GetMessenger()->RemoveObserver(this); ScreenlockBridge::Get()->RemoveObserver(this); #if defined(OS_CHROMEOS) DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this); #endif // defined(OS_CHROMEOS) if (bluetooth_adapter_) bluetooth_adapter_->RemoveObserver(this); } bool UnlockManager::IsUnlockAllowed() { return (remote_screenlock_state_ && *remote_screenlock_state_ == RemoteScreenlockState::UNLOCKED && life_cycle_ && life_cycle_->GetState() == RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED && proximity_monitor_ && proximity_monitor_->IsUnlockAllowed() && (screenlock_type_ != ProximityAuthSystem::SIGN_IN || (GetMessenger() && GetMessenger()->SupportsSignIn()))); } void UnlockManager::SetRemoteDeviceLifeCycle( RemoteDeviceLifeCycle* life_cycle) { if (GetMessenger()) GetMessenger()->RemoveObserver(this); life_cycle_ = life_cycle; if (life_cycle_) { proximity_monitor_ = CreateProximityMonitor(life_cycle->GetRemoteDevice()); SetWakingUpState(true); } else { proximity_monitor_.reset(); } UpdateLockScreen(); } void UnlockManager::OnLifeCycleStateChanged() { RemoteDeviceLifeCycle::State state = life_cycle_->GetState(); PA_LOG(INFO) << "[Unlock] RemoteDeviceLifeCycle state changed: " << static_cast(state); remote_screenlock_state_.reset(); if (state == RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED) GetMessenger()->AddObserver(this); if (state == RemoteDeviceLifeCycle::State::AUTHENTICATION_FAILED) SetWakingUpState(false); UpdateLockScreen(); } void UnlockManager::OnUnlockEventSent(bool success) { if (!is_attempting_auth_) { PA_LOG(ERROR) << "[Unlock] Sent easy_unlock event, but no auth attempted."; return; } AcceptAuthAttempt(success); } void UnlockManager::OnRemoteStatusUpdate( const RemoteStatusUpdate& status_update) { PA_LOG(INFO) << "[Unlock] Status Update: (" << "user_present=" << status_update.user_presence << ", " << "secure_screen_lock=" << status_update.secure_screen_lock_state << ", " << "trust_agent=" << status_update.trust_agent_state << ")"; metrics::RecordRemoteSecuritySettingsState( GetRemoteSecuritySettingsState(status_update)); remote_screenlock_state_.reset(new RemoteScreenlockState( GetScreenlockStateFromRemoteUpdate(status_update))); // This also calls |UpdateLockScreen()| SetWakingUpState(false); } void UnlockManager::OnDecryptResponse(const std::string& decrypted_bytes) { if (!is_attempting_auth_) { PA_LOG(ERROR) << "[Unlock] Decrypt response received but not attempting " << "auth."; return; } if (decrypted_bytes.empty()) { PA_LOG(WARNING) << "[Unlock] Failed to decrypt sign-in challenge."; AcceptAuthAttempt(false); } else { sign_in_secret_.reset(new std::string(decrypted_bytes)); GetMessenger()->DispatchUnlockEvent(); } } void UnlockManager::OnUnlockResponse(bool success) { if (!is_attempting_auth_) { PA_LOG(ERROR) << "[Unlock] Unlock response received but not attempting " << "auth."; return; } PA_LOG(INFO) << "[Unlock] Unlock response from remote device: " << (success ? "success" : "failure"); if (success) GetMessenger()->DispatchUnlockEvent(); else AcceptAuthAttempt(false); } void UnlockManager::OnDisconnected() { GetMessenger()->RemoveObserver(this); } void UnlockManager::OnScreenDidLock( ScreenlockBridge::LockHandler::ScreenType screen_type) { OnScreenLockedOrUnlocked(true); } void UnlockManager::OnScreenDidUnlock( ScreenlockBridge::LockHandler::ScreenType screen_type) { OnScreenLockedOrUnlocked(false); } void UnlockManager::OnFocusedUserChanged(const AccountId& account_id) {} void UnlockManager::OnScreenLockedOrUnlocked(bool is_locked) { // TODO(tengs): Chrome will only start connecting to the phone when // the screen is locked, for privacy reasons. We should reinvestigate // this behaviour if we want automatic locking. if (is_locked && bluetooth_adapter_ && bluetooth_adapter_->IsPowered() && life_cycle_ && life_cycle_->GetState() == RemoteDeviceLifeCycle::State::FINDING_CONNECTION) { SetWakingUpState(true); } is_locked_ = is_locked; UpdateProximityMonitorState(); } void UnlockManager::OnBluetoothAdapterInitialized( scoped_refptr adapter) { bluetooth_adapter_ = adapter; bluetooth_adapter_->AddObserver(this); } void UnlockManager::AdapterPresentChanged(device::BluetoothAdapter* adapter, bool present) { UpdateLockScreen(); } void UnlockManager::AdapterPoweredChanged(device::BluetoothAdapter* adapter, bool powered) { UpdateLockScreen(); } #if defined(OS_CHROMEOS) void UnlockManager::SuspendDone(const base::TimeDelta& sleep_duration) { SetWakingUpState(true); } #endif // defined(OS_CHROMEOS) void UnlockManager::OnAuthAttempted( ScreenlockBridge::LockHandler::AuthType auth_type) { if (is_attempting_auth_) { PA_LOG(INFO) << "[Unlock] Already attempting auth."; return; } if (auth_type != ScreenlockBridge::LockHandler::USER_CLICK) return; is_attempting_auth_ = true; if (!life_cycle_) { PA_LOG(ERROR) << "[Unlock] No life_cycle active when auth is attempted"; AcceptAuthAttempt(false); UpdateLockScreen(); return; } if (!IsUnlockAllowed()) { AcceptAuthAttempt(false); UpdateLockScreen(); return; } base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&UnlockManager::AcceptAuthAttempt, reject_auth_attempt_weak_ptr_factory_.GetWeakPtr(), false), base::TimeDelta::FromSeconds(kAuthAttemptTimeoutSecs)); if (screenlock_type_ == ProximityAuthSystem::SIGN_IN) { SendSignInChallenge(); } else { if (GetMessenger()->SupportsSignIn()) { GetMessenger()->RequestUnlock(); } else { PA_LOG(INFO) << "[Unlock] Protocol v3.1 not supported, skipping " << "request_unlock."; GetMessenger()->DispatchUnlockEvent(); } } } scoped_ptr UnlockManager::CreateProximityMonitor( const RemoteDevice& remote_device) { return make_scoped_ptr(new ProximityMonitorImpl( remote_device, make_scoped_ptr(new base::DefaultTickClock()))); } void UnlockManager::SendSignInChallenge() { if (!life_cycle_ || !GetMessenger() || !GetMessenger()->GetSecureContext()) { PA_LOG(ERROR) << "Not ready to send sign-in challenge"; return; } RemoteDevice remote_device = life_cycle_->GetRemoteDevice(); proximity_auth_client_->GetChallengeForUserAndDevice( remote_device.user_id, remote_device.public_key, GetMessenger()->GetSecureContext()->GetChannelBindingData(), base::Bind(&UnlockManager::OnGotSignInChallenge, weak_ptr_factory_.GetWeakPtr())); } void UnlockManager::OnGotSignInChallenge(const std::string& challenge) { PA_LOG(INFO) << "Got sign-in challenge, sending for decryption..."; GetMessenger()->RequestDecryption(challenge); } ScreenlockState UnlockManager::GetScreenlockState() { if (!life_cycle_ || life_cycle_->GetState() == RemoteDeviceLifeCycle::State::STOPPED) return ScreenlockState::INACTIVE; if (IsUnlockAllowed()) return ScreenlockState::AUTHENTICATED; if (life_cycle_->GetState() == RemoteDeviceLifeCycle::State::AUTHENTICATION_FAILED) return ScreenlockState::PHONE_NOT_AUTHENTICATED; if (is_waking_up_) return ScreenlockState::BLUETOOTH_CONNECTING; if (!bluetooth_adapter_ || !bluetooth_adapter_->IsPowered()) return ScreenlockState::NO_BLUETOOTH; Messenger* messenger = GetMessenger(); if (screenlock_type_ == ProximityAuthSystem::SIGN_IN && messenger && !messenger->SupportsSignIn()) return ScreenlockState::PHONE_UNSUPPORTED; // If the RSSI is too low, then the remote device is nowhere near the local // device. This message should take priority over messages about screen lock // states. if (!proximity_monitor_->IsUnlockAllowed() && !proximity_monitor_->IsInRssiRange()) return ScreenlockState::RSSI_TOO_LOW; if (remote_screenlock_state_) { switch (*remote_screenlock_state_) { case RemoteScreenlockState::DISABLED: return ScreenlockState::PHONE_NOT_LOCKABLE; case RemoteScreenlockState::LOCKED: if (proximity_monitor_->GetStrategy() == ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER && !proximity_monitor_->IsUnlockAllowed()) { return ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH; } return ScreenlockState::PHONE_LOCKED; case RemoteScreenlockState::UNKNOWN: return ScreenlockState::PHONE_UNSUPPORTED; case RemoteScreenlockState::UNLOCKED: // Handled by the code below. break; } } if (!proximity_monitor_->IsUnlockAllowed()) { ProximityMonitor::Strategy strategy = proximity_monitor_->GetStrategy(); if (strategy != ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER) { // CHECK_RSSI should have been handled above, and no other states should // prevent unlocking. PA_LOG(ERROR) << "[Unlock] Invalid ProximityMonitor strategy: " << static_cast(strategy); return ScreenlockState::NO_PHONE; } return ScreenlockState::TX_POWER_TOO_HIGH; } return ScreenlockState::NO_PHONE; } void UnlockManager::UpdateLockScreen() { UpdateProximityMonitorState(); ScreenlockState new_state = GetScreenlockState(); if (screenlock_state_ == new_state) return; proximity_auth_client_->UpdateScreenlockState(new_state); screenlock_state_ = new_state; } void UnlockManager::UpdateProximityMonitorState() { if (!proximity_monitor_) return; if (is_locked_ && life_cycle_ && life_cycle_->GetState() == RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED) { proximity_monitor_->Start(); } else { proximity_monitor_->Stop(); } } void UnlockManager::SetWakingUpState(bool is_waking_up) { is_waking_up_ = is_waking_up; // Clear the waking up state after a timeout. clear_waking_up_state_weak_ptr_factory_.InvalidateWeakPtrs(); if (is_waking_up_) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&UnlockManager::SetWakingUpState, clear_waking_up_state_weak_ptr_factory_.GetWeakPtr(), false), base::TimeDelta::FromSeconds(kWakingUpDurationSecs)); } UpdateLockScreen(); } void UnlockManager::AcceptAuthAttempt(bool should_accept) { if (!is_attempting_auth_) return; // Cancel the pending task to time out the auth attempt. reject_auth_attempt_weak_ptr_factory_.InvalidateWeakPtrs(); if (should_accept) proximity_monitor_->RecordProximityMetricsOnAuthSuccess(); is_attempting_auth_ = false; if (screenlock_type_ == ProximityAuthSystem::SIGN_IN) { PA_LOG(INFO) << "Finalizing sign-in..."; proximity_auth_client_->FinalizeSignin( should_accept && sign_in_secret_ ? *sign_in_secret_ : std::string()); } else { PA_LOG(INFO) << "Finalizing unlock..."; proximity_auth_client_->FinalizeUnlock(should_accept); } } UnlockManager::RemoteScreenlockState UnlockManager::GetScreenlockStateFromRemoteUpdate(RemoteStatusUpdate update) { switch (update.secure_screen_lock_state) { case SECURE_SCREEN_LOCK_DISABLED: return RemoteScreenlockState::DISABLED; case SECURE_SCREEN_LOCK_ENABLED: if (update.user_presence == USER_PRESENT) return RemoteScreenlockState::UNLOCKED; return RemoteScreenlockState::LOCKED; case SECURE_SCREEN_LOCK_STATE_UNKNOWN: return RemoteScreenlockState::UNKNOWN; } NOTREACHED(); return RemoteScreenlockState::UNKNOWN; } Messenger* UnlockManager::GetMessenger() { // TODO(tengs): We should use a weak pointer to hold the Messenger instance // instead. if (!life_cycle_) return nullptr; return life_cycle_->GetMessenger(); } } // namespace proximity_auth