| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2012 Google Inc. |
| * |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #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" |
| |
| /* |
| * 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; |
| |
| 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 (name != NULL && 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 */ |
| if (attempt > 1) |
| return 0; |
| memcpy(pinbuf, "0000", 4); |
| return 4; |
| } |
| break; |
| |
| case 0x05: /* Peripheral */ |
| switch ((class & 0xc0) >> 6) { |
| 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; |
| |
| snprintf(pinstr, sizeof(pinstr), "%06u", |
| rand() % 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) |