2016-08-04 13:28:34

by Szymon Janc

[permalink] [raw]
Subject: [RFC 0/5] Rebond support

Hi,

This serie adds support for asking agent for rebond consent. It makes
use of recently added 'Authentication Failed' error code on MGMT
device disconnected event.

If agent gives consent for rebond then device is unpaired and new
pairing is started without need for user to manually remove device
and discover it again.

I'm not fully happy with method name RebondConsent() so I'm open for
suggestions.

Comment are welcome.

Szymon Janc (5):
lib/mgmt: Add MGMT_DEV_DISCONN_AUTH_FAILURE definition
doc: Add RebondConsent method to agent API
core/adapter: Add support for rebond consent
plugins/policy: Disable policies on authentication failure
client: Add support for RebondConsent request

client/agent.c | 22 ++++++++++
doc/agent-api.txt | 8 ++++
lib/mgmt.h | 1 +
plugins/policy.c | 58 ++++++++++++++++++-------
src/adapter.c | 124 +++++++++++++++++++++++++++++++++++++++---------------
src/agent.c | 47 +++++++++++++++++++++
src/agent.h | 4 ++
7 files changed, 215 insertions(+), 49 deletions(-)

--
2.7.4

On behalf of SCHILLER Medical SAS.


2016-08-04 13:28:35

by Szymon Janc

[permalink] [raw]
Subject: [RFC 1/5] lib/mgmt: Add MGMT_DEV_DISCONN_AUTH_FAILURE definition

This error code was recently added to device disconnected event.
---
lib/mgmt.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/lib/mgmt.h b/lib/mgmt.h
index f6a976a..705be07 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -589,6 +589,7 @@ struct mgmt_ev_device_connected {
#define MGMT_DEV_DISCONN_TIMEOUT 0x01
#define MGMT_DEV_DISCONN_LOCAL_HOST 0x02
#define MGMT_DEV_DISCONN_REMOTE 0x03
+#define MGMT_DEV_DISCONN_AUTH_FAILURE 0x04

#define MGMT_EV_DEVICE_DISCONNECTED 0x000C
struct mgmt_ev_device_disconnected {
--
2.7.4


2016-08-04 13:28:39

by Szymon Janc

[permalink] [raw]
Subject: [RFC 5/5] client: Add support for RebondConsent request

This allows to accept or reject rebond consent from bluetoothd.
---
client/agent.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/client/agent.c b/client/agent.c
index 2cbc292..e4b1f01 100644
--- a/client/agent.c
+++ b/client/agent.c
@@ -144,6 +144,8 @@ dbus_bool_t agent_input(DBusConnection *conn, const char *input)
confirm_response(conn, input);
else if (!strcmp(member, "AuthorizeService"))
confirm_response(conn, input);
+ else if (!strcmp(member, "RebondConsent"))
+ confirm_response(conn, input);
else
g_dbus_send_error(conn, pending_message,
"org.bluez.Error.Canceled", NULL);
@@ -310,6 +312,23 @@ static DBusMessage *authorize_service(DBusConnection *conn,
return NULL;
}

+static DBusMessage *rebond_consent(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ const char *device;
+
+ rl_printf("Rebond Consent\n");
+
+ dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
+ DBUS_TYPE_INVALID);
+
+ agent_prompt("Remote Device lost bond. Rebond (yes/no): ");
+
+ pending_message = dbus_message_ref(msg);
+
+ return NULL;
+}
+
static DBusMessage *cancel_request(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -346,6 +365,9 @@ static const GDBusMethodTable methods[] = {
{ GDBUS_ASYNC_METHOD("AuthorizeService",
GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }),
NULL, authorize_service) },
+ { GDBUS_ASYNC_METHOD("RebondConsent",
+ GDBUS_ARGS({ "device", "o" }),
+ NULL, rebond_consent) },
{ GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) },
{ }
};
--
2.7.4


2016-08-04 13:28:37

by Szymon Janc

[permalink] [raw]
Subject: [RFC 3/5] core/adapter: Add support for rebond consent

If device was disconnected with authentication failed error (result of
PIN or Key missing HCI error) send a rebond consent request to agent.
If agent confirms request, unpair device and start new pairing without
need to discover device again.
---
src/adapter.c | 124 ++++++++++++++++++++++++++++++++++++++++++----------------
src/agent.c | 47 ++++++++++++++++++++++
src/agent.h | 4 ++
3 files changed, 141 insertions(+), 34 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index 3742398..704844a 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -6643,6 +6643,69 @@ static void disconnect_notify(struct btd_device *dev, uint8_t reason)
}
}

+static void remove_keys(struct btd_adapter *adapter,
+ struct btd_device *device, uint8_t type)
+{
+ char adapter_addr[18];
+ char device_addr[18];
+ char filename[PATH_MAX];
+ GKeyFile *key_file;
+ gsize length = 0;
+ char *str;
+
+ ba2str(btd_adapter_get_address(adapter), adapter_addr);
+ ba2str(device_get_address(device), device_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
+ device_addr);
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ if (type == BDADDR_BREDR) {
+ g_key_file_remove_group(key_file, "LinkKey", NULL);
+ } else {
+ g_key_file_remove_group(key_file, "LongTermKey", NULL);
+ g_key_file_remove_group(key_file, "LocalSignatureKey", NULL);
+ g_key_file_remove_group(key_file, "RemoteSignatureKey", NULL);
+ g_key_file_remove_group(key_file, "IdentityResolvingKey", NULL);
+ }
+
+ str = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(filename, str, length, NULL);
+ g_free(str);
+
+ g_key_file_free(key_file);
+}
+
+struct rebond_consent_data {
+ struct btd_adapter *adapter;
+ bdaddr_t addr;
+ uint8_t addr_type;
+};
+
+static void rebond_consent_cb(struct agent *agent, DBusError *err,
+ void *user_data)
+{
+ struct rebond_consent_data *data = user_data;
+ struct btd_device *device;
+
+ if (err)
+ return;
+
+ device = btd_adapter_find_device(data->adapter, &data->addr,
+ data->addr_type);
+ if (device) {
+ btd_adapter_remove_bonding(data->adapter, &data->addr,
+ data->addr_type);
+
+ remove_keys(data->adapter, device, data->addr_type);
+ device_set_unpaired(device, data->addr_type);
+ }
+
+ adapter_create_bonding(data->adapter, &data->addr, data->addr_type,
+ agent_get_io_capability(agent));
+}
+
static void dev_disconnected(struct btd_adapter *adapter,
const struct mgmt_addr_info *addr,
uint8_t reason)
@@ -6662,6 +6725,33 @@ static void dev_disconnected(struct btd_adapter *adapter,

bonding_attempt_complete(adapter, &addr->bdaddr, addr->type,
MGMT_STATUS_DISCONNECTED);
+
+ /* send re-bond consent only if lost bond */
+ if (reason != MGMT_DEV_DISCONN_AUTH_FAILURE)
+ return;
+
+ if (device && device_is_bonded(device, addr->type)) {
+ struct rebond_consent_data *data;
+ struct agent *agent;
+
+ info("Device %s lost bond", dst);
+
+ agent = agent_get(NULL);
+ if (!agent)
+ return;
+
+ data = new0(struct rebond_consent_data, 1);
+ data->adapter = adapter;
+ bacpy(&data->addr, &addr->bdaddr);
+ data->addr_type = addr->type;
+
+ if (agent_rebond_consent(agent, device,
+ rebond_consent_cb,
+ data, (GDestroyNotify)free) < 0)
+ free(data);
+
+ agent_unref(agent);
+ }
}

void btd_add_disconnect_cb(btd_disconnect_cb func)
@@ -7706,40 +7796,6 @@ static void connect_failed_callback(uint16_t index, uint16_t length,
btd_adapter_remove_device(adapter, device);
}

-static void remove_keys(struct btd_adapter *adapter,
- struct btd_device *device, uint8_t type)
-{
- char adapter_addr[18];
- char device_addr[18];
- char filename[PATH_MAX];
- GKeyFile *key_file;
- gsize length = 0;
- char *str;
-
- ba2str(btd_adapter_get_address(adapter), adapter_addr);
- ba2str(device_get_address(device), device_addr);
-
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
- device_addr);
- key_file = g_key_file_new();
- g_key_file_load_from_file(key_file, filename, 0, NULL);
-
- if (type == BDADDR_BREDR) {
- g_key_file_remove_group(key_file, "LinkKey", NULL);
- } else {
- g_key_file_remove_group(key_file, "LongTermKey", NULL);
- g_key_file_remove_group(key_file, "LocalSignatureKey", NULL);
- g_key_file_remove_group(key_file, "RemoteSignatureKey", NULL);
- g_key_file_remove_group(key_file, "IdentityResolvingKey", NULL);
- }
-
- str = g_key_file_to_data(key_file, &length, NULL);
- g_file_set_contents(filename, str, length, NULL);
- g_free(str);
-
- g_key_file_free(key_file);
-}
-
static void unpaired_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
diff --git a/src/agent.c b/src/agent.c
index ff44d57..4d441b5 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -70,6 +70,7 @@ typedef enum {
AGENT_REQUEST_PINCODE,
AGENT_REQUEST_AUTHORIZE_SERVICE,
AGENT_REQUEST_DISPLAY_PINCODE,
+ AGENT_REQUEST_REBOND_CONSENT,
} agent_request_type_t;

struct agent {
@@ -247,6 +248,7 @@ void agent_unref(struct agent *agent)
case AGENT_REQUEST_AUTHORIZATION:
case AGENT_REQUEST_AUTHORIZE_SERVICE:
case AGENT_REQUEST_DISPLAY_PINCODE:
+ case AGENT_REQUEST_REBOND_CONSENT:
default:
cb = agent->request->cb;
cb(agent, &err, agent->request->user_data);
@@ -899,6 +901,51 @@ failed:
return err;
}

+int agent_rebond_consent(struct agent *agent, struct btd_device *device,
+ agent_cb cb, void *user_data,
+ GDestroyNotify destroy)
+{
+ struct agent_request *req;
+ const char *dev_path = device_get_path(device);
+ int err;
+
+ if (agent->request)
+ return -EBUSY;
+
+ req = agent_request_new(agent, AGENT_REQUEST_REBOND_CONSENT, cb,
+ user_data, destroy);
+
+
+ req->msg = dbus_message_new_method_call(agent->owner, agent->path,
+ AGENT_INTERFACE, "RebondConsent");
+ if (req->msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ dbus_message_append_args(req->msg,
+ DBUS_TYPE_OBJECT_PATH, &dev_path,
+ DBUS_TYPE_INVALID);
+
+ if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg,
+ &req->call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ err = -EIO;
+ goto failed;
+ }
+
+ dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+
+ agent->request = req;
+
+ return 0;
+
+failed:
+ agent_request_free(req, FALSE);
+ return err;
+}
+
uint8_t agent_get_io_capability(struct agent *agent)
{
return agent->capability;
diff --git a/src/agent.h b/src/agent.h
index 1e46920..3b15453 100644
--- a/src/agent.h
+++ b/src/agent.h
@@ -65,6 +65,10 @@ int agent_display_pincode(struct agent *agent, struct btd_device *device,
const char *pincode, agent_cb cb,
void *user_data, GDestroyNotify destroy);

+int agent_rebond_consent(struct agent *agent, struct btd_device *device,
+ agent_cb cb, void *user_data,
+ GDestroyNotify destroy);
+
int agent_cancel(struct agent *agent);

uint8_t agent_get_io_capability(struct agent *agent);
--
2.7.4


2016-08-04 13:28:38

by Szymon Janc

[permalink] [raw]
Subject: [RFC 4/5] plugins/policy: Disable policies on authentication failure

If remote device was disconnected due to authentication failure (lost
bond) there is no point in trying to reconnect.
---
plugins/policy.c | 58 +++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 43 insertions(+), 15 deletions(-)

diff --git a/plugins/policy.c b/plugins/policy.c
index 0330456..933a968 100644
--- a/plugins/policy.c
+++ b/plugins/policy.c
@@ -93,6 +93,8 @@ struct policy_data {
uint8_t ct_retries;
guint tg_timer;
uint8_t tg_retries;
+
+ bool auth_failure;
};

static struct reconnect_data *reconnect_find(struct btd_device *dev)
@@ -172,22 +174,34 @@ static struct policy_data *find_data(struct btd_device *dev)
return NULL;
}

-static void policy_remove(void *user_data)
+static void policy_reset(struct policy_data *data)
{
- struct policy_data *data = user_data;
-
- if (data->source_timer > 0)
+ if (data->source_timer > 0) {
g_source_remove(data->source_timer);
+ data->source_timer = 0;
+ }

- if (data->sink_timer > 0)
+ if (data->sink_timer > 0) {
g_source_remove(data->sink_timer);
+ data->sink_timer = 0;
+ }

- if (data->ct_timer > 0)
+ if (data->ct_timer > 0) {
g_source_remove(data->ct_timer);
+ data->ct_timer = 0;
+ }

- if (data->tg_timer > 0)
+ if (data->tg_timer > 0) {
g_source_remove(data->tg_timer);
+ data->tg_timer = 0;
+ }
+}

+static void policy_remove(void *user_data)
+{
+ struct policy_data *data = user_data;
+
+ policy_reset(data);
g_free(data);
}

@@ -606,6 +620,11 @@ static void service_cb(struct btd_service *service,
{
struct btd_profile *profile = btd_service_get_profile(service);
struct reconnect_data *reconnect;
+ struct policy_data *policy;
+
+ policy = find_data(btd_service_get_device(service));
+ if (policy && policy->auth_failure)
+ return;

if (g_str_equal(profile->remote_uuid, A2DP_SINK_UUID))
sink_cb(service, old_state, new_state);
@@ -704,20 +723,29 @@ static void reconnect_set_timer(struct reconnect_data *reconnect)
static void disconnect_cb(struct btd_device *dev, uint8_t reason)
{
struct reconnect_data *reconnect;
+ struct policy_data *policy;

DBG("reason %u", reason);

- if (reason != MGMT_DEV_DISCONN_TIMEOUT)
- return;
-
- reconnect = reconnect_find(dev);
- if (!reconnect || !reconnect->reconnect)
- return;
+ switch (reason) {
+ case MGMT_DEV_DISCONN_AUTH_FAILURE:
+ policy = policy_get_data(dev);
+ policy_reset(policy);
+ policy->auth_failure = true;
+ break;
+ case MGMT_DEV_DISCONN_TIMEOUT:
+ reconnect = reconnect_find(dev);
+ if (!reconnect || !reconnect->reconnect)
+ break;

- DBG("Device %s identified for auto-reconnection",
+ DBG("Device %s identified for auto-reconnection",
device_get_path(dev));

- reconnect_set_timer(reconnect);
+ reconnect_set_timer(reconnect);
+ break;
+ default:
+ break;
+ }
}

static void conn_fail_cb(struct btd_device *dev, uint8_t status)
--
2.7.4


2016-08-04 13:28:36

by Szymon Janc

[permalink] [raw]
Subject: [RFC 2/5] doc: Add RebondConsent method to agent API

This method will be called when bluetoothd requires user consent for
performing re-bond operation.
---
doc/agent-api.txt | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/doc/agent-api.txt b/doc/agent-api.txt
index 801ccb6..16328d7 100644
--- a/doc/agent-api.txt
+++ b/doc/agent-api.txt
@@ -176,6 +176,14 @@ Methods void Release()
Possible errors: org.bluez.Error.Rejected
org.bluez.Error.Canceled

+ void RebondConsent(object device)
+
+ This method gets called when service daemon needs to
+ authorize rebonding of remote device that lost bond
+ (replied with 'Pin or Key missing' error).
+
+ Possible errors: org.bluez.Error.Rejected
+
void Cancel()

This method gets called to indicate that the agent
--
2.7.4