1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2012 Google Inc.
*
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <glib.h>
#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/log.h"
#include "src/storage.h"
#include "src/shared/util.h"
/*
* Nintendo Wii Remote devices require the bdaddr of the host as pin input for
* authentication. This plugin registers a pin-callback and forces this pin
* to be used for authentication.
*
* There are two ways to place the wiimote into discoverable mode.
* - Pressing the red-sync button on the back of the wiimote. This module
* supports pairing via this method. Auto-reconnect should be possible after
* the device was paired once.
* - Pressing the 1+2 buttons on the front of the wiimote. This module does
* not support this method since this method never enables auto-reconnect.
* Hence, pairing is not needed. Use it without pairing if you want.
* After connecting the wiimote you should immediately connect to the input
* service of the wiimote. If you don't, the wiimote will close the connection.
* The wiimote waits about 5 seconds until it turns off again.
* Auto-reconnect is only enabled when pairing with the wiimote via the red
* sync-button and then connecting to the input service. If you do not connect
* to the input service, then auto-reconnect is not enabled.
* If enabled, the wiimote connects to the host automatically when any button
* is pressed.
*/
static uint16_t wii_ids[][2] = {
{ 0x057e, 0x0306 }, /* 1st gen */
{ 0x054c, 0x0306 }, /* LEGO wiimote */
{ 0x057e, 0x0330 }, /* 2nd gen */
};
static const char *wii_names[] = {
"Nintendo RVL-CNT-01", /* 1st gen */
"Nintendo RVL-CNT-01-TR", /* 2nd gen */
"Nintendo RVL-CNT-01-UC", /* Wii U Pro Controller */
"Nintendo RVL-WBC-01", /* Balance Board */
};
static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device,
char *pinbuf, bool *display,
unsigned int attempt)
{
uint16_t vendor, product;
char addr[18], name[25];
unsigned int i;
/* Only try the pin code once per device. If it's not correct then it's
* an unknown device.
*/
if (attempt > 1)
return 0;
ba2str(device_get_address(device), addr);
vendor = btd_device_get_vendor(device);
product = btd_device_get_product(device);
device_get_name(device, name, sizeof(name));
for (i = 0; i < G_N_ELEMENTS(wii_ids); ++i) {
if (vendor == wii_ids[i][0] && product == wii_ids[i][1])
goto found;
}
for (i = 0; i < G_N_ELEMENTS(wii_names); ++i) {
if (g_str_equal(name, wii_names[i]))
goto found;
}
return 0;
found:
DBG("Forcing fixed pin on detected wiimote %s", addr);
memcpy(pinbuf, btd_adapter_get_address(adapter), 6);
return 6;
}
/*
* Plugin to handle automatic pairing of devices with reduced user
* interaction, including implementing the recommendation of the HID spec
* for keyboard devices.
*
* The plugin works by intercepting the PIN request for devices; if the
* device is a keyboard a random six-digit numeric PIN is generated and
* returned, flagged for displaying using DisplayPinCode.
*
*/
static ssize_t autopair_pincb(struct btd_adapter *adapter,
struct btd_device *device,
char *pinbuf, bool *display,
unsigned int attempt)
{
char addr[18];
char pinstr[7];
char name[25];
uint32_t class;
uint32_t val;
ssize_t ret;
/* Try with the wii_pincb first */
ret = wii_pincb(adapter, device, pinbuf, display, attempt);
if (ret > 0)
return ret;
ba2str(device_get_address(device), addr);
class = btd_device_get_class(device);
device_get_name(device, name, sizeof(name));
DBG("device '%s' (%s) class: 0x%x vid/pid: 0x%X/0x%X",
name, addr, class,
btd_device_get_vendor (device),
btd_device_get_product (device));
/* The iCade shouldn't use random PINs like normal keyboards */
if (strstr(name, "iCade") != NULL)
return 0;
/* This is a class-based pincode guesser. Ignore devices with an
* unknown class.
*/
if (class == 0)
return 0;
switch ((class & 0x1f00) >> 8) {
case 0x04: /* Audio/Video */
switch ((class & 0xfc) >> 2) {
case 0x01: /* Wearable Headset Device */
case 0x02: /* Hands-free Device */
case 0x06: /* Headphones */
case 0x07: /* Portable Audio */
case 0x0a: /* HiFi Audio Device */
{
const char *pincodes[] = {
"0000",
"1234",
"1111"
};
const char *pincode;
if (attempt > G_N_ELEMENTS(pincodes))
return 0;
pincode = pincodes[attempt - 1];
memcpy(pinbuf, pincode, strlen(pincode));
return strlen(pincode);
}
}
break;
case 0x05: /* Peripheral */
switch ((class & 0xc0) >> 6) {
case 0x00:
switch ((class & 0x1e) >> 2) {
case 0x01: /* Joystick */
case 0x02: /* Gamepad */
case 0x03: /* Remote Control */
if (attempt > 1)
return 0;
memcpy(pinbuf, "0000", 4);
return 4;
}
break;
case 0x01: /* Keyboard */
case 0x03: /* Combo keyboard/pointing device */
/* For keyboards rejecting the first random code
* in less than 500ms, try a fixed code. */
if (attempt > 1 &&
device_bonding_last_duration(device) < 500) {
/* Don't try more than one dumb code */
if (attempt > 2)
return 0;
/* Try "0000" as the code for the second
* attempt. */
memcpy(pinbuf, "0000", 4);
return 4;
}
/* Never try more than 3 random pincodes. */
if (attempt >= 4)
return 0;
if (util_getrandom(&val, sizeof(val), 0) < 0) {
error("Failed to get a random pincode");
return 0;
}
snprintf(pinstr, sizeof(pinstr), "%06u",
val % 1000000);
*display = true;
memcpy(pinbuf, pinstr, 6);
return 6;
case 0x02: /* Pointing device */
if (attempt > 1)
return 0;
memcpy(pinbuf, "0000", 4);
return 4;
}
break;
case 0x06: /* Imaging */
if (class & 0x80) { /* Printer */
if (attempt > 1)
return 0;
memcpy(pinbuf, "0000", 4);
return 4;
}
break;
}
return 0;
}
static int autopair_probe(struct btd_adapter *adapter)
{
btd_adapter_register_pin_cb(adapter, autopair_pincb);
return 0;
}
static void autopair_remove(struct btd_adapter *adapter)
{
btd_adapter_unregister_pin_cb(adapter, autopair_pincb);
}
static struct btd_adapter_driver autopair_driver = {
.name = "autopair",
.probe = autopair_probe,
.remove = autopair_remove,
};
static int autopair_init(void)
{
/* Initialize the random seed from /dev/urandom */
unsigned int seed;
int fd, err;
ssize_t n;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
err = -errno;
error("Failed to open /dev/urandom: %s (%d)", strerror(-err),
-err);
return err;
}
n = read(fd, &seed, sizeof(seed));
if (n < (ssize_t) sizeof(seed)) {
err = (n == -1) ? -errno : -EIO;
error("Failed to read %zu bytes from /dev/urandom: %s (%d)",
sizeof(seed), strerror(-err), -err);
close(fd);
return err;
}
close(fd);
srand(seed);
return btd_register_adapter_driver(&autopair_driver);
}
static void autopair_exit(void)
{
btd_unregister_adapter_driver(&autopair_driver);
}
BLUETOOTH_PLUGIN_DEFINE(autopair, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
autopair_init, autopair_exit)
|