Refactor pairing code to match the management interface

This patch refactors the pairing code in hciops and the core daemon to
better match the management interface. Particularly all logic regarding
IO capabilities and authentication requirements are moved into hciops.c.
The patch is quite big because there are a lot of cross-dependencies
which makes it difficult to split the changes up into smaller chunks.

The important adapter_ops preparations for the management interface in
this patch are the create_bonding and cancel_bonding callbacks. Those
will be directly mapped to corresponding management commands.
diff --git a/plugins/hciops.c b/plugins/hciops.c
index 59a15a0..75de36a 100644
--- a/plugins/hciops.c
+++ b/plugins/hciops.c
@@ -42,6 +42,7 @@
 
 #include "hcid.h"
 #include "sdpd.h"
+#include "btio.h"
 #include "adapter.h"
 #include "device.h"
 #include "plugin.h"
@@ -68,13 +69,23 @@
 	uint8_t svc_hint;
 };
 
-struct acl_connection {
+struct bt_conn {
+	struct dev_info *dev;
 	bdaddr_t bdaddr;
 	uint16_t handle;
+	uint8_t loc_cap;
+	uint8_t loc_auth;
+	uint8_t rem_cap;
+	uint8_t rem_auth;
+	uint8_t key_type;
+	gboolean bonding_initiator;
+	gboolean secmode3;
+	GIOChannel *io; /* For raw L2CAP socket (bonding) */
 };
 
 static int max_dev = -1;
 static struct dev_info {
+	int id;
 	int sk;
 	bdaddr_t bdaddr;
 	char name[249];
@@ -90,6 +101,9 @@
 	gboolean cache_enable;
 	gboolean already_up;
 	gboolean registered;
+	gboolean pairable;
+
+	uint8_t io_capability;
 
 	struct hci_version ver;
 
@@ -123,9 +137,11 @@
 
 	memset(dev, 0, sizeof(*dev));
 
+	dev->id = index;
 	dev->sk = sk;
 	dev->cache_enable = TRUE;
 	dev->registered = registered;
+	dev->io_capability = 0x03; /* No Input No Output */
 }
 
 /* Async HCI command handling with callback support */
@@ -329,6 +345,8 @@
 	if (adapter)
 		btd_adapter_pairable_changed(adapter, pairable);
 
+	devs[index].pairable = pairable;
+
 	return 0;
 }
 
@@ -574,22 +592,81 @@
 
 /* Start of HCI event callbacks */
 
-static int get_handle(int index, const bdaddr_t *bdaddr, uint16_t *handle)
+static gint conn_handle_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct bt_conn *conn = a;
+	uint16_t handle = *((const uint16_t *) b);
+
+	return (int) conn->handle - (int) handle;
+}
+
+static struct bt_conn *find_conn_by_handle(struct dev_info *dev,
+							uint16_t handle)
+{
+	GSList *match;
+
+	match = g_slist_find_custom(dev->connections, &handle,
+							conn_handle_cmp);
+	if (match)
+		return match->data;
+
+	return NULL;
+}
+
+static gint conn_bdaddr_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct bt_conn *conn = a;
+	const bdaddr_t *bdaddr = b;
+
+	return bacmp(&conn->bdaddr, bdaddr);
+}
+
+static struct bt_conn *find_connection(struct dev_info *dev, bdaddr_t *bdaddr)
+{
+	GSList *match;
+
+	match = g_slist_find_custom(dev->connections, bdaddr, conn_bdaddr_cmp);
+	if (match)
+		return match->data;
+
+	return NULL;
+}
+
+static struct bt_conn *get_connection(struct dev_info *dev, bdaddr_t *bdaddr)
+{
+	struct bt_conn *conn;
+
+	conn = find_connection(dev, bdaddr);
+	if (conn)
+		return conn;
+
+	conn = g_new0(struct bt_conn, 1);
+
+	conn->dev = dev;
+	conn->loc_cap = dev->io_capability;
+	conn->loc_auth = 0xff;
+	conn->rem_auth = 0xff;
+	conn->key_type = 0xff;
+	bacpy(&conn->bdaddr, bdaddr);
+
+	dev->connections = g_slist_append(dev->connections, conn);
+
+	return conn;
+}
+
+static int get_handle(int index, bdaddr_t *bdaddr, uint16_t *handle)
 {
 	struct dev_info *dev = &devs[index];
-	struct acl_connection *conn;
-	GSList *match;
+	struct bt_conn *conn;
 	char addr[18];
 
 	ba2str(bdaddr, addr);
 	DBG("hci%d dba %s", index, addr);
 
-	match = g_slist_find_custom(dev->connections, bdaddr,
-							(GCompareFunc) bacmp);
-	if (match == NULL)
+	conn = find_connection(dev, bdaddr);
+	if (conn == NULL)
 		return -ENOENT;
 
-	conn = match->data;
 	*handle = conn->handle;
 
 	return 0;
@@ -616,34 +693,42 @@
 	return 0;
 }
 
-static inline int get_bdaddr(int index, uint16_t handle, bdaddr_t *dba)
+static void bonding_complete(struct dev_info *dev, struct bt_conn *conn,
+								uint8_t status)
 {
-	struct dev_info *dev = &devs[index];
-	struct hci_conn_list_req *cl;
-	struct hci_conn_info *ci;
-	int i;
+	DBG("status 0x%02x", status);
 
-	cl = g_malloc0(10 * sizeof(*ci) + sizeof(*cl));
-
-	cl->dev_id = index;
-	cl->conn_num = 10;
-	ci = cl->conn_info;
-
-	if (ioctl(dev->sk, HCIGETCONNLIST, (void *) cl) < 0) {
-		g_free(cl);
-		return -EIO;
+	if (conn->io != NULL) {
+		g_io_channel_shutdown(conn->io, TRUE, NULL);
+		g_io_channel_unref(conn->io);
+		conn->io = NULL;
 	}
 
-	for (i = 0; i < cl->conn_num; i++, ci++)
-		if (ci->handle == handle) {
-			bacpy(dba, &ci->bdaddr);
-			g_free(cl);
-			return 0;
-		}
+	conn->bonding_initiator = FALSE;
 
-	g_free(cl);
+	btd_event_bonding_process_complete(&dev->bdaddr, &conn->bdaddr,
+								status);
+}
 
-	return -ENOENT;
+static int get_auth_info(int index, bdaddr_t *bdaddr, uint8_t *auth)
+{
+	struct dev_info *dev = &devs[index];
+	struct hci_auth_info_req req;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%d dba %s", index, addr);
+
+	memset(&req, 0, sizeof(req));
+	bacpy(&req.bdaddr, bdaddr);
+
+	if (ioctl(dev->sk, HCIGETAUTHINFO, (unsigned long) &req) < 0)
+		return -errno;
+
+	if (auth)
+		*auth = req.type;
+
+	return 0;
 }
 
 /* Link Key handling */
@@ -651,27 +736,21 @@
 static void link_key_request(int index, bdaddr_t *dba)
 {
 	struct dev_info *dev = &devs[index];
-	struct hci_auth_info_req req;
-	GSList *match;
 	struct link_key_info *key_info;
+	struct bt_conn *conn;
+	GSList *match;
 	char da[18];
-	int err;
 
 	ba2str(dba, da);
 	DBG("hci%d dba %s", index, da);
 
-	memset(&req, 0, sizeof(req));
-	bacpy(&req.bdaddr, dba);
+	conn = get_connection(dev, dba);
+	if (conn->handle == 0)
+		conn->secmode3 = TRUE;
 
-	err = ioctl(dev->sk, HCIGETAUTHINFO, (unsigned long) &req);
-	if (err < 0) {
-		if (errno != EINVAL)
-			DBG("HCIGETAUTHINFO failed %s (%d)",
-						strerror(errno), errno);
-		req.type = 0x00;
-	}
+	get_auth_info(index, dba, &conn->loc_auth);
 
-	DBG("kernel auth requirements = 0x%02x", req.type);
+	DBG("kernel auth requirements = 0x%02x", conn->loc_auth);
 
 	match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
 	if (match)
@@ -694,7 +773,8 @@
 
 	/* Don't use unauthenticated combination keys if MITM is
 	 * required */
-	if (key_info->type == 0x04 && req.type != 0xff && (req.type & 0x01))
+	if (key_info->type == 0x04 && conn->loc_auth != 0xff &&
+						(conn->loc_auth & 0x01))
 		hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY,
 								6, dba);
 	else {
@@ -714,14 +794,17 @@
 	evt_link_key_notify *evt = ptr;
 	bdaddr_t *dba = &evt->bdaddr;
 	struct link_key_info *key_info;
+	uint8_t old_key_type, key_type;
+	struct bt_conn *conn;
 	GSList *match;
-	uint8_t old_key_type, reason;
 	char da[18];
-	int err;
+	uint8_t status = 0;
 
 	ba2str(dba, da);
 	DBG("hci%d dba %s type %d", index, da, evt->key_type);
 
+	conn = get_connection(dev, &evt->bdaddr);
+
 	match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
 	if (match)
 		key_info = match->data;
@@ -731,7 +814,7 @@
 	if (key_info == NULL) {
 		key_info = g_new0(struct link_key_info, 1);
 		bacpy(&key_info->bdaddr, &evt->bdaddr);
-		old_key_type = 0xff;
+		old_key_type = conn->key_type;
 	} else {
 		dev->keys = g_slist_remove(dev->keys, key_info);
 		old_key_type = key_info->type;
@@ -741,26 +824,78 @@
 	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);
+	key_type = evt->key_type;
+
+	DBG("key type 0x%02x old key type 0x%02x", key_type, old_key_type);
+	DBG("local auth 0x%02x and remote auth 0x%02x",
+					conn->loc_auth, conn->rem_auth);
+
+	if (key_type == 0x06) {
+		/* Some buggy controller combinations generate a changed
+		 * combination key for legacy pairing even when there's no
+		 * previous key */
+		if ((!conn || conn->rem_auth == 0xff) && old_key_type == 0xff)
+			key_type = 0x00;
+		else if (old_key_type != 0xff)
+			key_type = old_key_type;
+		else
+			/* This is Changed Combination Link Key for
+			 * a temporary link key.*/
+			goto done;
+	}
+
+	key_info->type = key_type;
+	conn->key_type = key_type;
+
+	/* Skip the storage check if this is a debug key */
+	if (key_type == 0x03)
+		goto done;
+
+	/* Store the link key persistently if one of the following is true:
+	 * 1. this is a legacy link key
+	 * 2. this is a changed combination key and there was a previously
+	 *    stored one
+	 * 3. neither local nor remote side had no-bonding as a requirement
+	 * 4. the local side had dedicated bonding as a requirement
+	 * 5. the remote side is using dedicated bonding since in that case
+	 *    also the local requirements are set to dedicated bonding
+	 * If none of the above match only keep the link key around for
+	 * this connection and set the temporary flag for the device.
+	 */
+	if (key_type < 0x03 || (key_type == 0x06 && old_key_type != 0xff) ||
+			(conn->loc_auth > 0x01 && conn->rem_auth > 0x01) ||
+			(conn->loc_auth == 0x02 || conn->loc_auth == 0x03) ||
+			(conn->rem_auth == 0x02 || conn->rem_auth == 0x03)) {
+		int err;
+
+		err = btd_event_link_key_notify(&dev->bdaddr, dba,
+						evt->link_key, key_type,
+						dev->pin_length);
+
+		if (err == -ENODEV)
+			status = HCI_OE_LOW_RESOURCES;
+		else if (err < 0)
+			status = HCI_MEMORY_FULL;
+
+		goto done;
+	}
+
+done:
 	dev->pin_length = 0;
 
-	if (err == 0) {
-		dev->keys = g_slist_append(dev->keys, key_info);
+	if (status != 0) {
+		g_free(key_info);
+		bonding_complete(dev, conn, status);
+		disconnect_addr(index, dba, status);
 		return;
 	}
 
-	g_free(key_info);
+	dev->keys = g_slist_append(dev->keys, 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);
+	/* If we're connected and not dedicated bonding initiators we're
+	 * done with the bonding process */
+	if (!conn->bonding_initiator && conn->handle != 0)
+		bonding_complete(dev, conn, 0);
 }
 
 static void return_link_keys(int index, void *ptr)
@@ -795,13 +930,42 @@
 {
 	struct dev_info *dev = &devs[index];
 	evt_user_confirm_request *req = ptr;
+	gboolean loc_mitm, rem_mitm, auto_accept;
+	struct bt_conn *conn;
 
 	DBG("hci%d", index);
 
+	conn = find_connection(dev, &req->bdaddr);
+	if (conn == NULL)
+		return;
+
+	loc_mitm = (conn->loc_auth & 0x01) ? TRUE : FALSE;
+	rem_mitm = (conn->rem_auth & 0x01) ? TRUE : FALSE;
+
+	/* If we require MITM but the remote device can't provide that
+	 * (it has NoInputNoOutput) then reject the confirmation
+	 * request. The only exception is when we're dedicated bonding
+	 * initiators since then we always have the MITM bit set. */
+	if (!conn->bonding_initiator && loc_mitm && conn->rem_cap == 0x03) {
+		error("Rejecting request: remote device can't provide MITM");
+		goto fail;
+	}
+
+	/* If no side requires MITM protection; auto-accept */
+	if ((conn->loc_auth == 0xff || !loc_mitm || conn->rem_cap == 0x03) &&
+					(!rem_mitm || conn->loc_cap == 0x03)) {
+		DBG("auto accept of confirmation");
+		auto_accept = TRUE;
+	} else
+		auto_accept = FALSE;
+
 	if (btd_event_user_confirm(&dev->bdaddr, &req->bdaddr,
-					btohl(req->passkey)) < 0)
-		hci_send_cmd(dev->sk, OGF_LINK_CTL,
-				OCF_USER_CONFIRM_NEG_REPLY, 6, ptr);
+				btohl(req->passkey), auto_accept) == 0)
+		return;
+
+fail:
+	hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_USER_CONFIRM_NEG_REPLY,
+								6, ptr);
 }
 
 static void user_passkey_request(int index, void *ptr)
@@ -837,18 +1001,93 @@
 				OCF_REMOTE_OOB_DATA_NEG_REPLY, 6, ptr);
 }
 
+static int get_io_cap(int index, bdaddr_t *bdaddr, uint8_t *cap, uint8_t *auth)
+{
+	struct dev_info *dev = &devs[index];
+	struct bt_conn *conn;
+	int err;
+
+	conn = find_connection(dev, bdaddr);
+	if (conn == NULL)
+		return -ENOENT;
+
+	err = get_auth_info(index, bdaddr, &conn->loc_auth);
+	if (err < 0)
+		return err;
+
+	DBG("initial authentication requirement is 0x%02x", conn->loc_auth);
+
+	if (!dev->pairable && !conn->bonding_initiator) {
+		if (conn->rem_auth < 0x02) {
+			DBG("Allowing no bonding in non-bondable mode");
+			/* Kernel defaults to general bonding and so
+			 * overwrite for this special case. Otherwise
+			 * non-pairable test cases will fail. */
+			conn->loc_auth = conn->rem_auth;
+			goto done;
+		}
+
+		return -EPERM;
+	}
+
+	/* If the kernel doesn't know the local requirement just mirror
+	 * the remote one */
+	if (conn->loc_auth == 0xff)
+		conn->loc_auth = conn->rem_auth;
+
+	if (conn->loc_auth == 0x00 || conn->loc_auth == 0x04) {
+		/* If remote requests dedicated bonding follow that lead */
+		if (conn->rem_auth == 0x02 || conn->rem_auth == 0x03) {
+
+			/* If both remote and local IO capabilities allow MITM
+			 * then require it, otherwise don't */
+			if (conn->rem_cap == 0x03 || conn->loc_cap == 0x03)
+				conn->loc_auth = 0x02;
+			else
+				conn->loc_auth = 0x03;
+		}
+
+		/* If remote indicates no bonding then follow that. This
+		 * is important since the kernel might give general bonding
+		 * as default. */
+		if (conn->rem_auth == 0x00 || conn->rem_auth == 0x01)
+			conn->loc_auth = 0x00;
+
+		/* If remote requires MITM then also require it, unless
+		 * our IO capability is NoInputNoOutput (so some
+		 * just-works security cases can be tested) */
+		if (conn->rem_auth != 0xff && (conn->rem_auth & 0x01) &&
+							conn->loc_cap != 0x03)
+			conn->loc_auth |= 0x01;
+	}
+
+done:
+	*cap = conn->loc_cap;
+	*auth = conn->loc_auth;
+
+	DBG("final authentication requirement is 0x%02x", *auth);
+
+	return 0;
+}
+
 static void io_capa_request(int index, void *ptr)
 {
 	struct dev_info *dev = &devs[index];
 	bdaddr_t *dba = ptr;
+	uint8_t cap, auth = 0xff;
 	char da[18];
-	uint8_t cap, auth;
+	int err;
 
 	ba2str(dba, da);
 	DBG("hci%d IO capability request for %s", index, da);
 
-	if (btd_event_get_io_cap(&dev->bdaddr, dba, &cap, &auth) < 0) {
+	err = get_io_cap(index, dba, &cap, &auth);
+	if (err < 0) {
 		io_capability_neg_reply_cp cp;
+
+		error("Getting IO capability failed: %s (%d)",
+						strerror(-err), -err);
+
 		memset(&cp, 0, sizeof(cp));
 		bacpy(&cp.bdaddr, dba);
 		cp.reason = HCI_PAIRING_NOT_ALLOWED;
@@ -871,13 +1110,17 @@
 {
 	struct dev_info *dev = &devs[index];
 	evt_io_capability_response *evt = ptr;
+	struct bt_conn *conn;
 	char da[18];
 
 	ba2str(&evt->bdaddr, da);
 	DBG("hci%d IO capability response from %s", index, da);
 
-	btd_event_set_io_cap(&dev->bdaddr, &evt->bdaddr,
-				evt->capability, evt->authentication);
+	conn = find_connection(dev, &evt->bdaddr);
+	if (conn) {
+		conn->rem_cap = evt->capability;
+		conn->rem_auth = evt->authentication;
+	}
 }
 
 /* PIN code handling */
@@ -885,18 +1128,34 @@
 static void pin_code_request(int index, bdaddr_t *dba)
 {
 	struct dev_info *dev = &devs[index];
+	struct bt_conn *conn;
 	char addr[18];
 	int err;
 
 	ba2str(dba, addr);
 	DBG("hci%d PIN request for %s", index, addr);
 
+	conn = get_connection(dev, dba);
+	if (conn->handle == 0)
+		conn->secmode3 = TRUE;
+
+	/* Check if the adapter is not pairable and if there isn't a bonding in
+	 * progress */
+	if (!dev->pairable && !conn->bonding_initiator) {
+		DBG("Rejecting PIN request in non-pairable mode");
+		goto reject;
+	}
+
 	err = btd_event_request_pin(&dev->bdaddr, dba);
 	if (err < 0) {
 		error("PIN code negative reply: %s", strerror(-err));
-		hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY,
-								6, dba);
+		goto reject;
 	}
+
+	return;
+
+reject:
+	hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, dba);
 }
 
 static void start_inquiry(bdaddr_t *local, uint8_t status, gboolean periodic)
@@ -1562,18 +1821,20 @@
 {
 	struct dev_info *dev = &devs[index];
 	evt_read_remote_version_complete *evt = ptr;
-	bdaddr_t dba;
+	struct bt_conn *conn;
 
 	DBG("hci%d status %u", index, evt->status);
 
 	if (evt->status)
 		return;
 
-	if (get_bdaddr(index, btohs(evt->handle), &dba) < 0)
+	conn = find_conn_by_handle(dev, btohs(evt->handle));
+	if (conn == NULL)
 		return;
 
-	write_version_info(&dev->bdaddr, &dba, btohs(evt->manufacturer),
-				evt->lmp_ver, btohs(evt->lmp_subver));
+	write_version_info(&dev->bdaddr, &conn->bdaddr,
+				btohs(evt->manufacturer), evt->lmp_ver,
+				btohs(evt->lmp_subver));
 }
 
 static inline void inquiry_result(int index, int plen, void *ptr)
@@ -1650,17 +1911,18 @@
 {
 	struct dev_info *dev = &devs[index];
 	evt_read_remote_features_complete *evt = ptr;
-	bdaddr_t dba;
+	struct bt_conn *conn;
 
 	DBG("hci%d status %u", index, evt->status);
 
 	if (evt->status)
 		return;
 
-	if (get_bdaddr(index, btohs(evt->handle), &dba) < 0)
+	conn = find_conn_by_handle(dev, btohs(evt->handle));
+	if (conn == NULL)
 		return;
 
-	write_features_info(&dev->bdaddr, &dba, evt->features, NULL);
+	write_features_info(&dev->bdaddr, &conn->bdaddr, evt->features, NULL);
 }
 
 struct remote_version_req {
@@ -1697,32 +1959,48 @@
 								req, g_free);
 }
 
+static void conn_free(struct bt_conn *conn)
+{
+	if (conn->io != NULL) {
+		g_io_channel_shutdown(conn->io, TRUE, NULL);
+		g_io_channel_unref(conn->io);
+	}
+
+	g_free(conn);
+}
+
 static inline void conn_complete(int index, void *ptr)
 {
 	struct dev_info *dev = &devs[index];
 	evt_conn_complete *evt = ptr;
 	char filename[PATH_MAX];
 	char local_addr[18], peer_addr[18], *str;
+	struct bt_conn *conn;
 
 	if (evt->link_type != ACL_LINK)
 		return;
 
+	DBG("status 0x%02x", evt->status);
+
+	conn = find_connection(dev, &evt->bdaddr);
+
 	if (evt->status == 0) {
-		struct acl_connection *conn;
-
-		conn = g_new0(struct acl_connection, 1);
-
-		bacpy(&conn->bdaddr, &evt->bdaddr);
-		conn->handle = evt->handle;
-
-		dev->connections = g_slist_append(dev->connections, conn);
+		if (conn == NULL)
+			conn = get_connection(dev, &evt->bdaddr);
+		conn->handle = btohs(evt->handle);
+	} else if (conn != NULL) {
+		dev->connections = g_slist_remove(dev->connections, conn);
+		conn_free(conn);
 	}
 
 	btd_event_conn_complete(&dev->bdaddr, evt->status, &evt->bdaddr);
 
-	if (evt->status)
+	if (evt->status != 0)
 		return;
 
+	if (conn->secmode3)
+		bonding_complete(dev, conn, 0);
+
 	/* check if the remote version needs be requested */
 	ba2str(&dev->bdaddr, local_addr);
 	ba2str(&evt->bdaddr, peer_addr);
@@ -1745,14 +2023,10 @@
 	char local_addr[18], peer_addr[18], *str;
 
 	if (evt->status == 0) {
-		struct acl_connection *conn;
+		struct bt_conn *conn;
 
-		conn = g_new0(struct acl_connection, 1);
-
-		bacpy(&conn->bdaddr, &evt->peer_bdaddr);
-		conn->handle = evt->handle;
-
-		dev->connections = g_slist_append(dev->connections, conn);
+		conn = get_connection(dev, &evt->peer_bdaddr);
+		conn->handle = btohs(evt->handle);
 	}
 
 	btd_event_conn_complete(&dev->bdaddr, evt->status, &evt->peer_bdaddr);
@@ -1774,52 +2048,41 @@
 		free(str);
 }
 
-static int conn_handle_cmp(gconstpointer a, gconstpointer b)
-{
-	const struct acl_connection *conn = a;
-	uint16_t handle = *((uint16_t *) b);
-
-	return (int) conn->handle - (int) handle;
-}
-
 static inline void disconn_complete(int index, void *ptr)
 {
 	struct dev_info *dev = &devs[index];
 	evt_disconn_complete *evt = ptr;
-	struct acl_connection *conn;
-	uint16_t handle;
-	GSList *match;
+	struct bt_conn *conn;
+
+	DBG("handle %u status 0x%02x", btohs(evt->handle), evt->status);
 
 	if (evt->status != 0)
 		return;
 
-	handle = btohs(evt->handle);
-	match = g_slist_find_custom(dev->connections, &handle,
-							conn_handle_cmp);
-	if (match == NULL)
+	conn = find_conn_by_handle(dev, btohs(evt->handle));
+	if (conn == NULL)
 		return;
 
-	conn = match->data;
-
-	dev->connections = g_slist_delete_link(dev->connections, match);
+	dev->connections = g_slist_remove(dev->connections, conn);
 
 	btd_event_disconn_complete(&dev->bdaddr, &conn->bdaddr);
 
-	g_free(conn);
+	conn_free(conn);
 }
 
 static inline void auth_complete(int index, void *ptr)
 {
 	struct dev_info *dev = &devs[index];
 	evt_auth_complete *evt = ptr;
-	bdaddr_t dba;
+	struct bt_conn *conn;
 
 	DBG("hci%d status %u", index, evt->status);
 
-	if (get_bdaddr(index, btohs(evt->handle), &dba) < 0)
+	conn = find_conn_by_handle(dev, btohs(evt->handle));
+	if (conn == NULL)
 		return;
 
-	btd_event_bonding_process_complete(&dev->bdaddr, &dba, evt->status);
+	bonding_complete(dev, conn, evt->status);
 }
 
 static inline void simple_pairing_complete(int index, void *ptr)
@@ -1898,7 +2161,7 @@
 	g_slist_foreach(dev->uuids, (GFunc) g_free, NULL);
 	g_slist_free(dev->uuids);
 
-	g_slist_foreach(dev->connections, (GFunc) g_free, NULL);
+	g_slist_foreach(dev->connections, (GFunc) conn_free, NULL);
 	g_slist_free(dev->connections);
 
 	init_dev_info(index, -1, dev->registered);
@@ -2270,17 +2533,13 @@
 	}
 
 	for (i = 0; i < cl->conn_num; i++, ci++) {
-		struct acl_connection *conn;
+		struct bt_conn *conn;
 
 		if (ci->type != ACL_LINK)
 			continue;
 
-		conn = g_new0(struct acl_connection, 1);
-
-		bacpy(&conn->bdaddr, &ci->bdaddr);
+		conn = get_connection(dev, &ci->bdaddr);
 		conn->handle = ci->handle;
-
-		dev->connections = g_slist_append(dev->connections, conn);
 	}
 
 	err = 0;
@@ -2845,7 +3104,7 @@
 	*conns = NULL;
 
 	for (l = dev->connections; l != NULL; l = g_slist_next(l)) {
-		struct acl_connection *conn = l->data;
+		struct bt_conn *conn = l->data;
 
 		*conns = g_slist_append(*conns,
 				g_memdup(&conn->bdaddr, sizeof(bdaddr_t)));
@@ -3022,27 +3281,6 @@
 	return err;
 }
 
-static int hciops_get_auth_info(int index, bdaddr_t *bdaddr, uint8_t *auth)
-{
-	struct dev_info *dev = &devs[index];
-	struct hci_auth_info_req req;
-	char addr[18];
-
-	ba2str(bdaddr, addr);
-	DBG("hci%d dba %s", index, addr);
-
-	memset(&req, 0, sizeof(req));
-	bacpy(&req.bdaddr, bdaddr);
-
-	if (ioctl(dev->sk, HCIGETAUTHINFO, (unsigned long) &req) < 0)
-		return -errno;
-
-	if (auth)
-		*auth = req.type;
-
-	return 0;
-}
-
 static int hciops_read_scan_enable(int index)
 {
 	struct dev_info *dev = &devs[index];
@@ -3212,6 +3450,89 @@
 
 static int hciops_set_io_capability(int index, uint8_t io_capability)
 {
+	struct dev_info *dev = &devs[index];
+
+	dev->io_capability = io_capability;
+
+	return 0;
+}
+
+static void bonding_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct bt_conn *conn = user_data;
+	struct dev_info *dev = conn->dev;
+
+	if (!conn->io) {
+		if (!err)
+			g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	if (err)
+		/* Wait proper error to be propagated by bonding complete */
+		return;
+
+	if (hciops_request_authentication(dev->id, &conn->bdaddr) < 0)
+		goto failed;
+
+	return;
+
+failed:
+	bonding_complete(dev, conn, HCI_UNSPECIFIED_ERROR);
+}
+
+static int hciops_create_bonding(int index, bdaddr_t *bdaddr, uint8_t io_cap)
+{
+	struct dev_info *dev = &devs[index];
+	BtIOSecLevel sec_level;
+	struct bt_conn *conn;
+	GError *err = NULL;
+
+	conn = get_connection(dev, bdaddr);
+
+	if (conn->io != NULL)
+		return -EBUSY;
+
+	conn->loc_cap = io_cap;
+
+	/* If our IO capability is NoInputNoOutput use medium security
+	 * level (i.e. don't require MITM protection) else use high
+	 * security level */
+	if (io_cap == 0x03)
+		sec_level = BT_IO_SEC_MEDIUM;
+	else
+		sec_level = BT_IO_SEC_HIGH;
+
+	conn->io = bt_io_connect(BT_IO_L2RAW, bonding_connect_cb, conn,
+					NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &dev->bdaddr,
+					BT_IO_OPT_DEST_BDADDR, bdaddr,
+					BT_IO_OPT_SEC_LEVEL, sec_level,
+					BT_IO_OPT_INVALID);
+	if (conn->io == NULL) {
+		error("bt_io_connect: %s", err->message);
+		g_error_free(err);
+		return -EIO;
+	}
+
+	conn->bonding_initiator = TRUE;
+
+	return 0;
+}
+
+static int hciops_cancel_bonding(int index, bdaddr_t *bdaddr)
+{
+	struct dev_info *dev = &devs[index];
+	struct bt_conn *conn;
+
+	conn = find_connection(dev, bdaddr);
+	if (conn == NULL || conn->io == NULL)
+		return -ENOTCONN;
+
+	g_io_channel_shutdown(conn->io, TRUE, NULL);
+	g_io_channel_unref(conn->io);
+	conn->io = NULL;
+
 	return 0;
 }
 
@@ -3244,7 +3565,6 @@
 	.pincode_reply = hciops_pincode_reply,
 	.confirm_reply = hciops_confirm_reply,
 	.passkey_reply = hciops_passkey_reply,
-	.get_auth_info = hciops_get_auth_info,
 	.read_scan_enable = hciops_read_scan_enable,
 	.enable_le = hciops_enable_le,
 	.encrypt_link = hciops_encrypt_link,
@@ -3255,6 +3575,8 @@
 	.restore_powered = hciops_restore_powered,
 	.load_keys = hciops_load_keys,
 	.set_io_capability = hciops_set_io_capability,
+	.create_bonding = hciops_create_bonding,
+	.cancel_bonding = hciops_cancel_bonding,
 };
 
 static int hciops_init(void)
diff --git a/plugins/mgmtops.c b/plugins/mgmtops.c
index 6897d51..629cf36 100644
--- a/plugins/mgmtops.c
+++ b/plugins/mgmtops.c
@@ -450,7 +450,7 @@
 
 	btd_event_link_key_notify(&info->bdaddr, &ev->key.bdaddr,
 					ev->key.val, ev->key.type,
-					ev->key.pin_len, ev->old_key_type);
+					ev->key.pin_len);
 }
 
 static void mgmt_device_connected(int sk, void *buf, size_t len)
@@ -1479,16 +1479,6 @@
 	return -ENOSYS;
 }
 
-static int mgmt_get_auth_info(int index, bdaddr_t *bdaddr, uint8_t *auth)
-{
-	char addr[18];
-
-	ba2str(bdaddr, addr);
-	DBG("index %d addr %s", index, addr);
-
-	return -ENOSYS;
-}
-
 static int mgmt_read_scan_enable(int index)
 {
 	DBG("index %d", index);
@@ -1603,6 +1593,26 @@
 	return 0;
 }
 
+static int mgmt_create_bonding(int index, bdaddr_t *bdaddr, uint8_t io_cap)
+{
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%d bdaddr %s io_cap 0x%02x", index, addr, io_cap);
+
+	return -ENOSYS;
+}
+
+static int mgmt_cancel_bonding(int index, bdaddr_t *bdaddr)
+{
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%d bdaddr %s", index, addr);
+
+	return -ENOSYS;
+}
+
 static struct btd_adapter_ops mgmt_ops = {
 	.setup = mgmt_setup,
 	.cleanup = mgmt_cleanup,
@@ -1632,7 +1642,6 @@
 	.pincode_reply = mgmt_pincode_reply,
 	.confirm_reply = mgmt_confirm_reply,
 	.passkey_reply = mgmt_passkey_reply,
-	.get_auth_info = mgmt_get_auth_info,
 	.read_scan_enable = mgmt_read_scan_enable,
 	.enable_le = mgmt_enable_le,
 	.encrypt_link = mgmt_encrypt_link,
@@ -1643,6 +1652,8 @@
 	.restore_powered = mgmt_restore_powered,
 	.load_keys = mgmt_load_keys,
 	.set_io_capability = mgmt_set_io_capability,
+	.create_bonding = mgmt_create_bonding,
+	.cancel_bonding = mgmt_cancel_bonding,
 };
 
 static int mgmt_init(void)
diff --git a/src/adapter.c b/src/adapter.c
index 4c509d8..fb5bc78 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -3200,6 +3200,8 @@
 {
 	bdaddr_t bdaddr;
 
+	DBG("");
+
 	if (!g_slist_find(adapter->connections, device)) {
 		error("No matching connection for device");
 		return;
@@ -3620,12 +3622,6 @@
 	return adapter_ops->passkey_reply(adapter->dev_id, bdaddr, passkey);
 }
 
-int btd_adapter_get_auth_info(struct btd_adapter *adapter, bdaddr_t *bdaddr,
-								uint8_t *auth)
-{
-	return adapter_ops->get_auth_info(adapter->dev_id, bdaddr, auth);
-}
-
 int btd_adapter_read_scan_enable(struct btd_adapter *adapter)
 {
 	return adapter_ops->read_scan_enable(adapter->dev_id);
@@ -3650,3 +3646,14 @@
 {
 	return adapter_ops->set_did(adapter->dev_id, vendor, product, version);
 }
+
+int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+								uint8_t io_cap)
+{
+	return adapter_ops->create_bonding(adapter->dev_id, bdaddr, io_cap);
+}
+
+int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+	return adapter_ops->cancel_bonding(adapter->dev_id, bdaddr);
+}
diff --git a/src/adapter.h b/src/adapter.h
index b0c7610..18359a9 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -225,7 +225,6 @@
 	int (*pincode_reply) (int index, bdaddr_t *bdaddr, const char *pin);
 	int (*confirm_reply) (int index, bdaddr_t *bdaddr, gboolean success);
 	int (*passkey_reply) (int index, bdaddr_t *bdaddr, uint32_t passkey);
-	int (*get_auth_info) (int index, bdaddr_t *bdaddr, uint8_t *auth);
 	int (*read_scan_enable) (int index);
 	int (*enable_le) (int index);
 	int (*encrypt_link) (int index, bdaddr_t *bdaddr, bt_hci_result_t cb,
@@ -238,6 +237,8 @@
 	int (*restore_powered) (int index);
 	int (*load_keys) (int index, GSList *keys, gboolean debug_keys);
 	int (*set_io_capability) (int index, uint8_t io_capability);
+	int (*create_bonding) (int index, bdaddr_t *bdaddr, uint8_t io_cap);
+	int (*cancel_bonding) (int index, bdaddr_t *bdaddr);
 };
 
 int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority);
@@ -279,9 +280,6 @@
 int btd_adapter_passkey_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
 							uint32_t passkey);
 
-int btd_adapter_get_auth_info(struct btd_adapter *adapter, bdaddr_t *bdaddr,
-								uint8_t *auth);
-
 int btd_adapter_read_scan_enable(struct btd_adapter *adapter);
 
 void btd_adapter_update_local_ext_features(struct btd_adapter *adapter,
@@ -292,3 +290,8 @@
 
 int btd_adapter_set_did(struct btd_adapter *adapter, uint16_t vendor,
 					uint16_t product, uint16_t version);
+
+int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+							uint8_t io_cap);
+
+int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr);
diff --git a/src/device.c b/src/device.c
index bf42b4b..2c6dfc2 100644
--- a/src/device.c
+++ b/src/device.c
@@ -130,10 +130,6 @@
 	struct authentication_req *authr;	/* authentication request */
 	GSList		*disconnects;		/* disconnects message */
 
-	/* For Secure Simple Pairing */
-	uint8_t		cap;
-	uint8_t		auth;
-
 	gboolean	connected;
 
 	/* Whether were creating a security mode 3 connection */
@@ -144,7 +140,6 @@
 	gboolean	trusted;
 	gboolean	paired;
 	gboolean	blocked;
-	gboolean	renewed_key;
 
 	gboolean	authorizing;
 	gint		ref;
@@ -726,12 +721,10 @@
 
 static void bonding_request_cancel(struct bonding_req *bonding)
 {
-	if (!bonding->io)
-		return;
+	struct btd_device *device = bonding->device;
+	struct btd_adapter *adapter = device->adapter;
 
-	g_io_channel_shutdown(bonding->io, TRUE, NULL);
-	g_io_channel_unref(bonding->io);
-	bonding->io = NULL;
+	adapter_cancel_bonding(adapter, &device->bdaddr);
 }
 
 void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
@@ -812,25 +805,6 @@
 	return device->connected;
 }
 
-static void device_set_connected(struct btd_device *device,
-					DBusConnection *conn,
-					gboolean connected)
-{
-	emit_property_changed(conn, device->path, DEVICE_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &connected);
-
-	if (connected && device->secmode3) {
-		struct btd_adapter *adapter = device_get_adapter(device);
-		bdaddr_t sba;
-
-		adapter_get_address(adapter, &sba);
-
-		device->secmode3 = FALSE;
-
-		btd_event_bonding_process_complete(&sba, &device->bdaddr, 0);
-	}
-}
-
 void device_add_connection(struct btd_device *device, DBusConnection *conn)
 {
 	if (device->connected) {
@@ -842,7 +816,9 @@
 
 	device->connected = TRUE;
 
-	device_set_connected(device, conn, TRUE);
+	emit_property_changed(conn, device->path,
+					DEVICE_INTERFACE, "Connected",
+					DBUS_TYPE_BOOLEAN, &device->connected);
 }
 
 void device_remove_connection(struct btd_device *device, DBusConnection *conn)
@@ -868,7 +844,9 @@
 		device->disconnects = g_slist_remove(device->disconnects, msg);
 	}
 
-	device_set_connected(device, conn, FALSE);
+	emit_property_changed(conn, device->path,
+					DEVICE_INTERFACE, "Connected",
+					DBUS_TYPE_BOOLEAN, &device->connected);
 }
 
 guint device_add_disconnect_watch(struct btd_device *device,
@@ -907,16 +885,6 @@
 	}
 }
 
-gboolean device_get_secmode3_conn(struct btd_device *device)
-{
-	return device->secmode3;
-}
-
-void device_set_secmode3_conn(struct btd_device *device, gboolean enable)
-{
-	device->secmode3 = enable;
-}
-
 struct btd_device *device_create(DBusConnection *conn,
 				struct btd_adapter *adapter,
 				const gchar *address, device_type_t type)
@@ -958,8 +926,6 @@
 	if (read_blocked(&src, &device->bdaddr))
 		device_block(conn, device);
 
-	device->auth = 0xff;
-
 	if (read_link_key(&src, &device->bdaddr, NULL, NULL) == 0)
 		device->paired = TRUE;
 
@@ -1778,35 +1744,11 @@
 	if (!device)
 		return;
 
+	DBG("temporary %d", temporary);
+
 	device->temporary = temporary;
 }
 
-void device_set_cap(struct btd_device *device, uint8_t cap)
-{
-	if (!device)
-		return;
-
-	device->cap = cap;
-}
-
-uint8_t device_get_cap(struct btd_device *device)
-{
-	return device->cap;
-}
-
-void device_set_auth(struct btd_device *device, uint8_t auth)
-{
-	if (!device)
-		return;
-
-	device->auth = auth;
-}
-
-uint8_t device_get_auth(struct btd_device *device)
-{
-	return device->auth;
-}
-
 void device_set_type(struct btd_device *device, device_type_t type)
 {
 	if (!device)
@@ -1983,40 +1925,6 @@
 	return bonding;
 }
 
-static void bonding_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
-{
-	struct btd_device *device = user_data;
-	uint16_t handle;
-
-	if (!device->bonding) {
-		if (!err)
-			g_io_channel_shutdown(io, TRUE, NULL);
-		return;
-	}
-
-	if (err)
-		/* Wait proper error to be propagated by bonding complete */
-		return;
-
-	if (!bt_io_get(io, BT_IO_L2RAW, &err,
-			BT_IO_OPT_HANDLE, &handle,
-			BT_IO_OPT_INVALID)) {
-		error("Unable to get connection handle: %s", err->message);
-		g_error_free(err);
-		goto failed;
-	}
-
-	if (btd_adapter_request_authentication(device->adapter,
-							&device->bdaddr) < 0)
-		goto failed;
-
-	return;
-
-failed:
-	g_io_channel_shutdown(io, TRUE, NULL);
-	device_cancel_bonding(device, HCI_UNSPECIFIED_ERROR);
-}
-
 static void create_bond_req_exit(DBusConnection *conn, void *user_data)
 {
 	struct btd_device *device = user_data;
@@ -2045,9 +1953,7 @@
 	struct btd_adapter *adapter = device->adapter;
 	struct bonding_req *bonding;
 	bdaddr_t src;
-	GError *err = NULL;
-	GIOChannel *io;
-	BtIOSecLevel sec_level;
+	int err;
 
 	adapter_get_address(adapter, &src);
 	ba2str(&src, srcaddr);
@@ -2066,37 +1972,17 @@
 		return btd_error_already_exists(msg);
 	}
 
-	/* If our IO capability is NoInputNoOutput use medium security
-	 * level (i.e. don't require MITM protection) else use high
-	 * security level */
-	if (capability == 0x03)
-		sec_level = BT_IO_SEC_MEDIUM;
-	else
-		sec_level = BT_IO_SEC_HIGH;
-
-	io = bt_io_connect(BT_IO_L2RAW, bonding_connect_cb, device,
-				NULL, &err,
-				BT_IO_OPT_SOURCE_BDADDR, &src,
-				BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
-				BT_IO_OPT_SEC_LEVEL, sec_level,
-				BT_IO_OPT_INVALID);
-	if (io == NULL) {
-		DBusMessage *reply;
-		reply = btd_error_failed(msg, err->message);
-		error("bt_io_connect: %s", err->message);
-		g_error_free(err);
-		return reply;
-	}
+	err = adapter_create_bonding(adapter, &device->bdaddr, capability);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
 
 	bonding = bonding_request_new(conn, msg, device, agent_path,
 					capability);
 	if (!bonding) {
-		g_io_channel_shutdown(io, TRUE, NULL);
+		adapter_cancel_bonding(adapter, &device->bdaddr);
 		return NULL;
 	}
 
-	bonding->io = io;
-
 	bonding->listener_id = g_dbus_add_disconnect_watch(conn,
 						dbus_message_get_sender(msg),
 						create_bond_req_exit, device,
@@ -2116,7 +2002,7 @@
 		agent_cancel(auth->agent);
 }
 
-void device_authentication_complete(struct btd_device *device)
+static void device_authentication_complete(struct btd_device *device)
 {
 	g_free(device->authr);
 	device->authr = NULL;
@@ -2127,6 +2013,8 @@
 	struct bonding_req *bonding = device->bonding;
 	struct authentication_req *auth = device->authr;
 
+	DBG("bonding %p status 0x%02x", bonding, status);
+
 	if (auth && auth->type == AUTH_TYPE_NOTIFY && auth->agent)
 		agent_cancel(auth->agent);
 
@@ -2136,14 +2024,13 @@
 		return;
 	}
 
-	device->auth = 0xff;
-
 	device_authentication_complete(device);
 
-	if (device->renewed_key)
+	/* If we're already paired nothing more is needed */
+	if (device->paired)
 		return;
 
-	device_set_temporary(device, FALSE);
+	device_set_paired(device, TRUE);
 
 	/* If we were initiators start service discovery immediately.
 	 * However if the other end was the initator wait a few seconds
@@ -2411,11 +2298,6 @@
 	device->authorizing = auth;
 }
 
-void device_set_renewed_key(struct btd_device *device, gboolean renewed)
-{
-	device->renewed_key = renewed;
-}
-
 void btd_device_add_service(struct btd_device *device, const char *path)
 {
 	if (g_slist_find_custom(device->services, path, (GCompareFunc) strcmp))
diff --git a/src/device.h b/src/device.h
index f1bdd39..c77fc8d 100644
--- a/src/device.h
+++ b/src/device.h
@@ -77,14 +77,11 @@
 void device_set_auth(struct btd_device *device, uint8_t auth);
 uint8_t device_get_auth(struct btd_device *device);
 gboolean device_is_connected(struct btd_device *device);
-gboolean device_get_secmode3_conn(struct btd_device *device);
-void device_set_secmode3_conn(struct btd_device *device, gboolean enable);
 DBusMessage *device_create_bonding(struct btd_device *device,
 				DBusConnection *conn, DBusMessage *msg,
 				const char *agent_path, uint8_t capability);
 void device_remove_bonding(struct btd_device *device);
 void device_bonding_complete(struct btd_device *device, uint8_t status);
-void device_authentication_complete(struct btd_device *device);
 void device_simple_pairing_complete(struct btd_device *device, uint8_t status);
 gboolean device_is_creating(struct btd_device *device, const char *sender);
 gboolean device_is_bonding(struct btd_device *device, const char *sender);
@@ -95,9 +92,6 @@
 gboolean device_is_authenticating(struct btd_device *device);
 gboolean device_is_authorizing(struct btd_device *device);
 void device_set_authorizing(struct btd_device *device, gboolean auth);
-void device_set_renewed_key(struct btd_device *device, gboolean renewed);
-gboolean device_set_debug_key(struct btd_device *device, uint8_t *key);
-gboolean device_get_debug_key(struct btd_device *device, uint8_t *key);
 void device_add_connection(struct btd_device *device, DBusConnection *conn);
 void device_remove_connection(struct btd_device *device, DBusConnection *conn);
 void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
diff --git a/src/event.c b/src/event.c
index 11f153e..fc31964 100644
--- a/src/event.c
+++ b/src/event.c
@@ -139,11 +139,6 @@
 	if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
 		return -ENODEV;
 
-	/* Check if the adapter is not pairable and if there isn't a bonding in
-	 * progress */
-	if (!adapter_is_pairable(adapter) && !device_is_bonding(device, NULL))
-		return -EPERM;
-
 	memset(pin, 0, sizeof(pin));
 	pinlen = read_pin_code(sba, dba, pin);
 	if (pinlen > 0) {
@@ -189,52 +184,16 @@
 	btd_adapter_passkey_reply(adapter, &bdaddr, passkey);
 }
 
-int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey)
+int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey,
+							gboolean auto_accept)
 {
 	struct btd_adapter *adapter;
 	struct btd_device *device;
-	struct agent *agent;
-	uint8_t rem_cap, rem_auth, loc_cap, loc_auth;
-	gboolean bonding_initiator;
 
 	if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
 		return -ENODEV;
 
-	if (btd_adapter_get_auth_info(adapter, dba, &loc_auth) < 0) {
-		error("Unable to get local authentication requirements");
-		goto fail;
-	}
-
-	agent = device_get_agent(device);
-	if (agent == NULL) {
-		error("No agent available for user confirmation");
-		goto fail;
-	}
-
-	loc_cap = agent_get_io_capability(agent);
-
-	DBG("confirm IO capabilities are 0x%02x", loc_cap);
-	DBG("confirm authentication requirement is 0x%02x", loc_auth);
-
-	rem_cap = device_get_cap(device);
-	rem_auth = device_get_auth(device);
-
-	DBG("remote IO capabilities are 0x%02x", rem_cap);
-	DBG("remote authentication requirement is 0x%02x", rem_auth);
-
-	/* If we require MITM but the remote device can't provide that
-	 * (it has NoInputNoOutput) then reject the confirmation
-	 * request. The only exception is when we're dedicated bonding
-	 * initiators since then we always have the MITM bit set. */
-	bonding_initiator = device_is_bonding(device, NULL);
-	if (!bonding_initiator && (loc_auth & 0x01) && rem_cap == 0x03) {
-		error("Rejecting request: remote device can't provide MITM");
-		goto fail;
-	}
-
-	/* If no side requires MITM protection; auto-accept */
-	if ((loc_auth == 0xff || !(loc_auth & 0x01) || rem_cap == 0x03) &&
-				(!(rem_auth & 0x01) || loc_cap == 0x03)) {
+	if (auto_accept) {
 		DBG("auto accept of confirmation");
 
 		/* Wait 5 milliseconds before doing auto-accept */
@@ -249,9 +208,6 @@
 
 	return device_request_authentication(device, AUTH_TYPE_CONFIRM,
 							passkey, confirm_cb);
-
-fail:
-	return confirm_reply(adapter, device, FALSE);
 }
 
 int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba)
@@ -285,7 +241,7 @@
 	struct btd_device *device;
 	gboolean create;
 
-	DBG("status=%02x", status);
+	DBG("status 0x%02x", status);
 
 	create = status ? FALSE : TRUE;
 
@@ -295,9 +251,6 @@
 	if (!device)
 		return;
 
-	if (status == 0)
-		device_set_paired(device, TRUE);
-
 	if (!device_is_authenticating(device)) {
 		/* This means that there was no pending PIN or SSP token
 		 * request from the controller, i.e. this is not a new
@@ -695,94 +648,23 @@
 
 int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer,
 				uint8_t *key, uint8_t key_type,
-				uint8_t pin_length, uint8_t old_key_type)
+				uint8_t pin_length)
 {
-	struct btd_device *device;
 	struct btd_adapter *adapter;
-	uint8_t local_auth = 0xff, remote_auth, new_key_type;
-	gboolean temporary = FALSE;
+	struct btd_device *device;
+	int ret;
 
 	if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
 		return -ENODEV;
 
-	remote_auth = device_get_auth(device);
+	DBG("storing link key of type 0x%02x", key_type);
 
-	new_key_type = key_type;
+	ret = write_link_key(local, peer, key, key_type, pin_length);
 
-	if (key_type == 0x06) {
-		/* Some buggy controller combinations generate a changed
-		 * combination key for legacy pairing even when there's no
-		 * previous key */
-		if (remote_auth == 0xff && old_key_type == 0xff)
-			new_key_type = key_type = 0x00;
-		else if (old_key_type != 0xff)
-			new_key_type = old_key_type;
-		else
-			/* This is Changed Combination Link Key for
-			 * a temporary link key.*/
-			return 0;
-	}
+	if (ret == 0 && device_is_temporary(device))
+		device_set_temporary(device, FALSE);
 
-	btd_adapter_get_auth_info(adapter, peer, &local_auth);
-
-	DBG("key type 0x%02x old key type 0x%02x new key type 0x%02x",
-					key_type, old_key_type, new_key_type);
-
-	DBG("local auth 0x%02x and remote auth 0x%02x",
-					local_auth, remote_auth);
-
-	/* If this is not the first link key set a flag so a subsequent auth
-	 * complete event doesn't trigger SDP and remove any stored key */
-	if (old_key_type != 0xff) {
-		device_set_renewed_key(device, TRUE);
-		device_remove_bonding(device);
-	}
-
-	/* Skip the storage check if this is a debug key */
-	if (new_key_type == 0x03)
-		goto proceed;
-
-	/* Store the link key persistently if one of the following is true:
-	 * 1. this is a legacy link key
-	 * 2. this is a changed combination key and there was a previously
-	 *    stored one
-	 * 3. neither local nor remote side had no-bonding as a requirement
-	 * 4. the local side had dedicated bonding as a requirement
-	 * 5. the remote side is using dedicated bonding since in that case
-	 *    also the local requirements are set to dedicated bonding
-	 * If none of the above match only keep the link key around for
-	 * this connection and set the temporary flag for the device.
-	 */
-	if (key_type < 0x03 || (key_type == 0x06 && old_key_type != 0xff) ||
-				(local_auth > 0x01 && remote_auth > 0x01) ||
-				(local_auth == 0x02 || local_auth == 0x03) ||
-				(remote_auth == 0x02 || remote_auth == 0x03)) {
-		int err;
-
-		DBG("storing link key of type 0x%02x", key_type);
-
-		err = write_link_key(local, peer, key, new_key_type,
-								pin_length);
-		if (err < 0) {
-			error("write_link_key: %s (%d)", strerror(-err), -err);
-			return err;
-		}
-	} else
-		temporary = TRUE;
-
-proceed:
-	if (!device_is_connected(device))
-		device_set_secmode3_conn(device, TRUE);
-	else if (!device_is_bonding(device, NULL)) {
-		if (old_key_type == 0xff)
-			btd_event_bonding_process_complete(local, peer, 0);
-		else
-			device_authentication_complete(device);
-	}
-
-	device_set_temporary(device, temporary);
-
-	return 0;
+	return ret;
 }
 
 void btd_event_conn_complete(bdaddr_t *local, uint8_t status, bdaddr_t *peer)
@@ -795,14 +677,10 @@
 		return;
 
 	if (status) {
-		gboolean secmode3 = device_get_secmode3_conn(device);
-
-		device_set_secmode3_conn(device, FALSE);
-
 		if (device_is_bonding(device, NULL))
 			device_bonding_complete(device, status);
 		if (device_is_temporary(device))
-			adapter_remove_device(conn, adapter, device, secmode3);
+			adapter_remove_device(conn, adapter, device, TRUE);
 		return;
 	}
 
@@ -816,6 +694,8 @@
 	struct btd_adapter *adapter;
 	struct btd_device *device;
 
+	DBG("");
+
 	if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
 		return;
 
@@ -877,120 +757,3 @@
 
 	device_set_paired(device, TRUE);
 }
-
-int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
-						uint8_t *cap, uint8_t *auth)
-{
-	struct btd_adapter *adapter;
-	struct btd_device *device;
-	struct agent *agent = NULL;
-	uint8_t agent_cap;
-	int err;
-
-	if (!get_adapter_and_device(local, remote, &adapter, &device, TRUE))
-		return -ENODEV;
-
-	err = btd_adapter_get_auth_info(adapter, remote, auth);
-	if (err < 0)
-		return err;
-
-	DBG("initial authentication requirement is 0x%02x", *auth);
-
-	if (*auth == 0xff)
-		*auth = device_get_auth(device);
-
-	/* Check if the adapter is not pairable and if there isn't a bonding
-	 * in progress */
-	if (!adapter_is_pairable(adapter) &&
-				!device_is_bonding(device, NULL)) {
-		if (device_get_auth(device) < 0x02) {
-			DBG("Allowing no bonding in non-bondable mode");
-			/* No input, no output */
-			*cap = 0x03;
-			/* Kernel defaults to general bonding and so
-			 * overwrite for this special case. Otherwise
-			 * non-pairable test cases will fail. */
-			*auth = 0x00;
-			goto done;
-		}
-		return -EPERM;
-	}
-
-	/* For CreatePairedDevice use dedicated bonding */
-	agent = device_get_agent(device);
-	if (!agent) {
-		/* This is the non bondable mode case */
-		if (device_get_auth(device) > 0x01) {
-			DBG("Bonding request, but no agent present");
-			return -1;
-		}
-
-		/* No agent available, and no bonding case */
-		if (*auth == 0x00 || *auth == 0x04) {
-			DBG("Allowing no bonding without agent");
-			/* No input, no output */
-			*cap = 0x03;
-			/* If kernel defaults to general bonding, set it
-			 * back to no bonding */
-			*auth = 0x00;
-			goto done;
-		}
-
-		error("No agent available for IO capability");
-		return -1;
-	}
-
-	agent_cap = agent_get_io_capability(agent);
-
-	if (*auth == 0x00 || *auth == 0x04) {
-		/* If remote requests dedicated bonding follow that lead */
-		if (device_get_auth(device) == 0x02 ||
-				device_get_auth(device) == 0x03) {
-
-			/* If both remote and local IO capabilities allow MITM
-			 * then require it, otherwise don't */
-			if (device_get_cap(device) == 0x03 ||
-							agent_cap == 0x03)
-				*auth = 0x02;
-			else
-				*auth = 0x03;
-		}
-
-		/* If remote indicates no bonding then follow that. This
-		 * is important since the kernel might give general bonding
-		 * as default. */
-		if (device_get_auth(device) == 0x00 ||
-					device_get_auth(device) == 0x01)
-			*auth = 0x00;
-
-		/* If remote requires MITM then also require it, unless
-		 * our IO capability is NoInputNoOutput (so some
-		 * just-works security cases can be tested) */
-		if (device_get_auth(device) != 0xff &&
-					(device_get_auth(device) & 0x01) &&
-					agent_cap != 0x03)
-			*auth |= 0x01;
-	}
-
-	*cap = agent_get_io_capability(agent);
-
-done:
-	DBG("final authentication requirement is 0x%02x", *auth);
-
-	return 0;
-}
-
-int btd_event_set_io_cap(bdaddr_t *local, bdaddr_t *remote,
-						uint8_t cap, uint8_t auth)
-{
-	struct btd_adapter *adapter;
-	struct btd_device *device;
-
-	if (!get_adapter_and_device(local, remote, &adapter, &device, TRUE))
-		return -ENODEV;
-
-	device_set_cap(device, cap);
-	device_set_auth(device, auth);
-
-	return 0;
-}
diff --git a/src/event.h b/src/event.h
index ab51018..6d00df6 100644
--- a/src/event.h
+++ b/src/event.h
@@ -36,13 +36,9 @@
 void btd_event_setscan_enable_complete(bdaddr_t *local);
 void btd_event_le_set_scan_enable_complete(bdaddr_t *local, uint8_t status);
 void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer);
-int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
-						uint8_t *cap, uint8_t *auth);
-int btd_event_set_io_cap(bdaddr_t *local, bdaddr_t *remote,
-						uint8_t cap, uint8_t auth);
-int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
+int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey,
+							gboolean auto_accept);
 int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba);
 int btd_event_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
-int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer,
-				uint8_t *key, uint8_t key_type,
-				uint8_t pin_length, uint8_t old_key_type);
+int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t *key,
+					uint8_t key_type, uint8_t pin_length);