summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/direct_bt/L2CAPComm.hpp20
-rw-r--r--api/direct_bt/SMPHandler.hpp6
-rw-r--r--src/direct_bt/L2CAPComm.cpp98
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) {