Linux-Bluetooth Archive on lore.kernel.org
 help / color / Atom feed
* [Bluez PATCH v1] core: Add new policy for Just-Works repairing
@ 2020-02-07 11:38 Howard Chung
  2020-02-08  8:26 ` Marcel Holtmann
  0 siblings, 1 reply; 3+ messages in thread
From: Howard Chung @ 2020-02-07 11:38 UTC (permalink / raw)
  To: linux-bluetooth, luiz.von.dentz
  Cc: chromeos-bluetooth-upstreaming, howardchung

From: "howardchung@google.com" <howardchung@google.com>

When kernel find out that the incoming Just-Works pairing is
initiated by a paired device, it is user space's responsibility to
decide the next action.

This patch includes the following:
- add JustWorksRepairing policy as an option in main.conf
- handle the confirmation request from kernel

---
The Just-Works repairing policy could be one of the following:
- never: default; reject the repairing immediately.
- confirm: prompt a confirmation dialog to user.
- always: always accept the repairing.

Note that the confirmation dialog is only available in command
line for now.

 client/agent.c | 22 +++++++++++++++++++
 src/adapter.c  | 13 +++++++++++
 src/agent.c    | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/agent.h    |  4 ++++
 src/device.c   | 14 +++++++++---
 src/hcid.h     |  8 +++++++
 src/main.c     | 27 +++++++++++++++++++++++
 src/main.conf  |  5 +++++
 8 files changed, 149 insertions(+), 3 deletions(-)

diff --git a/client/agent.c b/client/agent.c
index 4def1b478..544344c46 100644
--- a/client/agent.c
+++ b/client/agent.c
@@ -239,6 +239,25 @@ static DBusMessage *request_authorization(DBusConnection *conn,
 	return NULL;
 }
 
+static DBusMessage *request_drop_old_key(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *device;
+
+	bt_shell_printf("Request drop old key\n");
+
+	dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
+							DBUS_TYPE_INVALID);
+
+	bt_shell_prompt_input("agent",
+			      "Drop the old key and accept pairing (yes/no):",
+			      confirm_response, conn);
+
+	pending_message = dbus_message_ref(msg);
+
+	return NULL;
+}
+
 static DBusMessage *authorize_service(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
@@ -292,6 +311,9 @@ static const GDBusMethodTable methods[] = {
 	{ GDBUS_ASYNC_METHOD("RequestAuthorization",
 			GDBUS_ARGS({ "device", "o" }),
 			NULL, request_authorization) },
+	{ GDBUS_ASYNC_METHOD("RequestDropOldKey",
+			GDBUS_ARGS({ "device", "o" }),
+			NULL, request_drop_old_key) },
 	{ GDBUS_ASYNC_METHOD("AuthorizeService",
 			GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }),
 			NULL,  authorize_service) },
diff --git a/src/adapter.c b/src/adapter.c
index 329c3ae0b..cecd80ea1 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -6909,6 +6909,19 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length,
 		return;
 	}
 
+	/*Just-Works repairing policy*/
+	if (ev->confirm_hint == 2) {
+		if (main_opts.jw_repairing == JW_REPAIRING_NEVER) {
+			btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
+							ev->addr.type, FALSE);
+			return;
+		} else if (main_opts.jw_repairing == JW_REPAIRING_ALWAYS) {
+			btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
+							ev->addr.type, TRUE);
+			return;
+		}
+	}
+
 	err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value),
 							ev->confirm_hint);
 	if (err < 0) {
diff --git a/src/agent.c b/src/agent.c
index e0ffcd22f..3a75e3b00 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -60,6 +60,7 @@ typedef enum {
 	AGENT_REQUEST_PASSKEY,
 	AGENT_REQUEST_CONFIRMATION,
 	AGENT_REQUEST_AUTHORIZATION,
+	AGENT_REQUEST_DROP_OLD_KEY,
 	AGENT_REQUEST_PINCODE,
 	AGENT_REQUEST_AUTHORIZE_SERVICE,
 	AGENT_REQUEST_DISPLAY_PINCODE,
@@ -239,6 +240,7 @@ void agent_unref(struct agent *agent)
 			break;
 		case AGENT_REQUEST_CONFIRMATION:
 		case AGENT_REQUEST_AUTHORIZATION:
+		case AGENT_REQUEST_DROP_OLD_KEY:
 		case AGENT_REQUEST_AUTHORIZE_SERVICE:
 		case AGENT_REQUEST_DISPLAY_PINCODE:
 		default:
@@ -798,6 +800,63 @@ failed:
 	return err;
 }
 
+static int drop_old_key_request_new(struct agent_request *req,
+						const char *device_path)
+{
+	struct agent *agent = req->agent;
+
+	req->msg = dbus_message_new_method_call(agent->owner, agent->path,
+				AGENT_INTERFACE, "RequestDropOldKey");
+	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 (g_dbus_send_message_with_reply(btd_get_dbus_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_drop_old_key(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;
+
+	DBG("Calling Agent.DropOldKey: name=%s, path=%s",
+						agent->owner, agent->path);
+
+	req = agent_request_new(agent, device, AGENT_REQUEST_DROP_OLD_KEY, cb,
+				user_data, destroy);
+
+	err = drop_old_key_request_new(req, dev_path);
+	if (err < 0)
+		goto failed;
+
+	agent->request = req;
+
+	return 0;
+
+failed:
+	agent_request_free(req, FALSE);
+	return err;
+}
+
 int agent_display_passkey(struct agent *agent, struct btd_device *device,
 				uint32_t passkey, uint16_t entered)
 {
diff --git a/src/agent.h b/src/agent.h
index 1438b9e6d..ccc651dba 100644
--- a/src/agent.h
+++ b/src/agent.h
@@ -65,6 +65,10 @@ int agent_request_authorization(struct agent *agent, struct btd_device *device,
 						agent_cb cb, void *user_data,
 						GDestroyNotify destroy);
 
+int agent_request_drop_old_key(struct agent *agent, struct btd_device *device,
+						agent_cb cb, void *user_data,
+						GDestroyNotify destroy);
+
 int agent_display_passkey(struct agent *agent, struct btd_device *device,
 				uint32_t passkey, uint16_t entered);
 
diff --git a/src/device.c b/src/device.c
index a4fe10980..e460e034f 100644
--- a/src/device.c
+++ b/src/device.c
@@ -6147,12 +6147,20 @@ int device_confirm_passkey(struct btd_device *device, uint8_t type,
 
 	auth->passkey = passkey;
 
-	if (confirm_hint)
+	switch (confirm_hint) {
+	case 0:
+		err = agent_request_confirmation(auth->agent, device, passkey,
+						confirm_cb, auth, NULL);
+		break;
+	case 1:
 		err = agent_request_authorization(auth->agent, device,
 						confirm_cb, auth, NULL);
-	else
-		err = agent_request_confirmation(auth->agent, device, passkey,
+		break;
+	case 2:
+		err = agent_request_drop_old_key(auth->agent, device,
 						confirm_cb, auth, NULL);
+		break;
+	}
 
 	if (err < 0) {
 		if (err == -EINPROGRESS) {
diff --git a/src/hcid.h b/src/hcid.h
index adea85ce2..bcd2b9fa1 100644
--- a/src/hcid.h
+++ b/src/hcid.h
@@ -35,6 +35,12 @@ typedef enum {
 	BT_GATT_CACHE_NO,
 } bt_gatt_cache_t;
 
+enum {
+	JW_REPAIRING_NEVER,
+	JW_REPAIRING_CONFIRM,
+	JW_REPAIRING_ALWAYS,
+} jw_repairing_t;
+
 struct main_opts {
 	char		*name;
 	uint32_t	class;
@@ -58,6 +64,8 @@ struct main_opts {
 	uint16_t	gatt_mtu;
 
 	uint8_t		key_size;
+
+	jw_repairing_t	jw_repairing;
 };
 
 extern struct main_opts main_opts;
diff --git a/src/main.c b/src/main.c
index 1a6ab36a3..d67f469f1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -93,6 +93,7 @@ static const char *supported_options[] = {
 	"MultiProfile",
 	"FastConnectable",
 	"Privacy",
+	"JustWorksRepairing",
 	NULL
 };
 
@@ -193,6 +194,20 @@ static bt_gatt_cache_t parse_gatt_cache(const char *cache)
 	}
 }
 
+static jw_repairing_t parse_jw_repairing(const char *jw_repairing)
+{
+	if (!strcmp(jw_repairing, "never")) {
+		return JW_REPAIRING_NEVER;
+	} else if (!strcmp(jw_repairing, "confirm")) {
+		return JW_REPAIRING_CONFIRM;
+	} else if (!strcmp(jw_repairing, "always")) {
+		return JW_REPAIRING_ALWAYS;
+	} else {
+		return JW_REPAIRING_NEVER;
+	}
+}
+
+
 static void check_options(GKeyFile *config, const char *group,
 						const char **options)
 {
@@ -331,6 +346,18 @@ static void parse_config(GKeyFile *config)
 		g_free(str);
 	}
 
+	str = g_key_file_get_string(config, "General",
+						"JustWorksRepairing", &err);
+	if (err) {
+		DBG("%s", err->message);
+		g_clear_error(&err);
+		main_opts.jw_repairing = JW_REPAIRING_NEVER;
+	} else {
+		DBG("just_works_repairing=%s", str);
+		main_opts.jw_repairing = parse_jw_repairing(str);
+		g_free(str);
+	}
+
 	str = g_key_file_get_string(config, "General", "Name", &err);
 	if (err) {
 		DBG("%s", err->message);
diff --git a/src/main.conf b/src/main.conf
index 40687a755..bb5ff5b15 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -72,6 +72,11 @@
 # Defaults to "off"
 # Privacy = off
 
+# Specify the policy to the JUST-WORKS repairing initiated by peer
+# Possible values: "never", "confirm", "always"
+# Defaults to "never"
+#JustWorksRepairing = never
+
 [GATT]
 # GATT attribute cache.
 # Possible values:
-- 
2.25.0.341.g760bfbb309-goog


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Bluez PATCH v1] core: Add new policy for Just-Works repairing
  2020-02-07 11:38 [Bluez PATCH v1] core: Add new policy for Just-Works repairing Howard Chung
@ 2020-02-08  8:26 ` Marcel Holtmann
  2020-02-10 21:07   ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 3+ messages in thread
From: Marcel Holtmann @ 2020-02-08  8:26 UTC (permalink / raw)
  To: Howard Chung
  Cc: linux-bluetooth, Luiz Augusto von Dentz, chromeos-bluetooth-upstreaming

Hi Howard,

> When kernel find out that the incoming Just-Works pairing is
> initiated by a paired device, it is user space's responsibility to
> decide the next action.
> 
> This patch includes the following:
> - add JustWorksRepairing policy as an option in main.conf
> - handle the confirmation request from kernel
> 
> ---
> The Just-Works repairing policy could be one of the following:
> - never: default; reject the repairing immediately.
> - confirm: prompt a confirmation dialog to user.
> - always: always accept the repairing.
> 
> Note that the confirmation dialog is only available in command
> line for now.
> 
> client/agent.c | 22 +++++++++++++++++++
> src/adapter.c  | 13 +++++++++++
> src/agent.c    | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
> src/agent.h    |  4 ++++
> src/device.c   | 14 +++++++++---
> src/hcid.h     |  8 +++++++
> src/main.c     | 27 +++++++++++++++++++++++
> src/main.conf  |  5 +++++
> 8 files changed, 149 insertions(+), 3 deletions(-)

you also need to document this in doc/agent-api.txt and we normally split patches into doc/, src/ and client/ changes.

> 
> diff --git a/client/agent.c b/client/agent.c
> index 4def1b478..544344c46 100644
> --- a/client/agent.c
> +++ b/client/agent.c
> @@ -239,6 +239,25 @@ static DBusMessage *request_authorization(DBusConnection *conn,
> 	return NULL;
> }
> 
> +static DBusMessage *request_drop_old_key(DBusConnection *conn,
> +					DBusMessage *msg, void *user_data)
> +{
> +	const char *device;
> +
> +	bt_shell_printf("Request drop old key\n");
> +
> +	dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
> +							DBUS_TYPE_INVALID);
> +
> +	bt_shell_prompt_input("agent",
> +			      "Drop the old key and accept pairing (yes/no):",
> +			      confirm_response, conn);
> +
> +	pending_message = dbus_message_ref(msg);
> +
> +	return NULL;
> +}
> +
> static DBusMessage *authorize_service(DBusConnection *conn,
> 					DBusMessage *msg, void *user_data)
> {
> @@ -292,6 +311,9 @@ static const GDBusMethodTable methods[] = {
> 	{ GDBUS_ASYNC_METHOD("RequestAuthorization",
> 			GDBUS_ARGS({ "device", "o" }),
> 			NULL, request_authorization) },
> +	{ GDBUS_ASYNC_METHOD("RequestDropOldKey",
> +			GDBUS_ARGS({ "device", "o" }),
> +			NULL, request_drop_old_key) },
> 	{ GDBUS_ASYNC_METHOD("AuthorizeService",
> 			GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }),

I am not fully convinced with the name yet. Can we start with a patch that just has the policy in main.conf for always and never.

> 			NULL,  authorize_service) },
> diff --git a/src/adapter.c b/src/adapter.c
> index 329c3ae0b..cecd80ea1 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -6909,6 +6909,19 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length,
> 		return;
> 	}
> 
> +	/*Just-Works repairing policy*/

Please follow our coding style for comments.

> +	if (ev->confirm_hint == 2) {
> +		if (main_opts.jw_repairing == JW_REPAIRING_NEVER) {
> +			btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
> +							ev->addr.type, FALSE);
> +			return;
> +		} else if (main_opts.jw_repairing == JW_REPAIRING_ALWAYS) {
> +			btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
> +							ev->addr.type, TRUE);
> +			return;
> +		}
> +	}
> +
> 	err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value),
> 							ev->confirm_hint);
> 	if (err < 0) {
> diff --git a/src/agent.c b/src/agent.c
> index e0ffcd22f..3a75e3b00 100644
> --- a/src/agent.c
> +++ b/src/agent.c
> @@ -60,6 +60,7 @@ typedef enum {
> 	AGENT_REQUEST_PASSKEY,
> 	AGENT_REQUEST_CONFIRMATION,
> 	AGENT_REQUEST_AUTHORIZATION,
> +	AGENT_REQUEST_DROP_OLD_KEY,
> 	AGENT_REQUEST_PINCODE,
> 	AGENT_REQUEST_AUTHORIZE_SERVICE,
> 	AGENT_REQUEST_DISPLAY_PINCODE,
> @@ -239,6 +240,7 @@ void agent_unref(struct agent *agent)
> 			break;
> 		case AGENT_REQUEST_CONFIRMATION:
> 		case AGENT_REQUEST_AUTHORIZATION:
> +		case AGENT_REQUEST_DROP_OLD_KEY:
> 		case AGENT_REQUEST_AUTHORIZE_SERVICE:
> 		case AGENT_REQUEST_DISPLAY_PINCODE:
> 		default:
> @@ -798,6 +800,63 @@ failed:
> 	return err;
> }
> 
> +static int drop_old_key_request_new(struct agent_request *req,
> +						const char *device_path)
> +{
> +	struct agent *agent = req->agent;
> +
> +	req->msg = dbus_message_new_method_call(agent->owner, agent->path,
> +				AGENT_INTERFACE, "RequestDropOldKey");
> +	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 (g_dbus_send_message_with_reply(btd_get_dbus_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_drop_old_key(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;
> +
> +	DBG("Calling Agent.DropOldKey: name=%s, path=%s",
> +						agent->owner, agent->path);
> +
> +	req = agent_request_new(agent, device, AGENT_REQUEST_DROP_OLD_KEY, cb,
> +				user_data, destroy);
> +
> +	err = drop_old_key_request_new(req, dev_path);
> +	if (err < 0)
> +		goto failed;
> +
> +	agent->request = req;
> +
> +	return 0;
> +
> +failed:
> +	agent_request_free(req, FALSE);
> +	return err;
> +}
> +

You need to build in a graceful fallback for agents that are not capable of handling the new agent callbacks.

> int agent_display_passkey(struct agent *agent, struct btd_device *device,
> 				uint32_t passkey, uint16_t entered)
> {
> diff --git a/src/agent.h b/src/agent.h
> index 1438b9e6d..ccc651dba 100644
> --- a/src/agent.h
> +++ b/src/agent.h
> @@ -65,6 +65,10 @@ int agent_request_authorization(struct agent *agent, struct btd_device *device,
> 						agent_cb cb, void *user_data,
> 						GDestroyNotify destroy);
> 
> +int agent_request_drop_old_key(struct agent *agent, struct btd_device *device,
> +						agent_cb cb, void *user_data,
> +						GDestroyNotify destroy);
> +
> int agent_display_passkey(struct agent *agent, struct btd_device *device,
> 				uint32_t passkey, uint16_t entered);
> 
> diff --git a/src/device.c b/src/device.c
> index a4fe10980..e460e034f 100644
> --- a/src/device.c
> +++ b/src/device.c
> @@ -6147,12 +6147,20 @@ int device_confirm_passkey(struct btd_device *device, uint8_t type,
> 
> 	auth->passkey = passkey;
> 
> -	if (confirm_hint)
> +	switch (confirm_hint) {
> +	case 0:
> +		err = agent_request_confirmation(auth->agent, device, passkey,
> +						confirm_cb, auth, NULL);
> +		break;
> +	case 1:
> 		err = agent_request_authorization(auth->agent, device,
> 						confirm_cb, auth, NULL);
> -	else
> -		err = agent_request_confirmation(auth->agent, device, passkey,
> +		break;
> +	case 2:
> +		err = agent_request_drop_old_key(auth->agent, device,
> 						confirm_cb, auth, NULL);
> +		break;
> +	}
> 
> 	if (err < 0) {
> 		if (err == -EINPROGRESS) {
> diff --git a/src/hcid.h b/src/hcid.h
> index adea85ce2..bcd2b9fa1 100644
> --- a/src/hcid.h
> +++ b/src/hcid.h
> @@ -35,6 +35,12 @@ typedef enum {
> 	BT_GATT_CACHE_NO,
> } bt_gatt_cache_t;
> 
> +enum {
> +	JW_REPAIRING_NEVER,
> +	JW_REPAIRING_CONFIRM,
> +	JW_REPAIRING_ALWAYS,
> +} jw_repairing_t;
> +
> struct main_opts {
> 	char		*name;
> 	uint32_t	class;
> @@ -58,6 +64,8 @@ struct main_opts {
> 	uint16_t	gatt_mtu;
> 
> 	uint8_t		key_size;
> +
> +	jw_repairing_t	jw_repairing;
> };
> 
> extern struct main_opts main_opts;
> diff --git a/src/main.c b/src/main.c
> index 1a6ab36a3..d67f469f1 100644
> --- a/src/main.c
> +++ b/src/main.c
> @@ -93,6 +93,7 @@ static const char *supported_options[] = {
> 	"MultiProfile",
> 	"FastConnectable",
> 	"Privacy",
> +	"JustWorksRepairing",
> 	NULL
> };
> 
> @@ -193,6 +194,20 @@ static bt_gatt_cache_t parse_gatt_cache(const char *cache)
> 	}
> }
> 
> +static jw_repairing_t parse_jw_repairing(const char *jw_repairing)
> +{
> +	if (!strcmp(jw_repairing, "never")) {
> +		return JW_REPAIRING_NEVER;
> +	} else if (!strcmp(jw_repairing, "confirm")) {
> +		return JW_REPAIRING_CONFIRM;
> +	} else if (!strcmp(jw_repairing, "always")) {
> +		return JW_REPAIRING_ALWAYS;
> +	} else {
> +		return JW_REPAIRING_NEVER;
> +	}
> +}
> +
> +
> static void check_options(GKeyFile *config, const char *group,
> 						const char **options)
> {
> @@ -331,6 +346,18 @@ static void parse_config(GKeyFile *config)
> 		g_free(str);
> 	}
> 
> +	str = g_key_file_get_string(config, "General",
> +						"JustWorksRepairing", &err);
> +	if (err) {
> +		DBG("%s", err->message);
> +		g_clear_error(&err);
> +		main_opts.jw_repairing = JW_REPAIRING_NEVER;
> +	} else {
> +		DBG("just_works_repairing=%s", str);
> +		main_opts.jw_repairing = parse_jw_repairing(str);
> +		g_free(str);
> +	}
> +
> 	str = g_key_file_get_string(config, "General", "Name", &err);
> 	if (err) {
> 		DBG("%s", err->message);
> diff --git a/src/main.conf b/src/main.conf
> index 40687a755..bb5ff5b15 100644
> --- a/src/main.conf
> +++ b/src/main.conf
> @@ -72,6 +72,11 @@
> # Defaults to "off"
> # Privacy = off
> 
> +# Specify the policy to the JUST-WORKS repairing initiated by peer
> +# Possible values: "never", "confirm", "always"
> +# Defaults to "never"
> +#JustWorksRepairing = never
> +
> [GATT]
> # GATT attribute cache.
> # Possible values:

The word “repairing” always makes me question if that is the best name. I leave this open for comments from others.

Regards

Marcel


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Bluez PATCH v1] core: Add new policy for Just-Works repairing
  2020-02-08  8:26 ` Marcel Holtmann
@ 2020-02-10 21:07   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 3+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-10 21:07 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Howard Chung, linux-bluetooth, Luiz Augusto von Dentz,
	chromeos-bluetooth-upstreaming

Hi Howard,

On Sat, Feb 8, 2020 at 12:27 AM Marcel Holtmann <marcel@holtmann.org> wrote:
>
> Hi Howard,
>
> > When kernel find out that the incoming Just-Works pairing is
> > initiated by a paired device, it is user space's responsibility to
> > decide the next action.
> >
> > This patch includes the following:
> > - add JustWorksRepairing policy as an option in main.conf
> > - handle the confirmation request from kernel
> >
> > ---
> > The Just-Works repairing policy could be one of the following:
> > - never: default; reject the repairing immediately.
> > - confirm: prompt a confirmation dialog to user.
> > - always: always accept the repairing.
> >
> > Note that the confirmation dialog is only available in command
> > line for now.
> >
> > client/agent.c | 22 +++++++++++++++++++
> > src/adapter.c  | 13 +++++++++++
> > src/agent.c    | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
> > src/agent.h    |  4 ++++
> > src/device.c   | 14 +++++++++---
> > src/hcid.h     |  8 +++++++
> > src/main.c     | 27 +++++++++++++++++++++++
> > src/main.conf  |  5 +++++
> > 8 files changed, 149 insertions(+), 3 deletions(-)
>
> you also need to document this in doc/agent-api.txt and we normally split patches into doc/, src/ and client/ changes.
>
> >
> > diff --git a/client/agent.c b/client/agent.c
> > index 4def1b478..544344c46 100644
> > --- a/client/agent.c
> > +++ b/client/agent.c
> > @@ -239,6 +239,25 @@ static DBusMessage *request_authorization(DBusConnection *conn,
> >       return NULL;
> > }
> >
> > +static DBusMessage *request_drop_old_key(DBusConnection *conn,
> > +                                     DBusMessage *msg, void *user_data)
> > +{
> > +     const char *device;
> > +
> > +     bt_shell_printf("Request drop old key\n");
> > +
> > +     dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
> > +                                                     DBUS_TYPE_INVALID);
> > +
> > +     bt_shell_prompt_input("agent",
> > +                           "Drop the old key and accept pairing (yes/no):",
> > +                           confirm_response, conn);
> > +
> > +     pending_message = dbus_message_ref(msg);
> > +
> > +     return NULL;
> > +}
> > +
> > static DBusMessage *authorize_service(DBusConnection *conn,
> >                                       DBusMessage *msg, void *user_data)
> > {
> > @@ -292,6 +311,9 @@ static const GDBusMethodTable methods[] = {
> >       { GDBUS_ASYNC_METHOD("RequestAuthorization",
> >                       GDBUS_ARGS({ "device", "o" }),
> >                       NULL, request_authorization) },
> > +     { GDBUS_ASYNC_METHOD("RequestDropOldKey",
> > +                     GDBUS_ARGS({ "device", "o" }),
> > +                     NULL, request_drop_old_key) },
> >       { GDBUS_ASYNC_METHOD("AuthorizeService",
> >                       GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }),
>
> I am not fully convinced with the name yet. Can we start with a patch that just has the policy in main.conf for always and never.

I was thinking we should just use RequestAuthorization which according
to the documentation was introduced exactly to deal with JustWorks
pairing, the agent can then check if the device has been already
paired and reject it if it wants to, that way we don't need a fallback
mechanism (which most likely would use RequestAuthorization anyway).

> >                       NULL,  authorize_service) },
> > diff --git a/src/adapter.c b/src/adapter.c
> > index 329c3ae0b..cecd80ea1 100644
> > --- a/src/adapter.c
> > +++ b/src/adapter.c
> > @@ -6909,6 +6909,19 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length,
> >               return;
> >       }
> >
> > +     /*Just-Works repairing policy*/
>
> Please follow our coding style for comments.
>
> > +     if (ev->confirm_hint == 2) {
> > +             if (main_opts.jw_repairing == JW_REPAIRING_NEVER) {
> > +                     btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
> > +                                                     ev->addr.type, FALSE);
> > +                     return;
> > +             } else if (main_opts.jw_repairing == JW_REPAIRING_ALWAYS) {
> > +                     btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
> > +                                                     ev->addr.type, TRUE);
> > +                     return;
> > +             }
> > +     }
> > +
> >       err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value),
> >                                                       ev->confirm_hint);
> >       if (err < 0) {
> > diff --git a/src/agent.c b/src/agent.c
> > index e0ffcd22f..3a75e3b00 100644
> > --- a/src/agent.c
> > +++ b/src/agent.c
> > @@ -60,6 +60,7 @@ typedef enum {
> >       AGENT_REQUEST_PASSKEY,
> >       AGENT_REQUEST_CONFIRMATION,
> >       AGENT_REQUEST_AUTHORIZATION,
> > +     AGENT_REQUEST_DROP_OLD_KEY,
> >       AGENT_REQUEST_PINCODE,
> >       AGENT_REQUEST_AUTHORIZE_SERVICE,
> >       AGENT_REQUEST_DISPLAY_PINCODE,
> > @@ -239,6 +240,7 @@ void agent_unref(struct agent *agent)
> >                       break;
> >               case AGENT_REQUEST_CONFIRMATION:
> >               case AGENT_REQUEST_AUTHORIZATION:
> > +             case AGENT_REQUEST_DROP_OLD_KEY:
> >               case AGENT_REQUEST_AUTHORIZE_SERVICE:
> >               case AGENT_REQUEST_DISPLAY_PINCODE:
> >               default:
> > @@ -798,6 +800,63 @@ failed:
> >       return err;
> > }
> >
> > +static int drop_old_key_request_new(struct agent_request *req,
> > +                                             const char *device_path)
> > +{
> > +     struct agent *agent = req->agent;
> > +
> > +     req->msg = dbus_message_new_method_call(agent->owner, agent->path,
> > +                             AGENT_INTERFACE, "RequestDropOldKey");
> > +     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 (g_dbus_send_message_with_reply(btd_get_dbus_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_drop_old_key(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;
> > +
> > +     DBG("Calling Agent.DropOldKey: name=%s, path=%s",
> > +                                             agent->owner, agent->path);
> > +
> > +     req = agent_request_new(agent, device, AGENT_REQUEST_DROP_OLD_KEY, cb,
> > +                             user_data, destroy);
> > +
> > +     err = drop_old_key_request_new(req, dev_path);
> > +     if (err < 0)
> > +             goto failed;
> > +
> > +     agent->request = req;
> > +
> > +     return 0;
> > +
> > +failed:
> > +     agent_request_free(req, FALSE);
> > +     return err;
> > +}
> > +
>
> You need to build in a graceful fallback for agents that are not capable of handling the new agent callbacks.
>
> > int agent_display_passkey(struct agent *agent, struct btd_device *device,
> >                               uint32_t passkey, uint16_t entered)
> > {
> > diff --git a/src/agent.h b/src/agent.h
> > index 1438b9e6d..ccc651dba 100644
> > --- a/src/agent.h
> > +++ b/src/agent.h
> > @@ -65,6 +65,10 @@ int agent_request_authorization(struct agent *agent, struct btd_device *device,
> >                                               agent_cb cb, void *user_data,
> >                                               GDestroyNotify destroy);
> >
> > +int agent_request_drop_old_key(struct agent *agent, struct btd_device *device,
> > +                                             agent_cb cb, void *user_data,
> > +                                             GDestroyNotify destroy);
> > +
> > int agent_display_passkey(struct agent *agent, struct btd_device *device,
> >                               uint32_t passkey, uint16_t entered);
> >
> > diff --git a/src/device.c b/src/device.c
> > index a4fe10980..e460e034f 100644
> > --- a/src/device.c
> > +++ b/src/device.c
> > @@ -6147,12 +6147,20 @@ int device_confirm_passkey(struct btd_device *device, uint8_t type,
> >
> >       auth->passkey = passkey;
> >
> > -     if (confirm_hint)
> > +     switch (confirm_hint) {
> > +     case 0:
> > +             err = agent_request_confirmation(auth->agent, device, passkey,
> > +                                             confirm_cb, auth, NULL);
> > +             break;
> > +     case 1:
> >               err = agent_request_authorization(auth->agent, device,
> >                                               confirm_cb, auth, NULL);
> > -     else
> > -             err = agent_request_confirmation(auth->agent, device, passkey,
> > +             break;
> > +     case 2:
> > +             err = agent_request_drop_old_key(auth->agent, device,
> >                                               confirm_cb, auth, NULL);
> > +             break;
> > +     }
> >
> >       if (err < 0) {
> >               if (err == -EINPROGRESS) {
> > diff --git a/src/hcid.h b/src/hcid.h
> > index adea85ce2..bcd2b9fa1 100644
> > --- a/src/hcid.h
> > +++ b/src/hcid.h
> > @@ -35,6 +35,12 @@ typedef enum {
> >       BT_GATT_CACHE_NO,
> > } bt_gatt_cache_t;
> >
> > +enum {
> > +     JW_REPAIRING_NEVER,
> > +     JW_REPAIRING_CONFIRM,
> > +     JW_REPAIRING_ALWAYS,
> > +} jw_repairing_t;
> > +
> > struct main_opts {
> >       char            *name;
> >       uint32_t        class;
> > @@ -58,6 +64,8 @@ struct main_opts {
> >       uint16_t        gatt_mtu;
> >
> >       uint8_t         key_size;
> > +
> > +     jw_repairing_t  jw_repairing;
> > };
> >
> > extern struct main_opts main_opts;
> > diff --git a/src/main.c b/src/main.c
> > index 1a6ab36a3..d67f469f1 100644
> > --- a/src/main.c
> > +++ b/src/main.c
> > @@ -93,6 +93,7 @@ static const char *supported_options[] = {
> >       "MultiProfile",
> >       "FastConnectable",
> >       "Privacy",
> > +     "JustWorksRepairing",
> >       NULL
> > };
> >
> > @@ -193,6 +194,20 @@ static bt_gatt_cache_t parse_gatt_cache(const char *cache)
> >       }
> > }
> >
> > +static jw_repairing_t parse_jw_repairing(const char *jw_repairing)
> > +{
> > +     if (!strcmp(jw_repairing, "never")) {
> > +             return JW_REPAIRING_NEVER;
> > +     } else if (!strcmp(jw_repairing, "confirm")) {
> > +             return JW_REPAIRING_CONFIRM;
> > +     } else if (!strcmp(jw_repairing, "always")) {
> > +             return JW_REPAIRING_ALWAYS;
> > +     } else {
> > +             return JW_REPAIRING_NEVER;
> > +     }
> > +}
> > +
> > +
> > static void check_options(GKeyFile *config, const char *group,
> >                                               const char **options)
> > {
> > @@ -331,6 +346,18 @@ static void parse_config(GKeyFile *config)
> >               g_free(str);
> >       }
> >
> > +     str = g_key_file_get_string(config, "General",
> > +                                             "JustWorksRepairing", &err);
> > +     if (err) {
> > +             DBG("%s", err->message);
> > +             g_clear_error(&err);
> > +             main_opts.jw_repairing = JW_REPAIRING_NEVER;
> > +     } else {
> > +             DBG("just_works_repairing=%s", str);
> > +             main_opts.jw_repairing = parse_jw_repairing(str);
> > +             g_free(str);
> > +     }
> > +
> >       str = g_key_file_get_string(config, "General", "Name", &err);
> >       if (err) {
> >               DBG("%s", err->message);
> > diff --git a/src/main.conf b/src/main.conf
> > index 40687a755..bb5ff5b15 100644
> > --- a/src/main.conf
> > +++ b/src/main.conf
> > @@ -72,6 +72,11 @@
> > # Defaults to "off"
> > # Privacy = off
> >
> > +# Specify the policy to the JUST-WORKS repairing initiated by peer
> > +# Possible values: "never", "confirm", "always"
> > +# Defaults to "never"
> > +#JustWorksRepairing = never
> > +
> > [GATT]
> > # GATT attribute cache.
> > # Possible values:
>
> The word “repairing” always makes me question if that is the best name. I leave this open for comments from others.
>
> Regards
>
> Marcel
>


--
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, back to index

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-07 11:38 [Bluez PATCH v1] core: Add new policy for Just-Works repairing Howard Chung
2020-02-08  8:26 ` Marcel Holtmann
2020-02-10 21:07   ` Luiz Augusto von Dentz

Linux-Bluetooth Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-bluetooth/0 linux-bluetooth/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-bluetooth linux-bluetooth/ https://lore.kernel.org/linux-bluetooth \
		linux-bluetooth@vger.kernel.org
	public-inbox-index linux-bluetooth

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-bluetooth


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git