/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "log.h" #include "adapter.h" #include "manager.h" #include "device.h" #include "error.h" #include "dbus-common.h" #include "agent.h" #include "storage.h" #include "event.h" static gboolean get_adapter_and_device(bdaddr_t *src, bdaddr_t *dst, struct btd_adapter **adapter, struct btd_device **device, gboolean create) { DBusConnection *conn = get_dbus_connection(); char peer_addr[18]; *adapter = manager_find_adapter(src); if (!*adapter) { error("Unable to find matching adapter"); return FALSE; } ba2str(dst, peer_addr); if (create) *device = adapter_get_device(conn, *adapter, peer_addr); else *device = adapter_find_device(*adapter, peer_addr); if (create && !*device) { error("Unable to get device object!"); return FALSE; } return TRUE; } /***************************************************************** * * Section reserved to HCI commands confirmation handling and low * level events(eg: device attached/dettached. * *****************************************************************/ static void pincode_cb(struct agent *agent, DBusError *derr, const char *pincode, struct btd_device *device) { struct btd_adapter *adapter = device_get_adapter(device); bdaddr_t dba; int err; device_get_address(device, &dba, NULL); if (derr) { err = btd_adapter_pincode_reply(adapter, &dba, NULL, 0); if (err < 0) goto fail; return; } err = btd_adapter_pincode_reply(adapter, &dba, pincode, pincode ? strlen(pincode) : 0); if (err < 0) goto fail; return; fail: error("Sending PIN code reply failed: %s (%d)", strerror(-err), -err); } int btd_event_request_pin(bdaddr_t *sba, bdaddr_t *dba, gboolean secure) { struct btd_adapter *adapter; struct btd_device *device; char pin[17]; ssize_t pinlen; gboolean display = FALSE; if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE)) return -ENODEV; memset(pin, 0, sizeof(pin)); pinlen = btd_adapter_get_pin(adapter, device, pin, &display); if (pinlen > 0 && (!secure || pinlen == 16)) { if (display && device_is_bonding(device, NULL)) return device_request_authentication(device, AUTH_TYPE_NOTIFY_PINCODE, pin, secure, pincode_cb); btd_adapter_pincode_reply(adapter, dba, pin, pinlen); return 0; } return device_request_authentication(device, AUTH_TYPE_PINCODE, NULL, secure, pincode_cb); } static int confirm_reply(struct btd_adapter *adapter, struct btd_device *device, gboolean success) { bdaddr_t bdaddr; uint8_t bdaddr_type; device_get_address(device, &bdaddr, &bdaddr_type); return btd_adapter_confirm_reply(adapter, &bdaddr, bdaddr_type, success); } static void confirm_cb(struct agent *agent, DBusError *err, void *user_data) { struct btd_device *device = user_data; struct btd_adapter *adapter = device_get_adapter(device); gboolean success = (err == NULL) ? TRUE : FALSE; confirm_reply(adapter, device, success); } static void passkey_cb(struct agent *agent, DBusError *err, uint32_t passkey, void *user_data) { struct btd_device *device = user_data; struct btd_adapter *adapter = device_get_adapter(device); bdaddr_t bdaddr; uint8_t bdaddr_type; device_get_address(device, &bdaddr, &bdaddr_type); if (err) passkey = INVALID_PASSKEY; btd_adapter_passkey_reply(adapter, &bdaddr, bdaddr_type, passkey); } int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey) { struct btd_adapter *adapter; struct btd_device *device; if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE)) return -ENODEV; return device_request_authentication(device, AUTH_TYPE_CONFIRM, &passkey, FALSE, confirm_cb); } int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba) { struct btd_adapter *adapter; struct btd_device *device; if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE)) return -ENODEV; return device_request_authentication(device, AUTH_TYPE_PASSKEY, NULL, FALSE, passkey_cb); } int btd_event_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey) { struct btd_adapter *adapter; struct btd_device *device; if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE)) return -ENODEV; return device_request_authentication(device, AUTH_TYPE_NOTIFY_PASSKEY, &passkey, FALSE, NULL); } void btd_event_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t status) { struct btd_adapter *adapter; struct btd_device *device; gboolean create; DBG("status=%02x", status); create = status ? FALSE : TRUE; if (!get_adapter_and_device(local, peer, &adapter, &device, create)) return; if (!device) return; device_simple_pairing_complete(device, status); } static void update_lastseen(bdaddr_t *sba, bdaddr_t *dba) { time_t t; struct tm *tm; t = time(NULL); tm = gmtime(&t); write_lastseen_info(sba, dba, tm); } static void update_lastused(bdaddr_t *sba, bdaddr_t *dba) { time_t t; struct tm *tm; t = time(NULL); tm = gmtime(&t); write_lastused_info(sba, dba, tm); } void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type, int8_t rssi, uint8_t confirm_name, uint8_t *data, uint8_t data_len) { struct btd_adapter *adapter; adapter = manager_find_adapter(local); if (!adapter) { error("No matching adapter found"); return; } update_lastseen(local, peer); if (data) write_remote_eir(local, peer, data, data_len); adapter_update_found_devices(adapter, peer, bdaddr_type, rssi, confirm_name, data, data_len); } void btd_event_set_legacy_pairing(bdaddr_t *local, bdaddr_t *peer, gboolean legacy) { struct btd_adapter *adapter; struct remote_dev_info *dev; adapter = manager_find_adapter(local); if (!adapter) { error("No matching adapter found"); return; } dev = adapter_search_found_devices(adapter, peer); if (dev) dev->legacy = legacy; } void btd_event_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class) { struct btd_adapter *adapter; struct btd_device *device; uint32_t old_class = 0; read_remote_class(local, peer, &old_class); if (old_class == class) return; write_remote_class(local, peer, class); if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE)) return; if (!device) return; device_set_class(device, class); } void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, char *name) { struct btd_adapter *adapter; struct btd_device *device; struct remote_dev_info *dev_info; if (!g_utf8_validate(name, -1, NULL)) { int i; /* Assume ASCII, and replace all non-ASCII with spaces */ for (i = 0; name[i] != '\0'; i++) { if (!isascii(name[i])) name[i] = ' '; } /* Remove leading and trailing whitespace characters */ g_strstrip(name); } write_device_name(local, peer, name); if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE)) return; dev_info = adapter_search_found_devices(adapter, peer); if (dev_info) { g_free(dev_info->name); dev_info->name = g_strdup(name); adapter_emit_device_found(adapter, dev_info); } if (device) device_set_name(device, name); } static char *buf2str(uint8_t *data, int datalen) { char *buf; int i; buf = g_try_new0(char, (datalen * 2) + 1); if (buf == NULL) return NULL; for (i = 0; i < datalen; i++) sprintf(buf + (i * 2), "%2.2x", data[i]); return buf; } static int store_longtermkey(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type, unsigned char *key, uint8_t master, uint8_t authenticated, uint8_t enc_size, uint16_t ediv, uint8_t rand[8]) { GString *newkey; char *val, *str; int err; val = buf2str(key, 16); if (val == NULL) return -ENOMEM; newkey = g_string_new(val); g_free(val); g_string_append_printf(newkey, " %d %d %d %d ", authenticated, master, enc_size, ediv); str = buf2str(rand, 8); if (str == NULL) { g_string_free(newkey, TRUE); return -ENOMEM; } newkey = g_string_append(newkey, str); g_free(str); err = write_longtermkeys(local, peer, bdaddr_type, newkey->str); g_string_free(newkey, TRUE); return err; } int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t *key, uint8_t key_type, uint8_t pin_length) { struct btd_adapter *adapter; struct btd_device *device; int ret; if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE)) return -ENODEV; DBG("storing link key of type 0x%02x", key_type); ret = write_link_key(local, peer, key, key_type, pin_length); if (ret == 0) { device_set_bonded(device, TRUE); if (device_is_temporary(device)) device_set_temporary(device, FALSE); } return ret; } int btd_event_ltk_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type, uint8_t *key, uint8_t master, uint8_t authenticated, uint8_t enc_size, uint16_t ediv, uint8_t rand[8]) { struct btd_adapter *adapter; struct btd_device *device; int ret; if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE)) return -ENODEV; ret = store_longtermkey(local, peer, bdaddr_type, key, master, authenticated, enc_size, ediv, rand); if (ret == 0) { device_set_bonded(device, TRUE); if (device_is_temporary(device)) device_set_temporary(device, FALSE); } return ret; } void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type, char *name, uint8_t *dev_class) { struct btd_adapter *adapter; struct btd_device *device; if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE)) return; update_lastused(local, peer); if (dev_class != NULL) { uint32_t class = dev_class[0] | (dev_class[1] << 8) | (dev_class[2] << 16); if (class != 0) write_remote_class(local, peer, class); } device_set_addr_type(device, bdaddr_type); adapter_add_connection(adapter, device); if (name != NULL) btd_event_remote_name(local, peer, name); } void btd_event_conn_failed(bdaddr_t *local, bdaddr_t *peer, uint8_t status) { struct btd_adapter *adapter; struct btd_device *device; DBusConnection *conn = get_dbus_connection(); DBG("status 0x%02x", status); if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE)) return; if (!device) return; if (device_is_bonding(device, NULL)) device_cancel_bonding(device, status); if (device_is_temporary(device)) adapter_remove_device(conn, adapter, device, TRUE); } void btd_event_disconn_complete(bdaddr_t *local, bdaddr_t *peer) { struct btd_adapter *adapter; struct btd_device *device; DBG(""); if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE)) return; if (!device) return; adapter_remove_connection(adapter, device); } void btd_event_device_blocked(bdaddr_t *local, bdaddr_t *peer) { struct btd_adapter *adapter; struct btd_device *device; DBusConnection *conn = get_dbus_connection(); if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE)) return; if (device) device_block(conn, device, TRUE); } void btd_event_device_unblocked(bdaddr_t *local, bdaddr_t *peer) { struct btd_adapter *adapter; struct btd_device *device; DBusConnection *conn = get_dbus_connection(); if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE)) return; if (device) device_unblock(conn, device, FALSE, TRUE); } void btd_event_device_unpaired(bdaddr_t *local, bdaddr_t *peer) { struct btd_adapter *adapter; struct btd_device *device; DBusConnection *conn = get_dbus_connection(); if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE)) return; device_set_temporary(device, TRUE); if (device_is_connected(device)) device_request_disconnect(device, NULL); else adapter_remove_device(conn, adapter, device, TRUE); } /* Section reserved to device HCI callbacks */ void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer) { struct btd_adapter *adapter; struct btd_device *device; if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE)) return; device_set_paired(device, TRUE); }