You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
889 lines
36 KiB
889 lines
36 KiB
/*
|
|
*
|
|
* Copyright 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
#include "security_manager_impl.h"
|
|
|
|
#include <iostream>
|
|
|
|
#include "common/bind.h"
|
|
#include "crypto_toolbox/crypto_toolbox.h"
|
|
#include "hci/address_with_type.h"
|
|
#include "os/log.h"
|
|
#include "os/rand.h"
|
|
#include "security/initial_informations.h"
|
|
#include "security/internal/security_manager_impl.h"
|
|
#include "security/pairing_handler_le.h"
|
|
#include "security/security_manager.h"
|
|
#include "security/ui.h"
|
|
|
|
namespace bluetooth {
|
|
namespace security {
|
|
namespace internal {
|
|
|
|
void SecurityManagerImpl::DispatchPairingHandler(
|
|
std::shared_ptr<record::SecurityRecord> record,
|
|
bool locally_initiated,
|
|
hci::IoCapability io_capability,
|
|
hci::AuthenticationRequirements auth_requirements,
|
|
pairing::OobData remote_p192_oob_data,
|
|
pairing::OobData remote_p256_oob_data) {
|
|
common::OnceCallback<void(hci::Address, PairingResultOrFailure)> callback =
|
|
common::BindOnce(&SecurityManagerImpl::OnPairingHandlerComplete, common::Unretained(this));
|
|
auto entry = pairing_handler_map_.find(record->GetPseudoAddress()->GetAddress());
|
|
if (entry != pairing_handler_map_.end()) {
|
|
LOG_WARN("Device already has a pairing handler, and is in the middle of pairing!");
|
|
return;
|
|
}
|
|
std::shared_ptr<pairing::PairingHandler> pairing_handler = nullptr;
|
|
switch (record->GetPseudoAddress()->GetAddressType()) {
|
|
case hci::AddressType::PUBLIC_DEVICE_ADDRESS: {
|
|
pairing_handler = std::make_shared<security::pairing::ClassicPairingHandler>(
|
|
security_manager_channel_,
|
|
record,
|
|
security_handler_,
|
|
std::move(callback),
|
|
user_interface_,
|
|
user_interface_handler_,
|
|
record->GetPseudoAddress()->ToString(),
|
|
name_db_module_);
|
|
break;
|
|
}
|
|
default:
|
|
ASSERT_LOG(false, "Pairing type %hhu not implemented!", record->GetPseudoAddress()->GetAddressType());
|
|
}
|
|
auto new_entry = std::pair<hci::Address, std::shared_ptr<pairing::PairingHandler>>(
|
|
record->GetPseudoAddress()->GetAddress(), pairing_handler);
|
|
pairing_handler_map_.insert(std::move(new_entry));
|
|
pairing_handler->Initiate(
|
|
locally_initiated, io_capability, auth_requirements, remote_p192_oob_data, remote_p256_oob_data);
|
|
}
|
|
|
|
void SecurityManagerImpl::Init() {
|
|
security_manager_channel_->SetChannelListener(this);
|
|
security_manager_channel_->SendCommand(hci::WriteSimplePairingModeBuilder::Create(hci::Enable::ENABLED));
|
|
security_manager_channel_->SendCommand(hci::WriteSecureConnectionsHostSupportBuilder::Create(hci::Enable::ENABLED));
|
|
|
|
ASSERT_LOG(storage_module_ != nullptr, "Storage module must not be null!");
|
|
security_database_.LoadRecordsFromStorage();
|
|
|
|
storage::AdapterConfig adapter_config = storage_module_->GetAdapterConfig();
|
|
if (!adapter_config.GetLeIdentityResolvingKey()) {
|
|
auto mutation = storage_module_->Modify();
|
|
mutation.Add(adapter_config.SetLeIdentityResolvingKey(bluetooth::os::GenerateRandom<16>()));
|
|
mutation.Commit();
|
|
}
|
|
|
|
Address controllerAddress = controller_->GetMacAddress();
|
|
if (!adapter_config.GetAddress() || adapter_config.GetAddress().value() != controllerAddress) {
|
|
auto mutation = storage_module_->Modify();
|
|
mutation.Add(adapter_config.SetAddress(controllerAddress));
|
|
mutation.Commit();
|
|
}
|
|
|
|
local_identity_address_ =
|
|
hci::AddressWithType(adapter_config.GetAddress().value(), hci::AddressType::PUBLIC_DEVICE_ADDRESS);
|
|
local_identity_resolving_key_ = adapter_config.GetLeIdentityResolvingKey().value().bytes;
|
|
|
|
hci::LeAddressManager::AddressPolicy address_policy = hci::LeAddressManager::AddressPolicy::USE_RESOLVABLE_ADDRESS;
|
|
hci::AddressWithType address_with_type(hci::Address{}, hci::AddressType::RANDOM_DEVICE_ADDRESS);
|
|
|
|
/* 7 minutes minimum, 15 minutes maximum for random address refreshing */
|
|
auto minimum_rotation_time = std::chrono::minutes(7);
|
|
auto maximum_rotation_time = std::chrono::minutes(15);
|
|
|
|
acl_manager_->SetPrivacyPolicyForInitiatorAddress(
|
|
address_policy, address_with_type, minimum_rotation_time, maximum_rotation_time);
|
|
}
|
|
|
|
void SecurityManagerImpl::CreateBond(hci::AddressWithType device) {
|
|
this->CreateBondOutOfBand(device, pairing::OobData(), pairing::OobData());
|
|
}
|
|
|
|
void SecurityManagerImpl::CreateBondOutOfBand(
|
|
hci::AddressWithType device, pairing::OobData remote_p192_oob_data, pairing::OobData remote_p256_oob_data) {
|
|
auto record = security_database_.FindOrCreate(device);
|
|
if (record->IsPaired()) {
|
|
// Bonded means we saved it, but the caller doesn't care
|
|
// Bonded will always mean paired
|
|
NotifyDeviceBonded(device);
|
|
} else {
|
|
if (!record->IsPairing()) {
|
|
// Dispatch pairing handler, if we are calling create we are the initiator
|
|
LOG_WARN("Dispatch #1");
|
|
DispatchPairingHandler(
|
|
record,
|
|
true,
|
|
this->local_io_capability_,
|
|
this->local_authentication_requirements_,
|
|
remote_p192_oob_data,
|
|
remote_p256_oob_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::CreateBondLe(hci::AddressWithType address) {
|
|
auto record = security_database_.FindOrCreate(address);
|
|
if (record->IsPaired()) {
|
|
NotifyDeviceBondFailed(address, PairingFailure("Already bonded"));
|
|
return;
|
|
}
|
|
|
|
pending_le_pairing_.address_ = address;
|
|
|
|
LeFixedChannelEntry* stored_chan = FindStoredLeChannel(address);
|
|
if (stored_chan) {
|
|
// We are already connected
|
|
ConnectionIsReadyStartPairing(stored_chan);
|
|
return;
|
|
}
|
|
|
|
l2cap_manager_le_->ConnectServices(
|
|
address, common::BindOnce(&SecurityManagerImpl::OnConnectionFailureLe, common::Unretained(this)),
|
|
security_handler_);
|
|
}
|
|
|
|
void SecurityManagerImpl::CancelBond(hci::AddressWithType device) {
|
|
auto entry = pairing_handler_map_.find(device.GetAddress());
|
|
if (entry != pairing_handler_map_.end()) {
|
|
auto cancel_me = entry->second;
|
|
pairing_handler_map_.erase(entry);
|
|
cancel_me->Cancel();
|
|
}
|
|
|
|
auto record = security_database_.FindOrCreate(device);
|
|
record->CancelPairing();
|
|
|
|
WipeLePairingHandler();
|
|
}
|
|
|
|
void SecurityManagerImpl::RemoveBond(hci::AddressWithType device) {
|
|
CancelBond(device);
|
|
security_manager_channel_->Disconnect(device.GetAddress());
|
|
security_database_.Remove(device);
|
|
security_manager_channel_->SendCommand(hci::DeleteStoredLinkKeyBuilder::Create(
|
|
device.GetAddress(), hci::DeleteStoredLinkKeyDeleteAllFlag::SPECIFIED_BD_ADDR));
|
|
NotifyDeviceUnbonded(device);
|
|
}
|
|
|
|
void SecurityManagerImpl::SetUserInterfaceHandler(UI* user_interface, os::Handler* handler) {
|
|
if (user_interface_ != nullptr || user_interface_handler_ != nullptr) {
|
|
LOG_ALWAYS_FATAL("Listener has already been registered!");
|
|
}
|
|
user_interface_ = user_interface;
|
|
user_interface_handler_ = handler;
|
|
}
|
|
|
|
// TODO(jpawlowski): remove once we have config file abstraction in cert tests
|
|
void SecurityManagerImpl::SetLeInitiatorAddressPolicyForTest(
|
|
hci::LeAddressManager::AddressPolicy address_policy,
|
|
hci::AddressWithType fixed_address,
|
|
crypto_toolbox::Octet16 rotation_irk,
|
|
std::chrono::milliseconds minimum_rotation_time,
|
|
std::chrono::milliseconds maximum_rotation_time) {
|
|
acl_manager_->SetPrivacyPolicyForInitiatorAddressForTest(
|
|
address_policy, fixed_address, rotation_irk, minimum_rotation_time, maximum_rotation_time);
|
|
}
|
|
|
|
void SecurityManagerImpl::RegisterCallbackListener(ISecurityManagerListener* listener, os::Handler* handler) {
|
|
for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
|
|
if (it->first == listener) {
|
|
LOG_ALWAYS_FATAL("Listener has already been registered!");
|
|
}
|
|
}
|
|
|
|
listeners_.push_back({listener, handler});
|
|
}
|
|
|
|
void SecurityManagerImpl::UnregisterCallbackListener(ISecurityManagerListener* listener) {
|
|
for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
|
|
if (it->first == listener) {
|
|
listeners_.erase(it);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOG_ALWAYS_FATAL("Listener has not been registered!");
|
|
}
|
|
|
|
void SecurityManagerImpl::NotifyDeviceBonded(hci::AddressWithType device) {
|
|
for (auto& iter : listeners_) {
|
|
iter.second->Post(common::Bind(&ISecurityManagerListener::OnDeviceBonded, common::Unretained(iter.first), device));
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::NotifyDeviceBondFailed(hci::AddressWithType device, PairingFailure status) {
|
|
for (auto& iter : listeners_) {
|
|
iter.second->Post(
|
|
common::Bind(&ISecurityManagerListener::OnDeviceBondFailed, common::Unretained(iter.first), device, status));
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::NotifyDeviceUnbonded(hci::AddressWithType device) {
|
|
for (auto& iter : listeners_) {
|
|
iter.second->Post(
|
|
common::Bind(&ISecurityManagerListener::OnDeviceUnbonded, common::Unretained(iter.first), device));
|
|
}
|
|
acl_manager_->RemoveDeviceFromConnectList(device);
|
|
}
|
|
|
|
void SecurityManagerImpl::NotifyEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view) {
|
|
for (auto& iter : listeners_) {
|
|
iter.second->Post(common::Bind(&ISecurityManagerListener::OnEncryptionStateChanged, common::Unretained(iter.first),
|
|
encryption_change_view));
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void SecurityManagerImpl::HandleEvent(T packet) {
|
|
ASSERT(packet.IsValid());
|
|
auto entry = pairing_handler_map_.find(packet.GetBdAddr());
|
|
|
|
if (entry == pairing_handler_map_.end()) {
|
|
auto bd_addr = packet.GetBdAddr();
|
|
auto event_code = packet.GetEventCode();
|
|
|
|
if (event_code != hci::EventCode::LINK_KEY_REQUEST && event_code != hci::EventCode::PIN_CODE_REQUEST &&
|
|
event_code != hci::EventCode::IO_CAPABILITY_RESPONSE) {
|
|
LOG_ERROR("No classic pairing handler for device '%s' ready for command %s ", bd_addr.ToString().c_str(),
|
|
hci::EventCodeText(event_code).c_str());
|
|
return;
|
|
}
|
|
|
|
auto device = storage_module_->GetDeviceByClassicMacAddress(bd_addr);
|
|
|
|
auto record =
|
|
security_database_.FindOrCreate(hci::AddressWithType{bd_addr, hci::AddressType::PUBLIC_DEVICE_ADDRESS});
|
|
LOG_WARN("Dispatch #2");
|
|
DispatchPairingHandler(
|
|
record,
|
|
false,
|
|
this->local_io_capability_,
|
|
this->local_authentication_requirements_,
|
|
pairing::OobData(),
|
|
pairing::OobData());
|
|
entry = pairing_handler_map_.find(bd_addr);
|
|
}
|
|
entry->second->OnReceive(packet);
|
|
}
|
|
|
|
void SecurityManagerImpl::OnHciEventReceived(hci::EventView packet) {
|
|
auto event = hci::EventView::Create(packet);
|
|
ASSERT_LOG(event.IsValid(), "Received invalid packet");
|
|
const hci::EventCode code = event.GetEventCode();
|
|
switch (code) {
|
|
case hci::EventCode::PIN_CODE_REQUEST:
|
|
HandleEvent<hci::PinCodeRequestView>(hci::PinCodeRequestView::Create(event));
|
|
break;
|
|
case hci::EventCode::LINK_KEY_REQUEST:
|
|
HandleEvent(hci::LinkKeyRequestView::Create(event));
|
|
break;
|
|
case hci::EventCode::LINK_KEY_NOTIFICATION:
|
|
HandleEvent(hci::LinkKeyNotificationView::Create(event));
|
|
break;
|
|
case hci::EventCode::IO_CAPABILITY_REQUEST:
|
|
HandleEvent(hci::IoCapabilityRequestView::Create(event));
|
|
break;
|
|
case hci::EventCode::IO_CAPABILITY_RESPONSE:
|
|
HandleEvent(hci::IoCapabilityResponseView::Create(event));
|
|
break;
|
|
case hci::EventCode::SIMPLE_PAIRING_COMPLETE:
|
|
HandleEvent(hci::SimplePairingCompleteView::Create(event));
|
|
break;
|
|
case hci::EventCode::REMOTE_OOB_DATA_REQUEST:
|
|
HandleEvent(hci::RemoteOobDataRequestView::Create(event));
|
|
break;
|
|
case hci::EventCode::USER_PASSKEY_NOTIFICATION:
|
|
HandleEvent(hci::UserPasskeyNotificationView::Create(event));
|
|
break;
|
|
case hci::EventCode::KEYPRESS_NOTIFICATION:
|
|
HandleEvent(hci::KeypressNotificationView::Create(event));
|
|
break;
|
|
case hci::EventCode::USER_CONFIRMATION_REQUEST:
|
|
HandleEvent(hci::UserConfirmationRequestView::Create(event));
|
|
break;
|
|
case hci::EventCode::USER_PASSKEY_REQUEST:
|
|
HandleEvent(hci::UserPasskeyRequestView::Create(event));
|
|
break;
|
|
case hci::EventCode::REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION:
|
|
LOG_INFO("Unhandled event: %s", hci::EventCodeText(code).c_str());
|
|
break;
|
|
|
|
case hci::EventCode::ENCRYPTION_CHANGE: {
|
|
EncryptionChangeView encryption_change_view = EncryptionChangeView::Create(event);
|
|
if (!encryption_change_view.IsValid()) {
|
|
LOG_ERROR("Invalid EncryptionChange packet received");
|
|
return;
|
|
}
|
|
if (encryption_change_view.GetConnectionHandle() == pending_le_pairing_.connection_handle_) {
|
|
pending_le_pairing_.handler_->OnHciEvent(event);
|
|
return;
|
|
}
|
|
NotifyEncryptionStateChanged(encryption_change_view);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ASSERT_LOG(false, "Cannot handle received packet: %s", hci::EventCodeText(code).c_str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::OnConnectionClosed(hci::Address address) {
|
|
auto entry = pairing_handler_map_.find(address);
|
|
if (entry != pairing_handler_map_.end()) {
|
|
LOG_INFO("Cancelling pairing handler for '%s'", address.ToString().c_str());
|
|
entry->second->Cancel();
|
|
}
|
|
auto record = security_database_.FindOrCreate(hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS));
|
|
if (record->IsTemporary()) {
|
|
security_database_.Remove(hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS));
|
|
}
|
|
if (this->facade_disconnect_callback_) {
|
|
this->security_handler_->Call(
|
|
*this->facade_disconnect_callback_, hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS));
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::OnHciLeEvent(hci::LeMetaEventView event) {
|
|
hci::SubeventCode code = event.GetSubeventCode();
|
|
|
|
if (code == hci::SubeventCode::LONG_TERM_KEY_REQUEST) {
|
|
hci::LeLongTermKeyRequestView le_long_term_key_request_view = hci::LeLongTermKeyRequestView::Create(event);
|
|
if (!le_long_term_key_request_view.IsValid()) {
|
|
LOG_ERROR("Invalid LeLongTermKeyRequestView packet received");
|
|
return;
|
|
}
|
|
|
|
if (le_long_term_key_request_view.GetConnectionHandle() == pending_le_pairing_.connection_handle_) {
|
|
pending_le_pairing_.handler_->OnHciLeEvent(event);
|
|
return;
|
|
}
|
|
|
|
LOG_INFO("Unhandled HCI LE security event, code %s", hci::SubeventCodeText(code).c_str());
|
|
return;
|
|
}
|
|
|
|
// hci::SubeventCode::READ_LOCAL_P256_PUBLIC_KEY_COMPLETE,
|
|
// hci::SubeventCode::GENERATE_DHKEY_COMPLETE,
|
|
LOG_ERROR("Unhandled HCI LE security event, code %s", hci::SubeventCodeText(code).c_str());
|
|
}
|
|
|
|
void SecurityManagerImpl::OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) {
|
|
auto entry = pairing_handler_map_.find(address.GetAddress());
|
|
if (entry != pairing_handler_map_.end()) {
|
|
entry->second->OnPairingPromptAccepted(address, confirmed);
|
|
} else {
|
|
if (pending_le_pairing_.address_ == address) {
|
|
pending_le_pairing_.handler_->OnUiAction(PairingEvent::UI_ACTION_TYPE::PAIRING_ACCEPTED, confirmed);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) {
|
|
auto entry = pairing_handler_map_.find(address.GetAddress());
|
|
if (entry != pairing_handler_map_.end()) {
|
|
entry->second->OnConfirmYesNo(address, confirmed);
|
|
} else {
|
|
if (pending_le_pairing_.address_ == address) {
|
|
pending_le_pairing_.handler_->OnUiAction(PairingEvent::UI_ACTION_TYPE::CONFIRM_YESNO, confirmed);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) {
|
|
auto entry = pairing_handler_map_.find(address.GetAddress());
|
|
if (entry != pairing_handler_map_.end()) {
|
|
entry->second->OnPasskeyEntry(address, passkey);
|
|
} else {
|
|
if (pending_le_pairing_.address_ == address) {
|
|
pending_le_pairing_.handler_->OnUiAction(PairingEvent::UI_ACTION_TYPE::PASSKEY, passkey);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::OnPinEntry(const bluetooth::hci::AddressWithType& address, std::vector<uint8_t> pin) {
|
|
auto entry = pairing_handler_map_.find(address.GetAddress());
|
|
if (entry != pairing_handler_map_.end()) {
|
|
LOG_INFO("PIN for %s", address.ToString().c_str());
|
|
entry->second->OnPinEntry(address, pin);
|
|
} else {
|
|
LOG_WARN("No handler found for PIN for %s", address.ToString().c_str());
|
|
// TODO(jpawlowski): Implement LE version
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::OnPairingHandlerComplete(hci::Address address, PairingResultOrFailure status) {
|
|
auto entry = pairing_handler_map_.find(address);
|
|
if (entry != pairing_handler_map_.end()) {
|
|
pairing_handler_map_.erase(entry);
|
|
security_manager_channel_->Release(address);
|
|
}
|
|
auto remote = hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS);
|
|
if (!std::holds_alternative<PairingFailure>(status)) {
|
|
NotifyDeviceBonded(remote);
|
|
} else {
|
|
NotifyDeviceBondFailed(remote, std::get<PairingFailure>(status));
|
|
}
|
|
auto record = this->security_database_.FindOrCreate(remote);
|
|
record->CancelPairing();
|
|
security_database_.SaveRecordsToStorage();
|
|
// Only call update link if we need to
|
|
auto policy_callback_entry = enforce_security_policy_callback_map_.find(remote);
|
|
if (policy_callback_entry != enforce_security_policy_callback_map_.end()) {
|
|
UpdateLinkSecurityCondition(remote);
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::OnL2capRegistrationCompleteLe(
|
|
l2cap::le::FixedChannelManager::RegistrationResult result,
|
|
std::unique_ptr<l2cap::le::FixedChannelService> le_smp_service) {
|
|
ASSERT_LOG(result == bluetooth::l2cap::le::FixedChannelManager::RegistrationResult::SUCCESS,
|
|
"Failed to register to LE SMP Fixed Channel Service");
|
|
}
|
|
|
|
LeFixedChannelEntry* SecurityManagerImpl::FindStoredLeChannel(const hci::AddressWithType& device) {
|
|
for (LeFixedChannelEntry& storage : all_channels_) {
|
|
if (storage.channel_->GetDevice() == device) {
|
|
return &storage;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool SecurityManagerImpl::EraseStoredLeChannel(const hci::AddressWithType& device) {
|
|
for (auto it = all_channels_.begin(); it != all_channels_.end(); it++) {
|
|
if (it->channel_->GetDevice() == device) {
|
|
all_channels_.erase(it);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SecurityManagerImpl::OnSmpCommandLe(hci::AddressWithType device) {
|
|
LeFixedChannelEntry* stored_chan = FindStoredLeChannel(device);
|
|
if (!stored_chan) {
|
|
LOG_ALWAYS_FATAL("Received SMP command for unknown channel");
|
|
return;
|
|
}
|
|
|
|
std::unique_ptr<l2cap::le::FixedChannel>& channel = stored_chan->channel_;
|
|
|
|
auto packet = channel->GetQueueUpEnd()->TryDequeue();
|
|
if (!packet) {
|
|
LOG_ERROR("Received dequeue, but no data ready...");
|
|
return;
|
|
}
|
|
|
|
// Pending pairing - pass the data to the handler
|
|
auto temp_cmd_view = CommandView::Create(*packet);
|
|
if (pending_le_pairing_.address_ == device) {
|
|
pending_le_pairing_.handler_->OnCommandView(temp_cmd_view);
|
|
return;
|
|
}
|
|
|
|
// no pending pairing attempt
|
|
if (!temp_cmd_view.IsValid()) {
|
|
LOG_ERROR("Invalid Command packet");
|
|
return;
|
|
}
|
|
|
|
if (temp_cmd_view.GetCode() == Code::SECURITY_REQUEST) {
|
|
// TODO: either start encryption or pairing
|
|
LOG_WARN("Unhandled security request!!!");
|
|
return;
|
|
}
|
|
|
|
auto my_role = channel->GetLinkOptions()->GetRole();
|
|
if (temp_cmd_view.GetCode() == Code::PAIRING_REQUEST && my_role == hci::Role::PERIPHERAL) {
|
|
// TODO: if (pending_le_pairing_) { do not start another }
|
|
|
|
LOG_INFO("start of security request handling!");
|
|
|
|
stored_chan->channel_->Acquire();
|
|
|
|
PairingRequestView pairing_request = PairingRequestView::Create(temp_cmd_view);
|
|
auto& enqueue_buffer = stored_chan->enqueue_buffer_;
|
|
|
|
std::optional<InitialInformations::out_of_band_data> remote_oob_data = std::nullopt;
|
|
if (remote_oob_data_address_.has_value() && remote_oob_data_address_.value() == channel->GetDevice())
|
|
remote_oob_data = InitialInformations::out_of_band_data{.le_sc_c = remote_oob_data_le_sc_c_.value(),
|
|
.le_sc_r = remote_oob_data_le_sc_r_.value()};
|
|
|
|
// TODO: this doesn't have to be a unique ptr, if there is a way to properly std::move it into place where it's
|
|
// stored
|
|
pending_le_pairing_.connection_handle_ = channel->GetLinkOptions()->GetHandle();
|
|
InitialInformations initial_informations{
|
|
.my_role = my_role,
|
|
.my_connection_address = channel->GetLinkOptions()->GetLocalAddress(),
|
|
.my_identity_address = local_identity_address_,
|
|
.my_identity_resolving_key = local_identity_resolving_key_,
|
|
/*TODO: properly obtain capabilities from device-specific storage*/
|
|
.myPairingCapabilities = {.io_capability = local_le_io_capability_,
|
|
.oob_data_flag = local_le_oob_data_present_,
|
|
.auth_req = local_le_auth_req_,
|
|
.maximum_encryption_key_size = local_maximum_encryption_key_size_,
|
|
.initiator_key_distribution = 0x07,
|
|
.responder_key_distribution = 0x07},
|
|
.remotely_initiated = true,
|
|
.connection_handle = channel->GetLinkOptions()->GetHandle(),
|
|
.remote_connection_address = channel->GetDevice(),
|
|
.remote_name = "TODO: grab proper device name in sec mgr",
|
|
/* contains pairing request, if the pairing was remotely initiated */
|
|
.pairing_request = pairing_request,
|
|
.remote_oob_data = remote_oob_data,
|
|
.my_oob_data = local_le_oob_data_,
|
|
/* Used by Pairing Handler to present user with requests*/
|
|
.user_interface = user_interface_,
|
|
.user_interface_handler = user_interface_handler_,
|
|
|
|
/* HCI interface to use */
|
|
.le_security_interface = hci_security_interface_le_,
|
|
.proper_l2cap_interface = enqueue_buffer.get(),
|
|
.l2cap_handler = security_handler_,
|
|
/* Callback to execute once the Pairing process is finished */
|
|
// TODO: make it an common::OnceCallback ?
|
|
.OnPairingFinished = std::bind(&SecurityManagerImpl::OnPairingFinished, this, std::placeholders::_1),
|
|
};
|
|
pending_le_pairing_.address_ = device;
|
|
pending_le_pairing_.handler_ = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::OnConnectionOpenLe(std::unique_ptr<l2cap::le::FixedChannel> channel_param) {
|
|
auto enqueue_buffer_temp =
|
|
std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(channel_param->GetQueueUpEnd());
|
|
|
|
all_channels_.push_back({std::move(channel_param), std::move(enqueue_buffer_temp)});
|
|
auto& stored_channel = all_channels_.back();
|
|
auto& channel = stored_channel.channel_;
|
|
|
|
channel->RegisterOnCloseCallback(
|
|
security_handler_,
|
|
common::BindOnce(&SecurityManagerImpl::OnConnectionClosedLe, common::Unretained(this), channel->GetDevice()));
|
|
channel->GetQueueUpEnd()->RegisterDequeue(
|
|
security_handler_,
|
|
common::Bind(&SecurityManagerImpl::OnSmpCommandLe, common::Unretained(this), channel->GetDevice()));
|
|
|
|
if (pending_le_pairing_.address_ != channel->GetDevice()) {
|
|
return;
|
|
}
|
|
|
|
ConnectionIsReadyStartPairing(&stored_channel);
|
|
}
|
|
|
|
void SecurityManagerImpl::ConnectionIsReadyStartPairing(LeFixedChannelEntry* stored_channel) {
|
|
auto& channel = stored_channel->channel_;
|
|
auto& enqueue_buffer = stored_channel->enqueue_buffer_;
|
|
|
|
stored_channel->channel_->Acquire();
|
|
|
|
std::optional<InitialInformations::out_of_band_data> remote_oob_data = std::nullopt;
|
|
if (remote_oob_data_address_.has_value() && remote_oob_data_address_.value() == channel->GetDevice())
|
|
remote_oob_data = InitialInformations::out_of_band_data{.le_sc_c = remote_oob_data_le_sc_c_.value(),
|
|
.le_sc_r = remote_oob_data_le_sc_r_.value()};
|
|
|
|
// TODO: this doesn't have to be a unique ptr, if there is a way to properly std::move it into place where it's stored
|
|
pending_le_pairing_.connection_handle_ = channel->GetLinkOptions()->GetHandle();
|
|
InitialInformations initial_informations{
|
|
.my_role = channel->GetLinkOptions()->GetRole(),
|
|
.my_connection_address = channel->GetLinkOptions()->GetLocalAddress(),
|
|
.my_identity_address = local_identity_address_,
|
|
.my_identity_resolving_key = local_identity_resolving_key_,
|
|
/*TODO: properly obtain capabilities from device-specific storage*/
|
|
.myPairingCapabilities = {.io_capability = local_le_io_capability_,
|
|
.oob_data_flag = local_le_oob_data_present_,
|
|
.auth_req = local_le_auth_req_,
|
|
.maximum_encryption_key_size = local_maximum_encryption_key_size_,
|
|
.initiator_key_distribution = 0x07,
|
|
.responder_key_distribution = 0x07},
|
|
.remotely_initiated = false,
|
|
.connection_handle = channel->GetLinkOptions()->GetHandle(),
|
|
.remote_connection_address = channel->GetDevice(),
|
|
.remote_name = "TODO: grab proper device name in sec mgr",
|
|
/* contains pairing request, if the pairing was remotely initiated */
|
|
.pairing_request = std::nullopt, // TODO: handle remotely initiated pairing in SecurityManager properly
|
|
.remote_oob_data = remote_oob_data,
|
|
.my_oob_data = local_le_oob_data_,
|
|
/* Used by Pairing Handler to present user with requests*/
|
|
.user_interface = user_interface_,
|
|
.user_interface_handler = user_interface_handler_,
|
|
|
|
/* HCI interface to use */
|
|
.le_security_interface = hci_security_interface_le_,
|
|
.proper_l2cap_interface = enqueue_buffer.get(),
|
|
.l2cap_handler = security_handler_,
|
|
/* Callback to execute once the Pairing process is finished */
|
|
// TODO: make it an common::OnceCallback ?
|
|
.OnPairingFinished = std::bind(&SecurityManagerImpl::OnPairingFinished, this, std::placeholders::_1),
|
|
};
|
|
pending_le_pairing_.handler_ = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
|
|
}
|
|
|
|
void SecurityManagerImpl::OnConnectionClosedLe(hci::AddressWithType address, hci::ErrorCode error_code) {
|
|
if (pending_le_pairing_.address_ != address) {
|
|
LeFixedChannelEntry* stored_chan = FindStoredLeChannel(address);
|
|
if (!stored_chan) {
|
|
LOG_ALWAYS_FATAL("Received connection closed for unknown channel");
|
|
return;
|
|
}
|
|
stored_chan->channel_->GetQueueUpEnd()->UnregisterDequeue();
|
|
stored_chan->enqueue_buffer_.reset();
|
|
EraseStoredLeChannel(address);
|
|
return;
|
|
}
|
|
pending_le_pairing_.handler_->SendExitSignal();
|
|
NotifyDeviceBondFailed(address, PairingFailure("Connection closed"));
|
|
}
|
|
|
|
void SecurityManagerImpl::OnConnectionFailureLe(bluetooth::l2cap::le::FixedChannelManager::ConnectionResult result) {
|
|
if (result.connection_result_code ==
|
|
bluetooth::l2cap::le::FixedChannelManager::ConnectionResultCode::FAIL_ALL_SERVICES_HAVE_CHANNEL) {
|
|
// TODO: already connected
|
|
}
|
|
|
|
// This callback is invoked only for devices we attempted to connect to.
|
|
NotifyDeviceBondFailed(pending_le_pairing_.address_, PairingFailure("Connection establishment failed"));
|
|
}
|
|
|
|
SecurityManagerImpl::SecurityManagerImpl(
|
|
os::Handler* security_handler,
|
|
l2cap::le::L2capLeModule* l2cap_le_module,
|
|
channel::SecurityManagerChannel* security_manager_channel,
|
|
hci::HciLayer* hci_layer,
|
|
hci::AclManager* acl_manager,
|
|
hci::Controller* controller,
|
|
storage::StorageModule* storage_module,
|
|
neighbor::NameDbModule* name_db_module)
|
|
: security_handler_(security_handler),
|
|
l2cap_le_module_(l2cap_le_module),
|
|
l2cap_manager_le_(l2cap_le_module_->GetFixedChannelManager()),
|
|
hci_security_interface_le_(
|
|
hci_layer->GetLeSecurityInterface(security_handler_->BindOn(this, &SecurityManagerImpl::OnHciLeEvent))),
|
|
security_manager_channel_(security_manager_channel),
|
|
acl_manager_(acl_manager),
|
|
controller_(controller),
|
|
storage_module_(storage_module),
|
|
security_record_storage_(storage_module, security_handler),
|
|
security_database_(security_record_storage_),
|
|
name_db_module_(name_db_module) {
|
|
Init();
|
|
|
|
l2cap_manager_le_->RegisterService(
|
|
bluetooth::l2cap::kSmpCid,
|
|
common::BindOnce(&SecurityManagerImpl::OnL2capRegistrationCompleteLe, common::Unretained(this)),
|
|
common::Bind(&SecurityManagerImpl::OnConnectionOpenLe, common::Unretained(this)), security_handler_);
|
|
}
|
|
|
|
void SecurityManagerImpl::OnPairingFinished(security::PairingResultOrFailure pairing_result) {
|
|
LOG_INFO(" ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ Received pairing result");
|
|
|
|
LeFixedChannelEntry* stored_chan = FindStoredLeChannel(pending_le_pairing_.address_);
|
|
if (stored_chan) {
|
|
stored_chan->channel_->Release();
|
|
}
|
|
|
|
if (std::holds_alternative<PairingFailure>(pairing_result)) {
|
|
PairingFailure failure = std::get<PairingFailure>(pairing_result);
|
|
LOG_INFO(" ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ failure message: %s",
|
|
failure.message.c_str());
|
|
NotifyDeviceBondFailed(stored_chan->channel_->GetDevice(), failure);
|
|
return;
|
|
}
|
|
|
|
auto result = std::get<PairingResult>(pairing_result);
|
|
LOG_INFO("Pairing with %s was successful", result.connection_address.ToString().c_str());
|
|
|
|
// TODO: ensure that the security level is not weaker than what we already have.
|
|
auto record = this->security_database_.FindOrCreate(result.connection_address);
|
|
record->identity_address_ = result.distributed_keys.remote_identity_address;
|
|
record->remote_ltk = result.distributed_keys.remote_ltk;
|
|
record->key_size = result.key_size;
|
|
record->security_level = result.security_level;
|
|
record->remote_ediv = result.distributed_keys.remote_ediv;
|
|
record->remote_rand = result.distributed_keys.remote_rand;
|
|
record->remote_irk = result.distributed_keys.remote_irk;
|
|
record->remote_signature_key = result.distributed_keys.remote_signature_key;
|
|
if (result.distributed_keys.remote_link_key)
|
|
record->SetLinkKey(*result.distributed_keys.remote_link_key, hci::KeyType::AUTHENTICATED_P256);
|
|
security_database_.SaveRecordsToStorage();
|
|
|
|
NotifyDeviceBonded(result.connection_address);
|
|
// We also notify bond complete using identity address. That's what old stack used to do.
|
|
if (result.distributed_keys.remote_identity_address)
|
|
NotifyDeviceBonded(*result.distributed_keys.remote_identity_address);
|
|
|
|
security_handler_->CallOn(this, &SecurityManagerImpl::WipeLePairingHandler);
|
|
}
|
|
|
|
void SecurityManagerImpl::WipeLePairingHandler() {
|
|
pending_le_pairing_.handler_.reset();
|
|
pending_le_pairing_.connection_handle_ = 0;
|
|
pending_le_pairing_.address_ = hci::AddressWithType();
|
|
}
|
|
|
|
// Facade Configuration API functions
|
|
void SecurityManagerImpl::SetDisconnectCallback(FacadeDisconnectCallback callback) {
|
|
this->facade_disconnect_callback_ = std::make_optional<FacadeDisconnectCallback>(callback);
|
|
}
|
|
|
|
void SecurityManagerImpl::SetIoCapability(hci::IoCapability io_capability) {
|
|
this->local_io_capability_ = io_capability;
|
|
}
|
|
|
|
void SecurityManagerImpl::SetLeIoCapability(security::IoCapability io_capability) {
|
|
this->local_le_io_capability_ = io_capability;
|
|
}
|
|
|
|
void SecurityManagerImpl::SetLeAuthRequirements(uint8_t auth_req) {
|
|
this->local_le_auth_req_ = auth_req;
|
|
}
|
|
|
|
void SecurityManagerImpl::SetLeMaximumEncryptionKeySize(uint8_t maximum_encryption_key_size) {
|
|
this->local_maximum_encryption_key_size_ = maximum_encryption_key_size;
|
|
}
|
|
|
|
void SecurityManagerImpl::SetLeOobDataPresent(OobDataFlag data_present) {
|
|
this->local_le_oob_data_present_ = data_present;
|
|
}
|
|
|
|
void SecurityManagerImpl::GetOutOfBandData(channel::SecurityCommandStatusCallback callback) {
|
|
this->security_manager_channel_->SendCommand(
|
|
hci::ReadLocalOobDataBuilder::Create(), std::forward<channel::SecurityCommandStatusCallback>(callback));
|
|
}
|
|
|
|
void SecurityManagerImpl::GetLeOutOfBandData(
|
|
std::array<uint8_t, 16>* confirmation_value, std::array<uint8_t, 16>* random_value) {
|
|
local_le_oob_data_ = std::make_optional<MyOobData>(PairingHandlerLe::GenerateOobData());
|
|
*confirmation_value = local_le_oob_data_.value().c;
|
|
*random_value = local_le_oob_data_.value().r;
|
|
}
|
|
|
|
void SecurityManagerImpl::SetOutOfBandData(
|
|
hci::AddressWithType remote_address,
|
|
std::array<uint8_t, 16> confirmation_value,
|
|
std::array<uint8_t, 16> random_value) {
|
|
remote_oob_data_address_ = remote_address;
|
|
remote_oob_data_le_sc_c_ = confirmation_value;
|
|
remote_oob_data_le_sc_r_ = random_value;
|
|
}
|
|
|
|
void SecurityManagerImpl::SetAuthenticationRequirements(hci::AuthenticationRequirements authentication_requirements) {
|
|
this->local_authentication_requirements_ = authentication_requirements;
|
|
}
|
|
|
|
void SecurityManagerImpl::InternalEnforceSecurityPolicy(
|
|
hci::AddressWithType remote,
|
|
l2cap::classic::SecurityPolicy policy,
|
|
l2cap::classic::SecurityEnforcementInterface::ResultCallback result_callback) {
|
|
if (IsSecurityRequirementSatisfied(remote, policy)) {
|
|
// Notify client immediately if already satisfied
|
|
std::move(result_callback).Invoke(true);
|
|
return;
|
|
}
|
|
|
|
// At this point we don't meet the security requirements; must pair
|
|
auto record = this->security_database_.FindOrCreate(remote);
|
|
hci::AuthenticationRequirements authentication_requirements = kDefaultAuthenticationRequirements;
|
|
enforce_security_policy_callback_map_[remote] = {policy, std::move(result_callback)};
|
|
|
|
switch (policy) {
|
|
case l2cap::classic::SecurityPolicy::BEST:
|
|
case l2cap::classic::SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT:
|
|
// Force MITM requirement locally
|
|
authentication_requirements = hci::AuthenticationRequirements::GENERAL_BONDING_MITM_PROTECTION;
|
|
break;
|
|
case l2cap::classic::SecurityPolicy::ENCRYPTED_TRANSPORT:
|
|
authentication_requirements = hci::AuthenticationRequirements::GENERAL_BONDING;
|
|
break;
|
|
default:
|
|
// I could hear the voice of Myles, "This should be an ASSERT!"
|
|
ASSERT_LOG(false, "Unreachable code path");
|
|
return;
|
|
}
|
|
|
|
LOG_WARN("Dispatch #3");
|
|
DispatchPairingHandler(
|
|
record,
|
|
true,
|
|
this->local_io_capability_,
|
|
std::as_const(authentication_requirements),
|
|
pairing::OobData(),
|
|
pairing::OobData());
|
|
}
|
|
|
|
void SecurityManagerImpl::UpdateLinkSecurityCondition(hci::AddressWithType remote) {
|
|
auto entry = enforce_security_policy_callback_map_.find(remote);
|
|
if (entry == enforce_security_policy_callback_map_.end()) {
|
|
LOG_ERROR("No L2CAP security policy callback pending for %s", remote.ToString().c_str());
|
|
return;
|
|
}
|
|
std::move(entry->second.callback_).Invoke(IsSecurityRequirementSatisfied(remote, entry->second.policy_));
|
|
enforce_security_policy_callback_map_.erase(entry);
|
|
}
|
|
|
|
bool SecurityManagerImpl::IsSecurityRequirementSatisfied(
|
|
hci::AddressWithType remote, l2cap::classic::SecurityPolicy policy) {
|
|
auto record = security_database_.FindOrCreate(remote);
|
|
switch (policy) {
|
|
case l2cap::classic::SecurityPolicy::BEST:
|
|
case l2cap::classic::SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT:
|
|
return (record->IsPaired() && record->IsAuthenticated());
|
|
case l2cap::classic::SecurityPolicy::ENCRYPTED_TRANSPORT:
|
|
return record->IsPaired();
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::EnforceSecurityPolicy(
|
|
hci::AddressWithType remote,
|
|
l2cap::classic::SecurityPolicy policy,
|
|
l2cap::classic::SecurityEnforcementInterface::ResultCallback result_callback) {
|
|
LOG_INFO("Attempting to enforce security policy");
|
|
auto record = security_database_.FindOrCreate(remote);
|
|
if (!record->IsPairing()) {
|
|
this->InternalEnforceSecurityPolicy(remote, policy, std::move(result_callback));
|
|
}
|
|
}
|
|
|
|
void SecurityManagerImpl::EnforceLeSecurityPolicy(
|
|
hci::AddressWithType remote, l2cap::le::SecurityPolicy policy,
|
|
l2cap::le::SecurityEnforcementInterface::ResultCallback result_callback) {
|
|
bool result = false;
|
|
// TODO(jpawlowski): Implement for LE
|
|
switch (policy) {
|
|
case l2cap::le::SecurityPolicy::BEST:
|
|
break;
|
|
case l2cap::le::SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT:
|
|
break;
|
|
case l2cap::le::SecurityPolicy::ENCRYPTED_TRANSPORT:
|
|
break;
|
|
case l2cap::le::SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK:
|
|
result = true;
|
|
break;
|
|
case l2cap::le::SecurityPolicy::_NOT_FOR_YOU__AUTHENTICATED_PAIRING_WITH_128_BIT_KEY:
|
|
break;
|
|
case l2cap::le::SecurityPolicy::_NOT_FOR_YOU__AUTHORIZATION:
|
|
break;
|
|
}
|
|
result_callback.Invoke(result);
|
|
}
|
|
} // namespace internal
|
|
} // namespace security
|
|
} // namespace bluetooth
|