Add APIs for OOB Simple Secure pairing.

Change-Id: Iddc784ef4d0b6e71d15a91061eca51ccbd8c266e
diff --git a/src/adapter.c b/src/adapter.c
index c6dbd05..16334ec 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1630,8 +1630,8 @@
 	return IO_CAPABILITY_INVALID;
 }
 
-static DBusMessage *create_paired_device(DBusConnection *conn,
-					DBusMessage *msg, void *data)
+static DBusMessage *create_paired_device_generic(DBusConnection *conn,
+						DBusMessage *msg, void *data, const gboolean oob)
 {
 	struct btd_adapter *adapter = data;
 	struct btd_device *device;
@@ -1664,7 +1664,19 @@
 				ERROR_INTERFACE ".Failed",
 				"Unable to create a new device object");
 
-	return device_create_bonding(device, conn, msg, agent_path, cap);
+	return device_create_bonding(device, conn, msg, agent_path, cap, oob);
+}
+
+static DBusMessage *create_paired_device_oob(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	return create_paired_device_generic(conn, msg, data, TRUE);
+}
+
+static DBusMessage *create_paired_device(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	return create_paired_device_generic(conn, msg, data, FALSE);
 }
 
 static gint device_path_cmp(struct btd_device *device, const gchar *path)
@@ -1751,18 +1763,16 @@
 	adapter->agent = NULL;
 }
 
-static DBusMessage *register_agent(DBusConnection *conn, DBusMessage *msg,
-								void *data)
+
+static DBusMessage *register_agent_generic(DBusConnection *conn, DBusMessage *msg,
+					void *data, const char *path, const char *capability,
+					const gboolean oob)
 {
-	const char *path, *name, *capability;
+	const char *name;
 	struct agent *agent;
 	struct btd_adapter *adapter = data;
 	uint8_t cap;
 
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-			DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID))
-		return NULL;
-
 	if (adapter->agent)
 		return g_dbus_create_error(msg,
 				ERROR_INTERFACE ".AlreadyExists",
@@ -1774,7 +1784,7 @@
 
 	name = dbus_message_get_sender(msg);
 
-	agent = agent_create(adapter, name, path, cap,
+	agent = agent_create(adapter, name, path, cap, oob,
 				(agent_remove_cb) agent_removed, adapter);
 	if (!agent)
 		return g_dbus_create_error(msg,
@@ -1789,6 +1799,32 @@
 	return dbus_message_new_method_return(msg);
 }
 
+static DBusMessage *register_agent_oob(DBusConnection *conn, DBusMessage *msg,
+					 void *data)
+{
+	const char *path, *capability;
+	gboolean oob;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_STRING, &capability,
+					DBUS_TYPE_BOOLEAN, &oob, DBUS_TYPE_INVALID))
+		return NULL;
+
+	return register_agent_generic(conn, msg, data, path, capability, oob);
+}
+
+static DBusMessage *register_agent(DBusConnection *conn, DBusMessage *msg,
+					 void *data)
+{
+	const char *path, *capability;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID))
+		return NULL;
+
+	return register_agent_generic(conn, msg, data, path, capability, FALSE);
+}
+
 static DBusMessage *unregister_agent(DBusConnection *conn, DBusMessage *msg,
 								void *data)
 {
@@ -1914,6 +1950,44 @@
 	return dbus_message_new_method_return(msg);
 }
 
+static DBusMessage *read_local_oob_data(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct btd_adapter *adapter = data;
+	DBusMessage *reply;
+
+	uint8_t hash[16], randomizer[16];
+	uint8_t *hash_ptr = hash;
+	uint8_t *r_ptr = randomizer;
+	int dd, err;
+
+	dd = hci_open_dev(adapter->dev_id);
+
+	if (dd < 0) {
+		err = -errno;
+		goto fail;
+	}
+
+	err = hci_read_local_oob_data(dd, hash, randomizer, HCI_REQ_TIMEOUT);
+
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = -errno;
+		goto fail;
+	}
+
+	reply = dbus_message_new_method_return(msg);
+	dbus_message_append_args(reply,
+				DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash_ptr, 16,
+				DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &r_ptr, 16,
+				DBUS_TYPE_INVALID);
+
+	return reply;
+fail:
+	return failed_strerror(msg, errno);
+}
+
 static DBusMessage *set_link_timeout(DBusConnection *conn,
 					DBusMessage *msg, void *data)
 {
@@ -1973,12 +2047,16 @@
 						G_DBUS_METHOD_FLAG_ASYNC},
 	{ "CreatePairedDevice",	"sos",	"o",	create_paired_device,
 						G_DBUS_METHOD_FLAG_ASYNC},
+	{ "CreatePairedDeviceOutOfBand",	"sos",	"o",	create_paired_device_oob,
+						G_DBUS_METHOD_FLAG_ASYNC},
 	{ "CancelDeviceCreation","s",	"",	cancel_device_creation,
 						G_DBUS_METHOD_FLAG_ASYNC},
 	{ "RemoveDevice",	"o",	"",	remove_device,
 						G_DBUS_METHOD_FLAG_ASYNC},
 	{ "FindDevice",		"s",	"o",	find_device		},
+	{ "ReadLocalOutOfBandData", "", "ayay",	read_local_oob_data},
 	{ "RegisterAgent",	"os",	"",	register_agent		},
+	{ "RegisterAgent",	"osb",	"",	register_agent_oob	},
 	{ "UnregisterAgent",	"o",	"",	unregister_agent	},
 	{ "AddRfcommServiceRecord",	"sttq",	"u",	add_rfcomm_service_record },
 	{ "RemoveServiceRecord",	"u",	"",	remove_service_record },
diff --git a/src/agent.c b/src/agent.c
index efa0320..5097241 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -56,6 +56,8 @@
 	AGENT_REQUEST_PINCODE,
 	AGENT_REQUEST_AUTHORIZE,
 	AGENT_REQUEST_CONFIRM_MODE,
+	AGENT_REQUEST_OOB_AVAILABILITY,
+	AGENT_REQUEST_OOB_DATA,
 	AGENT_REQUEST_PAIRING_CONSENT,
 } agent_request_type_t;
 
@@ -64,6 +66,7 @@
 	char *name;
 	char *path;
 	uint8_t capability;
+	gboolean oob;
 	struct agent_request *request;
 	int exited;
 	agent_remove_cb remove_cb;
@@ -189,7 +192,8 @@
 
 struct agent *agent_create(struct btd_adapter *adapter, const char *name,
 				const char *path, uint8_t capability,
-				agent_remove_cb cb, void *remove_cb_data)
+				gboolean oob, agent_remove_cb cb,
+				void *remove_cb_data)
 {
 	struct agent *agent;
 
@@ -199,6 +203,7 @@
 	agent->name = g_strdup(name);
 	agent->path = g_strdup(path);
 	agent->capability = capability;
+	agent->oob = oob;
 	agent->remove_cb = cb;
 	agent->remove_cb_data = remove_cb_data;
 
@@ -355,6 +360,61 @@
 	return 0;
 }
 
+
+static int agent_call_oob_availability(struct agent_request *req,
+					const char *device_path)
+{
+	struct agent *agent = req->agent;
+
+	req->msg = dbus_message_new_method_call(agent->name, agent->path,
+				"org.bluez.Agent", "OutOfBandAvailable");
+	if (!req->msg) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	dbus_message_append_args(req->msg,
+				DBUS_TYPE_OBJECT_PATH, &device_path,
+				DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, req->msg,
+					&req->call, REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+	return 0;
+}
+
+int agent_request_oob_availability(struct agent *agent,
+					const char *path,
+					agent_cb cb,
+					void *user_data,
+					GDestroyNotify destroy)
+{
+	struct agent_request *req;
+	int err;
+
+	if (agent->request)
+		return -EBUSY;
+
+	req = agent_request_new(agent, AGENT_REQUEST_OOB_AVAILABILITY, cb,
+							user_data, destroy);
+
+	err = agent_call_oob_availability(req, path);
+	if (err < 0) {
+		agent_request_free(req, FALSE);
+		return -ENOMEM;
+	}
+
+	agent->request = req;
+
+	DBG("oob availability request was sent for %s", path);
+
+	return 0;
+}
+
 static void pincode_reply(DBusPendingCall *call, void *user_data)
 {
 	struct agent_request *req = user_data;
@@ -637,6 +697,112 @@
 	return err;
 }
 
+static void oob_data_reply(DBusPendingCall *call, void *user_data)
+{
+	struct agent_request *req = user_data;
+	struct agent *agent = req->agent;
+	agent_oob_data_cb cb = req->cb;
+	DBusMessage *message;
+	DBusError err;
+	uint8_t *hash_ptr, *r_ptr;
+        uint8_t hash_len, r_len;
+
+	/* steal_reply will always return non-NULL since the callback
+	 * is only called after a reply has been received */
+	message = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, message)) {
+		if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
+				g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
+				request_fallback(req, oob_data_reply) == 0) {
+			dbus_error_free(&err);
+			return;
+		}
+
+		error("Agent replied with an error: %s, %s",
+				err.name, err.message);
+		cb(agent, &err, 0, 0, req->user_data);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	if (!dbus_message_get_args(message, &err,
+				DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash_ptr, &hash_len,
+				DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &r_ptr, &r_len,
+				DBUS_TYPE_INVALID)) {
+		error("Wrong OOB data reply signature: %s", err.message);
+		cb(agent, &err, 0, 0, req->user_data);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	cb(agent, NULL, hash_ptr, r_ptr, req->user_data);
+
+done:
+	if (message)
+		dbus_message_unref(message);
+
+	dbus_pending_call_cancel(req->call);
+	agent->request = NULL;
+	agent_request_free(req, TRUE);
+}
+
+static int oob_data_request_new(struct agent_request *req,
+				const char *device_path)
+{
+	struct agent *agent = req->agent;
+
+	req->msg = dbus_message_new_method_call(agent->name, agent->path,
+					"org.bluez.Agent", "RequestOobData");
+	if (req->msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, req->msg,
+					&req->call, REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(req->call, oob_data_reply, req, NULL);
+	return 0;
+}
+
+int agent_request_oob_data(struct agent *agent, struct btd_device *device,
+				agent_oob_data_cb cb, void *user_data,
+				GDestroyNotify destroy)
+{
+	struct agent_request *req;
+	const gchar *dev_path = device_get_path(device);
+	int err;
+
+	if (agent->request)
+		return -EBUSY;
+
+	DBG("Calling Agent.RequestOobData: name=%s, path=%s",
+			agent->name, agent->path);
+
+	req = agent_request_new(agent, AGENT_REQUEST_OOB_DATA, cb,
+							user_data, destroy);
+
+	err = oob_data_request_new(req, dev_path);
+	if (err < 0)
+		goto failed;
+
+	agent->request = req;
+
+	return 0;
+
+failed:
+	agent_request_free(req, FALSE);
+	return err;
+}
+
 static int confirmation_request_new(struct agent_request *req,
 					const char *device_path,
 					uint32_t passkey)
@@ -821,6 +987,11 @@
 	return agent->capability;
 }
 
+gboolean agent_get_oob_capability(struct agent *agent)
+{
+	return agent->oob;
+}
+
 gboolean agent_matches(struct agent *agent, const char *name, const char *path)
 {
 	if (g_str_equal(agent->name, name) && g_str_equal(agent->path, path))
diff --git a/src/agent.h b/src/agent.h
index e184250..2f743b8 100644
--- a/src/agent.h
+++ b/src/agent.h
@@ -33,11 +33,16 @@
 typedef void (*agent_passkey_cb) (struct agent *agent, DBusError *err,
 					uint32_t passkey, void *user_data);
 
+typedef void (*agent_oob_data_cb) (struct agent *agent, DBusError *err,
+					uint8_t *hash, uint8_t *randomizer,
+					void *user_data);
+
 typedef void (*agent_remove_cb) (struct agent *agent, void *user_data);
 
 struct agent *agent_create(struct btd_adapter *adapter, const char *name,
 				const char *path, uint8_t capability,
-				agent_remove_cb cb, void *remove_cb_data);
+				gboolean oob, agent_remove_cb cb,
+				void *remove_cb_data);
 
 void agent_free(struct agent *agent);
 
@@ -57,6 +62,14 @@
 				agent_passkey_cb cb, void *user_data,
 				GDestroyNotify destroy);
 
+int agent_request_oob_data(struct agent *agent, struct btd_device *device,
+				agent_oob_data_cb cb, void *user_data,
+				GDestroyNotify destroy);
+
+int agent_request_oob_availability(struct agent *agent,
+                                        const char *path, agent_cb cb,
+                                        void *user_data, GDestroyNotify destroy);
+
 int agent_request_confirmation(struct agent *agent, struct btd_device *device,
 				uint32_t passkey, agent_cb cb,
 				void *user_data, GDestroyNotify destroy);
@@ -69,6 +82,7 @@
 gboolean agent_is_busy(struct agent *agent, void *user_data);
 
 uint8_t agent_get_io_capability(struct agent *agent);
+gboolean agent_get_oob_capability(struct agent *agent);
 
 gboolean agent_matches(struct agent *agent, const char *name, const char *path);
 
diff --git a/src/dbus-hci.c b/src/dbus-hci.c
index 79bee44..7077b1f 100644
--- a/src/dbus-hci.c
+++ b/src/dbus-hci.c
@@ -59,6 +59,12 @@
 #include "storage.h"
 #include "dbus-hci.h"
 
+struct oob_availability_req {
+        struct btd_device *device;
+        uint8_t auth;
+        uint8_t capa;
+};
+
 static DBusConnection *connection = NULL;
 
 gboolean get_adapter_and_device(bdaddr_t *src, bdaddr_t *dst,
@@ -287,6 +293,82 @@
 	hci_close_dev(dd);
 }
 
+static void oob_data_cb(struct  agent *agent, DBusError *err, uint8_t *hash,
+				uint8_t *randomizer, void *user_data)
+{
+	struct btd_device *device = user_data;
+	struct btd_adapter *adapter = device_get_adapter(device);
+	remote_oob_data_reply_cp cp;
+	bdaddr_t dba;
+	int dd;
+	uint16_t dev_id = adapter_get_dev_id(adapter);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		error("Unable to open hci%d", dev_id);
+		return;
+	}
+
+	device_get_address(device, &dba);
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, &dba);
+
+	memcpy(&cp.hash, hash, 16);
+	memcpy(&cp.randomizer, randomizer, 16);
+
+	if (err)
+                hci_send_cmd(dd, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_NEG_REPLY,
+                                6, &dba);
+
+	else
+                hci_send_cmd(dd, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_REPLY,
+                                REMOTE_OOB_DATA_REPLY_CP_SIZE, &cp);
+
+	hci_close_dev(dd);
+}
+
+static void io_capa_oob_response(struct btd_adapter *adapter, struct btd_device *device,
+					uint8_t cap, uint8_t auth, gboolean oob)
+{
+        io_capability_reply_cp cp;
+	int dd;
+	uint16_t dev_id = adapter_get_dev_id(adapter);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		error("Unable to open hci%d", dev_id);
+		return;
+	}
+	memset(&cp, 0, sizeof(cp));
+	device_get_address(device, &cp.bdaddr);
+
+	cp.capability = cap;
+	if (oob)
+		cp.oob_data = 0x01;
+	else
+		cp.oob_data = 0x00;
+	cp.authentication = auth;
+
+	hci_send_cmd(dd, OGF_LINK_CTL, OCF_IO_CAPABILITY_REPLY,
+			IO_CAPABILITY_REPLY_CP_SIZE, &cp);
+	hci_close_dev(dd);
+}
+
+static void oob_availability_cb(struct agent *agent, DBusError *err,
+					void *user_data)
+{
+	struct oob_availability_req *oob = user_data;
+	struct btd_device *device = oob->device;
+	struct btd_adapter *adapter = device_get_adapter(device);
+
+	if (err) {
+		io_capa_oob_response(adapter, device, oob->capa, oob->auth, FALSE);
+	} else {
+		io_capa_oob_response(adapter, device, oob->capa, oob->auth, TRUE);
+	}
+}
+
+
 static void pairing_consent_cb(struct agent *agent, DBusError *err,
 					void *user_data)
 {
@@ -792,6 +874,25 @@
 	return 0;
 }
 
+int hcid_dbus_get_oob_data(bdaddr_t *sba, bdaddr_t * dba)
+{
+	struct btd_adapter *adapter;
+	struct btd_device *device;
+	struct agent *agent = NULL;
+
+        if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
+	        return -ENODEV;
+
+	agent = device_get_agent(device);
+	if (agent == NULL) {
+		error("No agent available for device");
+		return -1;
+	}
+
+	return device_request_authentication(device, AUTH_TYPE_OOB, 0,
+						oob_data_cb);
+}
+
 void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle,
 				bdaddr_t *peer)
 {
@@ -944,24 +1045,26 @@
 	device_set_paired(device, TRUE);
 }
 
-int hcid_dbus_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
-						uint8_t *cap, uint8_t *auth)
+int hcid_dbus_get_io_cap(bdaddr_t *local, bdaddr_t *remote)
 {
 	struct btd_adapter *adapter;
 	struct btd_device *device;
+	struct oob_availability_req *oob_req;
 	struct agent *agent = NULL;
-	uint8_t agent_cap;
+	uint8_t agent_cap, auth, cap;
+	gboolean oob = FALSE;
+	int ret;
 
 	if (!get_adapter_and_device(local, remote, &adapter, &device, TRUE))
 		return -ENODEV;
 
-	if (get_auth_requirements(local, remote, auth) < 0)
+	if (get_auth_requirements(local, remote, &auth) < 0)
 		return -1;
 
-	DBG("initial authentication requirement is 0x%02x", *auth);
+	DBG("initial authentication requirement is 0x%02x", auth);
 
-	if (*auth == 0xff)
-		*auth = device_get_auth(device);
+	if (auth == 0xff)
+		auth = device_get_auth(device);
 
 	/* Check if the adapter is not pairable and if there isn't a bonding
 	 * in progress */
@@ -970,11 +1073,11 @@
 		if (device_get_auth(device) < 0x02) {
 			DBG("Allowing no bonding in non-bondable mode");
 			/* No input, no output */
-			*cap = 0x03;
+			cap = 0x03;
 			/* Kernel defaults to general bonding and so
 			 * overwrite for this special case. Otherwise
 			 * non-pairable test cases will fail. */
-			*auth = 0x00;
+			auth = 0x00;
 			goto done;
 		}
 		return -EPERM;
@@ -990,13 +1093,13 @@
 		}
 
 		/* No agent available, and no bonding case */
-		if (*auth == 0x00 || *auth == 0x04) {
+		if (auth == 0x00 || auth == 0x04) {
 			DBG("Allowing no bonding without agent");
 			/* No input, no output */
-			*cap = 0x03;
+			cap = 0x03;
 			/* If kernel defaults to general bonding, set it
 			 * back to no bonding */
-			*auth = 0x00;
+			auth = 0x00;
 			goto done;
 		}
 
@@ -1006,7 +1109,7 @@
 
 	agent_cap = agent_get_io_capability(agent);
 
-	if (*auth == 0x00 || *auth == 0x04) {
+	if (auth == 0x00 || auth == 0x04) {
 		/* If remote requests dedicated bonding follow that lead */
 		if (device_get_auth(device) == 0x02 ||
 				device_get_auth(device) == 0x03) {
@@ -1015,9 +1118,9 @@
 			 * then require it, otherwise don't */
 			if (device_get_cap(device) == 0x03 ||
 							agent_cap == 0x03)
-				*auth = 0x02;
+				auth = 0x02;
 			else
-				*auth = 0x03;
+				auth = 0x03;
 		}
 
 		/* If remote indicates no bonding then follow that. This
@@ -1025,7 +1128,7 @@
 		 * as default. */
 		if (device_get_auth(device) == 0x00 ||
 					device_get_auth(device) == 0x01)
-			*auth = 0x00;
+			auth = 0x00;
 
 		/* If remote requires MITM then also require it, unless
 		 * our IO capability is NoInputNoOutput (so some
@@ -1033,14 +1136,32 @@
 		if (device_get_auth(device) != 0xff &&
 					(device_get_auth(device) & 0x01) &&
 					agent_cap != 0x03)
-			*auth |= 0x01;
+			auth |= 0x01;
 	}
 
-	*cap = agent_get_io_capability(agent);
+	DBG("final authentication requirement is 0x%02x", auth);
+	cap = agent_get_io_capability(agent);
+	oob = agent_get_oob_capability(agent);
+
+	// if pairing is not locally initiated
+	if (oob && agent == adapter_get_agent(adapter)) {
+		oob_req = g_new0(struct oob_availability_req, 1);
+		oob_req->device = device;
+		oob_req->auth = auth;
+		oob_req->capa = cap;
+
+		ret = device_request_oob_availability(device, oob_availability_cb,
+							oob_req);
+		if (ret < 0) {
+			g_free(oob_req);
+			oob = FALSE;
+			goto done;
+		}
+		return ret;
+	}
 
 done:
-	DBG("final authentication requirement is 0x%02x", *auth);
-
+	io_capa_oob_response(adapter, device, cap, auth, oob);
 	return 0;
 }
 
diff --git a/src/dbus-hci.h b/src/dbus-hci.h
index 44cd5e5..8ca25aa 100644
--- a/src/dbus-hci.h
+++ b/src/dbus-hci.h
@@ -34,8 +34,7 @@
 void hcid_dbus_setscan_enable_complete(bdaddr_t *local);
 void hcid_dbus_write_simple_pairing_mode_complete(bdaddr_t *local);
 void hcid_dbus_returned_link_key(bdaddr_t *local, bdaddr_t *peer);
-int hcid_dbus_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
-						uint8_t *cap, uint8_t *auth);
+int hcid_dbus_get_io_cap(bdaddr_t *local, bdaddr_t *remote);
 int hcid_dbus_set_io_cap(bdaddr_t *local, bdaddr_t *remote,
 						uint8_t cap, uint8_t auth);
 int hcid_dbus_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
@@ -44,6 +43,7 @@
 int hcid_dbus_link_key_notify(bdaddr_t *local, bdaddr_t *peer,
 				uint8_t *key, uint8_t key_type,
 				int pin_length, uint8_t old_key_type);
+int hcid_dbus_get_oob_data(bdaddr_t *sba, bdaddr_t *dba);
 
 gboolean get_adapter_and_device(bdaddr_t *src, bdaddr_t *dst,
 					struct btd_adapter **adapter,
diff --git a/src/device.c b/src/device.c
index 9d50ad9..53248a5 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1918,7 +1918,8 @@
 						DBusMessage *msg,
 						struct btd_device *device,
 						const char *agent_path,
-						uint8_t capability)
+						uint8_t capability,
+						gboolean oob)
 {
 	struct bonding_req *bonding;
 	const char *name = dbus_message_get_sender(msg);
@@ -1931,6 +1932,7 @@
 
 	agent = agent_create(device->adapter, name, agent_path,
 					capability,
+					oob,
 					device_agent_removed,
 					device);
 	if (!agent) {
@@ -2057,7 +2059,8 @@
 					DBusConnection *conn,
 					DBusMessage *msg,
 					const char *agent_path,
-					uint8_t capability)
+					uint8_t capability,
+					gboolean oob)
 {
 	char filename[PATH_MAX + 1];
 	char *str, srcaddr[18], dstaddr[18];
@@ -2112,7 +2115,7 @@
 	}
 
 	bonding = bonding_request_new(conn, msg, device, agent_path,
-					capability);
+					capability, oob);
 	if (!bonding) {
 		g_io_channel_shutdown(io, TRUE, NULL);
 		return NULL;
@@ -2283,6 +2286,22 @@
 	device->authr->agent = NULL;
 }
 
+static void oob_data_cb(struct agent *agent, DBusError *err,
+				uint8_t *hash, uint8_t *randomizer, void *data)
+{
+	struct authentication_req *auth = data;
+	struct btd_device *device = auth->device;
+
+	/* No need to reply anything if the authentication already failed */
+	if (auth->cb == NULL)
+		return;
+
+	((agent_oob_data_cb) auth->cb)(agent, err, hash, randomizer, device);
+
+	device->authr->cb = NULL;
+	device->authr->agent = NULL;
+}
+
 static void passkey_cb(struct agent *agent, DBusError *err,
 						uint32_t passkey, void *data)
 {
@@ -2313,6 +2332,30 @@
 	auth->cb = NULL;
 }
 
+int device_request_oob_availability(struct btd_device *device,
+						void *cb, void *user_data)
+{
+	struct agent *agent;
+	int err;
+
+	DBG("%s: requesting agent oob availability", device->path);
+
+	agent = device_get_agent(device);
+	if (!agent) {
+		error("No agent available for OOB request");
+		return -EPERM;
+	}
+
+	err = agent_request_oob_availability(agent, device_get_path(device),
+						cb, user_data, g_free);
+
+	if (err < 0) {
+		error("Failed requesting oob availability");
+	}
+	return err;
+}
+
+
 int device_request_authentication(struct btd_device *device, auth_type_t type,
 						uint32_t passkey, void *cb)
 {
@@ -2351,6 +2394,9 @@
 	case AUTH_TYPE_NOTIFY:
 		err = agent_display_passkey(agent, device, passkey);
 		break;
+	case AUTH_TYPE_OOB:
+		err = agent_request_oob_data(agent, device, oob_data_cb, auth, NULL);
+		break;
 	case AUTH_TYPE_AUTO:
 		err = 0;
 		break;
@@ -2396,6 +2442,9 @@
 	case AUTH_TYPE_PASSKEY:
 		((agent_passkey_cb) auth->cb)(agent, &err, 0, device);
 		break;
+	case AUTH_TYPE_OOB:
+		((agent_oob_data_cb) auth->cb)(agent, &err, 0, 0, device);
+		break;
 	case AUTH_TYPE_PAIRING_CONSENT:
 		((agent_cb) auth->cb) (agent, &err, device);
 		break;
diff --git a/src/device.h b/src/device.h
index 752dad3..31d4fcc 100644
--- a/src/device.h
+++ b/src/device.h
@@ -31,6 +31,7 @@
 	AUTH_TYPE_PASSKEY,
 	AUTH_TYPE_CONFIRM,
 	AUTH_TYPE_NOTIFY,
+	AUTH_TYPE_OOB,
 	AUTH_TYPE_AUTO,
 	AUTH_TYPE_PAIRING_CONSENT,
 } auth_type_t;
@@ -66,7 +67,8 @@
 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);
+				const char *agent_path, uint8_t capability,
+				gboolean oob);
 void device_remove_bonding(struct btd_device *device);
 void device_bonding_complete(struct btd_device *device, uint8_t status);
 void device_simple_pairing_complete(struct btd_device *device, uint8_t status);
@@ -75,6 +77,8 @@
 void device_cancel_bonding(struct btd_device *device, uint8_t status);
 int device_request_authentication(struct btd_device *device, auth_type_t type,
 				uint32_t passkey, void *cb);
+int device_request_oob_availability(struct btd_device *device,
+				void *cb, void  *user_data);
 void device_cancel_authentication(struct btd_device *device, gboolean aborted);
 gboolean device_is_authenticating(struct btd_device *device);
 gboolean device_is_authorizing(struct btd_device *device);
diff --git a/src/security.c b/src/security.c
index ca394e1..f91df94 100644
--- a/src/security.c
+++ b/src/security.c
@@ -462,33 +462,28 @@
 
 static void remote_oob_data_request(int dev, bdaddr_t *sba, void *ptr)
 {
-	hci_send_cmd(dev, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_NEG_REPLY, 6, ptr);
+	evt_remote_oob_data_request *req = ptr;
+
+	if (hcid_dbus_get_oob_data(sba, &req->bdaddr) < 0)
+		hci_send_cmd(dev, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_NEG_REPLY,
+				6, ptr);
 }
 
 static void io_capa_request(int dev, bdaddr_t *sba, bdaddr_t *dba)
 {
 	char sa[18], da[18];
-	uint8_t cap, auth;
 
-	ba2str(sba, sa); ba2str(dba, da);
+	ba2str(sba, sa);
+	ba2str(dba, da);
 	info("io_capa_request (sba=%s, dba=%s)", sa, da);
 
-	if (hcid_dbus_get_io_cap(sba, dba, &cap, &auth) < 0) {
+	if (hcid_dbus_get_io_cap(sba, dba) < 0) {
 		io_capability_neg_reply_cp cp;
 		memset(&cp, 0, sizeof(cp));
 		bacpy(&cp.bdaddr, dba);
 		cp.reason = HCI_PAIRING_NOT_ALLOWED;
 		hci_send_cmd(dev, OGF_LINK_CTL, OCF_IO_CAPABILITY_NEG_REPLY,
 					IO_CAPABILITY_NEG_REPLY_CP_SIZE, &cp);
-	} else {
-		io_capability_reply_cp cp;
-		memset(&cp, 0, sizeof(cp));
-		bacpy(&cp.bdaddr, dba);
-		cp.capability = cap;
-		cp.oob_data = 0x00;
-		cp.authentication = auth;
-		hci_send_cmd(dev, OGF_LINK_CTL, OCF_IO_CAPABILITY_REPLY,
-					IO_CAPABILITY_REPLY_CP_SIZE, &cp);
 	}
 }