Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
bluetoothmanagement.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:trusted-data
5#include <QtCore/qloggingcategory.h>
6#include <QtCore/qsocketnotifier.h>
7#include <QtCore/qtimer.h>
8
10#include "bluez_data_p.h"
11#include "../qbluetoothsocketbase_p.h"
12
13#include <unistd.h>
14#include <sys/prctl.h>
15#include <sys/syscall.h>
16#include <sys/types.h>
17#include <linux/capability.h>
18
19#include <cerrno>
20
21QT_BEGIN_NAMESPACE
22
23// Packet data structures for Mgmt API bluez.git/doc/mgmt-api.txt
24
25struct MgmtHdr {
26 quint16 cmdCode;
27 quint16 controllerIndex;
28 quint16 length;
29} __attribute__((packed));
30
39
40
41/*
42 * This class encapsulates access to the Bluetooth Management API as introduced by
43 * Linux kernel 3.4. Some Bluetooth information is not exposed via the usual DBus
44 * API (e.g. the random/public address type info). In those cases we have to fall back
45 * to this mgmt API.
46 *
47 * Note that opening such a Bluetooth mgmt socket requires CAP_NET_ADMIN (root) capability.
48 *
49 * Documentation can be found in bluez-git/doc/mgmt-api.txt
50 */
51
52Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
53
54// These structs and defines come straight from linux/capability.h.
55// To avoid missing definitions we re-define them if not existing.
56// In addition, we don't want to pull in a libcap2 dependency
57struct capHdr {
59 int pid;
60};
61
67
69#define _LINUX_CAPABILITY_VERSION_3 0x20080522
70#endif
71
73#define _LINUX_CAPABILITY_U32S_3 2
74#endif
75
76#ifndef CAP_NET_ADMIN
77#define CAP_NET_ADMIN 12
78#endif
79
80#ifndef CAP_TO_INDEX
81#define CAP_TO_INDEX(x) ((x) >> 5) /* 1 << 5 == bits in __u32 */
82#endif
83
84#ifndef CAP_TO_MASK
85#define CAP_TO_MASK(x) (1 << ((x) & 31)) /* mask for indexed __u32 */
86#endif
87
88const int msecInADay = 1000*60*60*24;
89
90static int sysCallCapGet(capHdr *header, capData *data)
91{
92 return syscall(__NR_capget, header, data);
93}
94
95/*
96 * Checks that the current process has the effective CAP_NET_ADMIN permission.
97 */
99{
100 // We only care for cap version 3 introduced by kernel 2.6.26
101 // because the new BlueZ management API only exists since kernel 3.4.
102
103 struct capHdr header = {};
104 struct capData data[_LINUX_CAPABILITY_U32S_3] = {{}};
105 header.version = _LINUX_CAPABILITY_VERSION_3;
106 header.pid = getpid();
107
108 if (sysCallCapGet(&header, data) < 0) {
109 qCWarning(QT_BT_BLUEZ, "BluetoothManangement: getCap failed with %s",
110 qPrintable(qt_error_string(errno)));
111 return false;
112 }
113
114 return (data[CAP_TO_INDEX(CAP_NET_ADMIN)].effective & CAP_TO_MASK(CAP_NET_ADMIN));
115}
116
117BluetoothManagement::BluetoothManagement(QObject *parent) : QObject(parent)
118{
119 bool hasPermission = hasBtMgmtPermission();
120 if (!hasPermission) {
121 qCInfo(QT_BT_BLUEZ, "Missing CAP_NET_ADMIN permission. Cannot determine whether "
122 "a found address is of random or public type.");
123 return;
124 }
125
126 sockaddr_hci hciAddr;
127
128 fd = ::socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI);
129 if (fd < 0) {
130 qCWarning(QT_BT_BLUEZ, "Cannot open Bluetooth Management socket: %s",
131 qPrintable(qt_error_string(errno)));
132 return;
133 }
134
135 memset(&hciAddr, 0, sizeof(hciAddr));
136 hciAddr.hci_dev = HCI_DEV_NONE;
138 hciAddr.hci_family = AF_BLUETOOTH;
139
140 if (::bind(fd, (struct sockaddr *)(&hciAddr), sizeof(hciAddr)) < 0) {
141 qCWarning(QT_BT_BLUEZ, "Cannot bind Bluetooth Management socket: %s",
142 qPrintable(qt_error_string(errno)));
143 ::close(fd);
144 fd = -1;
145 return;
146 }
147
148 notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
149 connect(notifier, &QSocketNotifier::activated, this, &BluetoothManagement::_q_readNotifier);
150
151 // ensure cache is regularly cleaned (once every 24h)
152 QTimer* timer = new QTimer(this);
153 timer->setInterval(msecInADay);
154 timer->setTimerType(Qt::VeryCoarseTimer);
155 connect(timer, &QTimer::timeout, this, &BluetoothManagement::cleanupOldAddressFlags);
156 timer->start();
157}
158
159Q_GLOBAL_STATIC(BluetoothManagement, bluetoothKernelManager)
160
162{
163 return bluetoothKernelManager();
164}
165
166void BluetoothManagement::_q_readNotifier()
167{
168 char *dst = buffer.reserve(QPRIVATELINEARBUFFER_BUFFERSIZE);
169 const auto readCount = ::read(fd, dst, QPRIVATELINEARBUFFER_BUFFERSIZE);
170 buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE - (readCount < 0 ? 0 : readCount));
171 if (readCount < 0) {
172 qCWarning(QT_BT_BLUEZ, "Management Control read error %s", qPrintable(qt_error_string(errno)));
173 return;
174 }
175
176 // do we have at least one complete mgmt header?
177 if (size_t(buffer.size()) < sizeof(MgmtHdr))
178 return;
179
180 QByteArray data = buffer.readAll();
181
182 while (true) {
183 if (size_t(data.size()) < sizeof(MgmtHdr))
184 break;
185
186 const MgmtHdr *hdr = reinterpret_cast<const MgmtHdr*>(data.constData());
187 const auto nextPackageSize = qsizetype(qFromLittleEndian(hdr->length) + sizeof(MgmtHdr));
188 const qsizetype remainingPackageSize = data.size() - nextPackageSize;
189
190 if (data.size() < nextPackageSize)
191 break; // not a complete event header -> wait for next notifier
192
193 switch (static_cast<EventCode>(qFromLittleEndian(hdr->cmdCode))) {
194 case EventCode::DeviceFoundEvent:
195 {
196 const MgmtEventDeviceFound *event = reinterpret_cast<const MgmtEventDeviceFound*>
197 (data.constData() + sizeof(MgmtHdr));
198
199 if (event->type == BDADDR_LE_RANDOM) {
200 const bdaddr_t address = event->bdaddr;
201 quint64 bdaddr;
202
203 convertAddress(address.b, &bdaddr);
204 const QBluetoothAddress qtAddress(bdaddr);
205 qCDebug(QT_BT_BLUEZ) << "BluetoothManagement: found random device"
206 << qtAddress;
207 processRandomAddressFlagInformation(qtAddress);
208 }
209
210 break;
211 }
212 default:
213 qCDebug(QT_BT_BLUEZ) << "BluetoothManagement: Ignored event:"
214 << Qt::hex << (EventCode)qFromLittleEndian(hdr->cmdCode);
215 break;
216 }
217
218 if (data.size() > nextPackageSize)
219 data = data.right(remainingPackageSize);
220 else
221 data.clear();
222
223 if (data.isEmpty())
224 break;
225 }
226
227 if (!data.isEmpty())
228 buffer.ungetBlock(data.constData(), data.size());
229}
230
231void BluetoothManagement::processRandomAddressFlagInformation(const QBluetoothAddress &address)
232{
233 // insert or update
234 QMutexLocker locker(&accessLock);
235 privateFlagAddresses[address] = QDateTime::currentDateTimeUtc();
236}
237
238/*
239 * Ensure that private address cache is not older than 24h.
240 */
241void BluetoothManagement::cleanupOldAddressFlags()
242{
243 const auto cutOffTime = QDateTime::currentDateTimeUtc().addDays(-1);
244
245 QMutexLocker locker(&accessLock);
246
247 auto i = privateFlagAddresses.begin();
248 while (i != privateFlagAddresses.end()) {
249 if (i.value() < cutOffTime)
250 i = privateFlagAddresses.erase(i);
251 else
252 i++;
253 }
254}
255
256bool BluetoothManagement::isAddressRandom(const QBluetoothAddress &address) const
257{
258 if (fd == -1 || address.isNull())
259 return false;
260
261 QMutexLocker locker(&accessLock);
262 return privateFlagAddresses.contains(address);
263}
264
266{
267 return (fd == -1) ? false : true;
268}
269
270
271QT_END_NAMESPACE
272
273#include "moc_bluetoothmanagement_p.cpp"
#define _LINUX_CAPABILITY_U32S_3
#define CAP_TO_INDEX(x)
#define _LINUX_CAPABILITY_VERSION_3
#define CAP_TO_MASK(x)
static int sysCallCapGet(capHdr *header, capData *data)
#define CAP_NET_ADMIN
const int msecInADay
static bool hasBtMgmtPermission()
#define QPRIVATELINEARBUFFER_BUFFERSIZE
#define HCI_CHANNEL_CONTROL
#define BTPROTO_HCI
#define HCI_DEV_NONE
#define BDADDR_LE_RANDOM
bool isAddressRandom(const QBluetoothAddress &address) const
static BluetoothManagement * instance()
unsigned short hci_dev
unsigned short hci_channel