Implement storing of link keys in runtime memory

The management interface will enable storing link keys in runtime memory
in the kernel. This patch makes the adapter interface match this model
and adds storing of link keys into hciops.
This commit is contained in:
Johan Hedberg 2010-12-18 13:50:10 +02:00
parent 423b80f993
commit b95ef500f7
5 changed files with 186 additions and 50 deletions

View File

@ -90,6 +90,8 @@ static struct dev_info {
GIOChannel *io;
guint watch_id;
GSList *keys;
int pin_length;
} *devs = NULL;
@ -534,12 +536,27 @@ static int hciops_set_did(int index, uint16_t vendor, uint16_t product,
/* Start of HCI event callbacks */
static int get_handle(int index, bdaddr_t *dba, uint16_t *handle)
static int disconnect_handle(int index, uint16_t handle, uint8_t reason)
{
struct dev_info *dev = &devs[index];
disconnect_cp cp;
memset(&cp, 0, sizeof(cp)); cp.handle = htobs(handle);
cp.reason = reason;
if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_DISCONNECT,
DISCONNECT_CP_SIZE, &cp) < 0)
return -errno;
return 0;
}
static int disconnect_addr(int index, bdaddr_t *dba, uint8_t reason)
{
struct dev_info *dev = &devs[index];
struct hci_conn_list_req *cl;
struct hci_conn_info *ci;
int i;
int i, err;
cl = g_malloc0(10 * sizeof(*ci) + sizeof(*cl));
@ -548,21 +565,22 @@ static int get_handle(int index, bdaddr_t *dba, uint16_t *handle)
ci = cl->conn_info;
if (ioctl(dev->sk, HCIGETCONNLIST, (void *) cl) < 0) {
g_free(cl);
return -EIO;
err = -errno;
goto failed;
}
err = -ENOENT;
for (i = 0; i < cl->conn_num; i++, ci++) {
if (bacmp(&ci->bdaddr, dba) == 0) {
*handle = ci->handle;
g_free(cl);
return 0;
err = disconnect_handle(index, ci->handle, reason);
break;
}
}
failed:
g_free(cl);
return -ENOENT;
return err;
}
static inline int get_bdaddr(int index, uint16_t handle, bdaddr_t *dba)
@ -625,6 +643,8 @@ static void link_key_request(int index, bdaddr_t *dba)
struct btd_adapter *adapter;
struct btd_device *device;
struct hci_auth_info_req req;
GSList *match;
struct link_key_info *key_info;
unsigned char key[16];
char da[18];
uint8_t type;
@ -652,11 +672,23 @@ static void link_key_request(int index, bdaddr_t *dba)
DBG("kernel auth requirements = 0x%02x", req.type);
if (main_opts.debug_keys && device &&
device_get_debug_key(device, key))
match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
if (match)
key_info = match->data;
else
key_info = NULL;
DBG("Matching key %s", key_info ? "found" : "not found");
if (key_info) {
memcpy(key, key_info->key, sizeof(key));
type = key_info->type;
} else
type = 0xff;
if (device && device_get_debug_key(device, key))
type = 0x03;
else if (read_link_key(&dev->bdaddr, dba, key, &type) < 0 ||
type == 0x03) {
else if (key_info == NULL || key_info->type == 0x03) {
/* Link key not found */
hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY,
6, dba);
@ -688,44 +720,54 @@ static void link_key_notify(int index, void *ptr)
struct dev_info *dev = &devs[index];
evt_link_key_notify *evt = ptr;
bdaddr_t *dba = &evt->bdaddr;
struct link_key_info *key_info;
GSList *match;
uint8_t old_key_type, reason;
char da[18];
int err;
unsigned char old_key[16];
uint8_t old_key_type;
ba2str(dba, da);
DBG("hci%d dba %s type %d", index, da, evt->key_type);
err = read_link_key(&dev->bdaddr, dba, old_key, &old_key_type);
if (err < 0)
match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
if (match)
key_info = match->data;
else
key_info = NULL;
if (key_info == NULL) {
key_info = g_new0(struct link_key_info, 1);
bacpy(&key_info->bdaddr, &evt->bdaddr);
old_key_type = 0xff;
} else {
dev->keys = g_slist_remove(dev->keys, key_info);
old_key_type = key_info->type;
}
memcpy(key_info->key, evt->link_key, sizeof(evt->link_key));
key_info->type = evt->key_type;
key_info->pin_len = dev->pin_length;
err = btd_event_link_key_notify(&dev->bdaddr, dba, evt->link_key,
evt->key_type, dev->pin_length,
old_key_type);
dev->pin_length = -1;
if (err < 0) {
uint16_t handle;
if (err == -ENODEV)
btd_event_bonding_process_complete(&dev->bdaddr, dba,
HCI_OE_LOW_RESOURCES);
else
btd_event_bonding_process_complete(&dev->bdaddr, dba,
HCI_MEMORY_FULL);
if (get_handle(index, dba, &handle) == 0) {
disconnect_cp cp;
memset(&cp, 0, sizeof(cp));
cp.handle = htobs(handle);
cp.reason = HCI_OE_LOW_RESOURCES;
hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_DISCONNECT,
DISCONNECT_CP_SIZE, &cp);
}
if (err == 0) {
dev->keys = g_slist_append(dev->keys, key_info);
return;
}
g_free(key_info);
if (err == -ENODEV)
reason = HCI_OE_LOW_RESOURCES;
else
reason = HCI_MEMORY_FULL;
btd_event_bonding_process_complete(&dev->bdaddr, dba, reason);
disconnect_addr(index, dba, reason);
}
static void return_link_keys(int index, void *ptr)
@ -1842,16 +1884,23 @@ static inline void le_metaevent(int index, void *ptr)
static void stop_hci_dev(int index)
{
struct dev_info *dev = &devs[index];
GIOChannel *chan = dev->io;
if (!chan)
if (dev->sk < 0)
return;
info("Stopping hci%d event socket", index);
g_source_remove(dev->watch_id);
g_io_channel_unref(dev->io);
if (dev->watch_id > 0)
g_source_remove(dev->watch_id);
if (dev->io != NULL)
g_io_channel_unref(dev->io);
hci_close_dev(dev->sk);
g_slist_foreach(dev->keys, (GFunc) g_free, NULL);
g_slist_free(dev->keys);
init_dev_info(index, -1, dev->registered);
}
@ -2443,12 +2492,8 @@ static void hciops_cleanup(void)
DBG("");
for (i = 0; i <= max_dev; i++) {
struct dev_info *dev = &devs[i];
if (dev->sk >= 0)
hci_close_dev(dev->sk);
}
for (i = 0; i <= max_dev; i++)
stop_hci_dev(i);
g_free(devs);
devs = NULL;
@ -2868,11 +2913,18 @@ static int hciops_remove_bonding(int index, bdaddr_t *bdaddr)
{
struct dev_info *dev = &devs[index];
delete_stored_link_key_cp cp;
GSList *match;
char addr[18];
ba2str(bdaddr, addr);
DBG("hci%d dba %s", index, addr);
match = g_slist_find_custom(dev->keys, bdaddr, (GCompareFunc) bacmp);
if (match) {
g_free(match->data);
dev->keys = g_slist_delete_link(dev->keys, match);
}
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, bdaddr);
@ -3224,6 +3276,20 @@ static int hciops_restore_powered(int index)
return 0;
}
static int hciops_load_keys(int index, GSList *keys)
{
struct dev_info *dev = &devs[index];
DBG("hci%d keys %d", index, g_slist_length(keys));
if (dev->keys != NULL)
return -EEXIST;
dev->keys = keys;
return 0;
}
static struct btd_adapter_ops hci_ops = {
.setup = hciops_setup,
.cleanup = hciops_cleanup,
@ -3265,6 +3331,7 @@ static struct btd_adapter_ops hci_ops = {
.services_updated = hciops_services_updated,
.disable_cod_cache = hciops_disable_cod_cache,
.restore_powered = hciops_restore_powered,
.load_keys = hciops_load_keys,
};
static int hciops_init(void)

View File

@ -789,6 +789,12 @@ static int mgmt_restore_powered(int index)
return -ENOSYS;
}
static int mgmt_load_keys(int index, GSList *keys)
{
DBG("index %d keys %d", index, g_slist_length(keys));
return -ENOSYS;
}
static struct btd_adapter_ops mgmt_ops = {
.setup = mgmt_setup,
.cleanup = mgmt_cleanup,
@ -830,6 +836,7 @@ static struct btd_adapter_ops mgmt_ops = {
.services_updated = mgmt_services_updated,
.disable_cod_cache = mgmt_disable_cod_cache,
.restore_powered = mgmt_restore_powered,
.load_keys = mgmt_load_keys,
};
static int mgmt_init(void)

View File

@ -1857,11 +1857,53 @@ static void create_stored_device_from_profiles(char *key, char *value,
g_slist_free(uuids);
}
struct adapter_keys {
struct btd_adapter *adapter;
GSList *keys;
};
static struct link_key_info *get_key_info(const char *addr, const char *value)
{
struct link_key_info *info;
char tmp[3];
int i;
if (strlen(value) < 36) {
error("Unexpectedly short (%zu) link key line", strlen(value));
return NULL;
}
info = g_new0(struct link_key_info, 1);
str2ba(addr, &info->bdaddr);
memset(tmp, 0, sizeof(tmp));
for (i = 0; i < 16; i++) {
memcpy(tmp, value + (i * 2), 2);
info->key[i] = (uint8_t) strtol(tmp, NULL, 16);
}
memcpy(tmp, value + 33, 2);
info->type = (uint8_t) strtol(tmp, NULL, 10);
memcpy(tmp, value + 35, 2);
info->pin_len = strtol(tmp, NULL, 10);
return info;
}
static void create_stored_device_from_linkkeys(char *key, char *value,
void *user_data)
{
struct btd_adapter *adapter = user_data;
struct adapter_keys *keys = user_data;
struct btd_adapter *adapter = keys->adapter;
struct btd_device *device;
struct link_key_info *info;
info = get_key_info(key, value);
if (info)
keys->keys = g_slist_append(keys->keys, info);
if (g_slist_find_custom(adapter->devices, key,
(GCompareFunc) device_address_cmp))
@ -1895,6 +1937,8 @@ static void load_devices(struct btd_adapter *adapter)
{
char filename[PATH_MAX + 1];
char srcaddr[18];
struct adapter_keys keys = { adapter, NULL };
int err;
ba2str(&adapter->bdaddr, srcaddr);
@ -1903,8 +1947,15 @@ static void load_devices(struct btd_adapter *adapter)
adapter);
create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "linkkeys");
textfile_foreach(filename, create_stored_device_from_linkkeys,
adapter);
textfile_foreach(filename, create_stored_device_from_linkkeys, &keys);
err = adapter_ops->load_keys(adapter->dev_id, keys.keys);
if (err < 0) {
error("Unable to load keys to adapter_ops: %s (%d)",
strerror(-err), -err);
g_slist_foreach(keys.keys, (GFunc) g_free, NULL);
g_slist_free(keys.keys);
}
create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "blocked");
textfile_foreach(filename, create_stored_device_from_blocked, adapter);

View File

@ -66,6 +66,13 @@ typedef enum {
struct btd_adapter;
struct link_key_info {
bdaddr_t bdaddr;
unsigned char key[16];
uint8_t type;
int pin_len;
};
struct remote_dev_info {
bdaddr_t bdaddr;
int8_t rssi;
@ -239,6 +246,7 @@ struct btd_adapter_ops {
int (*services_updated) (int index);
int (*disable_cod_cache) (int index);
int (*restore_powered) (int index);
int (*load_keys) (int index, GSList *keys);
};
int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority);

View File

@ -2380,6 +2380,9 @@ gboolean device_set_debug_key(struct btd_device *device, uint8_t *key)
gboolean device_get_debug_key(struct btd_device *device, uint8_t *key)
{
if (main_opts.debug_keys == FALSE)
return FALSE;
if (!device->has_debug_key)
return FALSE;