diff options
-rw-r--r-- | api/direct_bt/L2CAPComm.hpp | 20 | ||||
-rw-r--r-- | api/direct_bt/SMPHandler.hpp | 6 | ||||
-rw-r--r-- | src/direct_bt/L2CAPComm.cpp | 98 |
3 files changed, 99 insertions, 25 deletions
diff --git a/api/direct_bt/L2CAPComm.hpp b/api/direct_bt/L2CAPComm.hpp index 879c8247..9ff2f069 100644 --- a/api/direct_bt/L2CAPComm.hpp +++ b/api/direct_bt/L2CAPComm.hpp @@ -142,6 +142,9 @@ namespace direct_bt { std::atomic<pthread_t> tid_connect; std::atomic<pthread_t> tid_read; + bool setBTSecurityLevelImpl(const BTSecurityLevel sec_level); + bool getBTSecurityLevelImpl(BTSecurityLevel& sec_level); + public: /** * Constructing a non connected L2CAP channel instance for the pre-defined PSM and CID. @@ -161,9 +164,10 @@ namespace direct_bt { * </p> * * @param device the remote device to establish this L2CAP connection + * @param sec_level sec_level < BTSecurityLevel::NONE will not set security level * @return true if connection has been established, otherwise false */ - bool open(const DBTDevice& device); + bool open(const DBTDevice& device, const BTSecurityLevel sec_level=BTSecurityLevel::NONE); bool isOpen() const { return is_open; } @@ -180,16 +184,24 @@ namespace direct_bt { std::recursive_mutex & mutex_write() { return mtx_write; } /** - * If sec_level > BTSecurityLevel::NONE, sets the BlueZ's L2CAP socket BT_SECURITY sec_level, determining the SMP security mode per connection. + * If sec_level > BTSecurityLevel::UNSET, sets the BlueZ's L2CAP socket BT_SECURITY sec_level, determining the SMP security mode per connection. * <p> * To unset security, the L2CAP socket should be closed and opened again. * </p> * - * @param sec_level sec_level <= BTSecurityLevel::NONE will not set security level and returns false. - * @return true if a security level > BTSecurityLevel::NONE has been set successfully, false if no security level has been set or if it failed. + * @param sec_level sec_level < BTSecurityLevel::NONE will not set security level and returns false. + * @return true if a security level > BTSecurityLevel::UNSET has been set successfully, false if no security level has been set or if it failed. */ bool setBTSecurityLevel(const BTSecurityLevel sec_level); + /** + * Fetches the current BlueZ's L2CAP socket BT_SECURITY sec_level. + * + * @param sec_level return value reference written to if method returns true + * @return true if successful with result written to sec_level, otherwise false. + */ + bool getBTSecurityLevel(BTSecurityLevel& sec_level); + /** Generic read, w/o locking suitable for a unique ringbuffer sink. Using L2CAPEnv::L2CAP_READER_POLL_TIMEOUT.*/ jau::snsize_t read(uint8_t* buffer, const jau::nsize_t capacity); diff --git a/api/direct_bt/SMPHandler.hpp b/api/direct_bt/SMPHandler.hpp index 6f16eaae..891547ea 100644 --- a/api/direct_bt/SMPHandler.hpp +++ b/api/direct_bt/SMPHandler.hpp @@ -216,10 +216,10 @@ namespace direct_bt { std::string getStateString() const noexcept { return L2CAPComm::getStateString(is_connected, has_ioerror); } /** - * If sec_level > BTSecurityLevel::NONE, establish security level per L2CAP connection. + * If sec_level > BTSecurityLevel::UNSET, change security level per L2CAP connection. * - * @param sec_level sec_level <= BTSecurityLevel::NONE will not set security level and returns false. - * @return true if a security level > BTSecurityLevel::NONE has been set successfully, false if no security level has been set or if it failed. + * @param sec_level sec_level < BTSecurityLevel::NONE will not set security level and returns false. + * @return true if a security level > BTSecurityLevel::UNSET has been set successfully, false if no security level has been set or if it failed. */ bool establishSecurity(const BTSecurityLevel sec_level); diff --git a/src/direct_bt/L2CAPComm.cpp b/src/direct_bt/L2CAPComm.cpp index 3eeae8a6..049acfe7 100644 --- a/src/direct_bt/L2CAPComm.cpp +++ b/src/direct_bt/L2CAPComm.cpp @@ -116,7 +116,8 @@ L2CAPComm::L2CAPComm(const EUI48& adapterAddress_, const uint16_t psm_, const ui is_open(false), has_ioerror(false), interrupt_flag(false), tid_connect(0), tid_read(0) { } -bool L2CAPComm::open(const DBTDevice& device) { + +bool L2CAPComm::open(const DBTDevice& device, const BTSecurityLevel sec_level) { bool expOpen = false; // C++11, exp as value since C++20 if( !is_open.compare_exchange_strong(expOpen, true) ) { @@ -144,6 +145,14 @@ bool L2CAPComm::open(const DBTDevice& device) { goto failure; // open failed } +#if USE_LINUX_BT_SECURITY + if( BTSecurityLevel::UNSET < sec_level ) { + if( !setBTSecurityLevelImpl(sec_level) ) { + goto failure; // sec_level failed + } + } +#endif + tid_connect = pthread_self(); // temporary safe tid to allow interruption // actual request to connect to remote device @@ -246,25 +255,78 @@ bool L2CAPComm::setBTSecurityLevel(const BTSecurityLevel sec_level) { } const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor - if( BTSecurityLevel::NONE < sec_level ) { - #if USE_LINUX_BT_SECURITY - struct bt_security bt_sec; - int result; - - bzero(&bt_sec, sizeof(bt_sec)); - bt_sec.level = direct_bt::number(sec_level); - result = setsockopt(socket_descriptor, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, sizeof(bt_sec)); - if ( 0 == result ) { - DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s, success", getBTSecurityLevelString(sec_level).c_str()); - return true; - } else { - ERR_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s, failed", getBTSecurityLevelString(sec_level).c_str()); - } - #else - DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s, not implemented", getBTSecurityLevelString(sec_level).c_str()); - #endif + return setBTSecurityLevelImpl(sec_level); +} + +bool L2CAPComm::setBTSecurityLevelImpl(const BTSecurityLevel sec_level) { + if( BTSecurityLevel::NONE > sec_level ) { + DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s, not set", getBTSecurityLevelString(sec_level).c_str()); + return false; + } + +#if USE_LINUX_BT_SECURITY + struct bt_security bt_sec; + int result; + + BTSecurityLevel old_sec_level = BTSecurityLevel::UNSET; + if( jau::environment::get().debug ) { + getBTSecurityLevelImpl(old_sec_level); + } + bzero(&bt_sec, sizeof(bt_sec)); + bt_sec.level = direct_bt::number(sec_level); + result = setsockopt(socket_descriptor, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, sizeof(bt_sec)); + if ( 0 == result ) { + DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s -> %s, success", + getBTSecurityLevelString(old_sec_level).c_str(), getBTSecurityLevelString(sec_level).c_str()); + return true; + } else { + ERR_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s -> %s, failed", + getBTSecurityLevelString(old_sec_level).c_str(), getBTSecurityLevelString(sec_level).c_str()); + return false; + } +#else + DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s, not implemented", getBTSecurityLevelString(sec_level).c_str()); + return false; +#endif +} + +bool L2CAPComm::getBTSecurityLevel(BTSecurityLevel & sec_level) { + if( !is_open ) { + DBG_PRINT("L2CAPComm::getBTSecurityLevel: Not connected: %s, dd %d, %s, psm %u, cid %u", + getStateString().c_str(), socket_descriptor.load(), deviceString.c_str(), psm, cid); + return false; + } + const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor + + return getBTSecurityLevelImpl(sec_level); +} + +bool L2CAPComm::getBTSecurityLevelImpl(BTSecurityLevel & sec_level) { +#if USE_LINUX_BT_SECURITY + struct bt_security bt_sec; + socklen_t optlen = sizeof(bt_sec); + int result; + + bzero(&bt_sec, sizeof(bt_sec)); + result = getsockopt(socket_descriptor, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, &optlen); + if ( 0 == result ) { + if( optlen == sizeof(bt_sec) ) { + sec_level = static_cast<BTSecurityLevel>(bt_sec.level); + DBG_PRINT("L2CAPComm::getBTSecurityLevel: sec_level %s, success", getBTSecurityLevelString(sec_level).c_str()); + return true; + } else { + ERR_PRINT("L2CAPComm::getBTSecurityLevel: sec_level %s, failed. Returned size %zd != %zd ", + getBTSecurityLevelString(sec_level).c_str(), optlen, sizeof(bt_sec)); + return false; + } + } else { + ERR_PRINT("L2CAPComm::getBTSecurityLevel: sec_level %s, failed. Result %d", getBTSecurityLevelString(sec_level).c_str(), result); + return false; } +#else + DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s, not implemented", getBTSecurityLevelString(sec_level).c_str()); return false; +#endif } jau::snsize_t L2CAPComm::read(uint8_t* buffer, const jau::nsize_t capacity) { |