From 2ca9a55fd57a937ce85c81cf6cfc6072a6d66b47 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 8 Nov 2023 09:21:52 -0800 Subject: [PATCH] dpp: Add StartConfigurator, PKEX agent support Adds a configurator variant to be used along side an agent. When called the configurator will start and wait for an initial PKEX exchange message from an enrollee at which point it will request the code from an agent. This provides more flexibility for configurators that are capable of configuring multiple enrollees with different identifiers/codes. Note that the timing requirements per the DPP spec still apply so this is not meant to be used with a human configurator but within an automated agent which does a quick lookup of potential identifiers/codes and can reply within the 200ms window. --- src/dpp.c | 225 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 223 insertions(+), 2 deletions(-) diff --git a/src/dpp.c b/src/dpp.c index 176217f2..c54bd484 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -88,6 +88,13 @@ enum dpp_interface { DPP_INTERFACE_PKEX, }; +struct pkex_agent { + char *owner; + char *path; + unsigned int disconnect_watch; + uint32_t pending_id; +}; + struct dpp_sm { struct netdev *netdev; char *uri; @@ -112,6 +119,8 @@ struct dpp_sm { enum dpp_state state; enum dpp_interface interface; + struct pkex_agent *agent; + /* * List of frequencies to jump between. The presence of this list is * also used to signify that a configurator is an initiator vs responder @@ -162,6 +171,7 @@ struct dpp_sm { char *pkex_id; char *pkex_key; uint8_t pkex_version; + struct l_ecc_point *peer_encr_key; struct l_ecc_point *pkex_m; /* Ephemeral key Y' or X' for enrollee or configurator */ struct l_ecc_point *y_or_x; @@ -337,6 +347,68 @@ static void dpp_free_pending_pkex_data(struct dpp_sm *dpp) } memset(dpp->peer_addr, 0, sizeof(dpp->peer_addr)); + + if (dpp->peer_encr_key) { + l_ecc_point_free(dpp->peer_encr_key); + dpp->peer_encr_key = NULL; + } +} + +static void pkex_agent_free(void *data) +{ + struct pkex_agent *agent = data; + + l_free(agent->owner); + l_free(agent->path); + l_dbus_remove_watch(dbus_get_bus(), agent->disconnect_watch); + l_free(agent); +} + +static void dpp_agent_cancel(struct dpp_sm *dpp) +{ + struct l_dbus_message *msg; + + const char *reason = "shutdown"; + + msg = l_dbus_message_new_method_call(dbus_get_bus(), + dpp->agent->owner, + dpp->agent->path, + IWD_SHARED_CODE_AGENT_INTERFACE, + "Cancel"); + l_dbus_message_set_arguments(msg, "s", reason); + l_dbus_message_set_no_reply(msg, true); + l_dbus_send(dbus_get_bus(), msg); +} + +static void dpp_agent_release(struct dpp_sm *dpp) +{ + struct l_dbus_message *msg; + + msg = l_dbus_message_new_method_call(dbus_get_bus(), + dpp->agent->owner, + dpp->agent->path, + IWD_SHARED_CODE_AGENT_INTERFACE, + "Release"); + l_dbus_message_set_no_reply(msg, true); + l_dbus_send(dbus_get_bus(), msg); +} + +static void dpp_destroy_agent(struct dpp_sm *dpp) +{ + if (!dpp->agent) + return; + + if (dpp->agent->pending_id) { + dpp_agent_cancel(dpp); + l_dbus_cancel(dbus_get_bus(), dpp->agent->pending_id); + } + + dpp_agent_release(dpp); + + l_debug("Released SharedCodeAgent on path %s", dpp->agent->path); + + pkex_agent_free(dpp->agent); + dpp->agent = NULL; } static void dpp_free_auth_data(struct dpp_sm *dpp) @@ -451,6 +523,8 @@ static void dpp_reset(struct dpp_sm *dpp) explicit_bzero(dpp->z, dpp->key_len); explicit_bzero(dpp->u, dpp->u_len); + dpp_destroy_agent(dpp); + dpp_free_pending_pkex_data(dpp); dpp_free_auth_data(dpp); @@ -1804,6 +1878,20 @@ static void dpp_offchannel_timeout(int error, void *user_data) switch (dpp->state) { case DPP_STATE_PKEX_EXCHANGE: + if (dpp->role != DPP_CAPABILITY_CONFIGURATOR || !dpp->agent) + break; + + /* + * We have a pending agent request but it did not arrive in + * time, we cant assume the enrollee will be waiting around + * for our response so cancel the request and continue waiting + * for another request + */ + if (dpp->agent->pending_id) { + dpp_free_pending_pkex_data(dpp); + dpp_agent_cancel(dpp); + } + /* Fall through */ case DPP_STATE_PRESENCE: break; case DPP_STATE_NOTHING: @@ -2916,6 +3004,63 @@ bad_code: return; } +static void dpp_pkex_agent_reply(struct l_dbus_message *message, + void *user_data) +{ + struct dpp_sm *dpp = user_data; + const char *error, *text; + const char *code; + + dpp->agent->pending_id = 0; + + l_debug("SharedCodeAgent %s path %s replied", dpp->agent->owner, + dpp->agent->path); + + if (l_dbus_message_get_error(message, &error, &text)) { + l_error("RequestSharedCode(%s) returned %s(\"%s\")", + dpp->pkex_id, error, text); + goto reset; + } + + if (!l_dbus_message_get_arguments(message, "s", &code)) { + l_debug("Invalid arguments, check SharedCodeAgent!"); + goto reset; + } + + dpp->pkex_key = l_strdup(code); + dpp_process_pkex_exchange_request(dpp, dpp->peer_encr_key); + + return; + +reset: + dpp_free_pending_pkex_data(dpp); +} + +static bool dpp_pkex_agent_request(struct dpp_sm *dpp) +{ + struct l_dbus_message *msg; + + if (!dpp->agent) + return false; + + if (L_WARN_ON(dpp->agent->pending_id)) + return false; + + msg = l_dbus_message_new_method_call(dbus_get_bus(), + dpp->agent->owner, + dpp->agent->path, + IWD_SHARED_CODE_AGENT_INTERFACE, + "RequestSharedCode"); + l_dbus_message_set_arguments(msg, "s", dpp->pkex_id); + + + dpp->agent->pending_id = l_dbus_send_with_reply(dbus_get_bus(), + msg, + dpp_pkex_agent_reply, + dpp, NULL); + return dpp->agent->pending_id != 0; +} + static void dpp_handle_pkex_exchange_request(struct dpp_sm *dpp, const uint8_t *from, const uint8_t *body, size_t body_len) @@ -3015,6 +3160,33 @@ static void dpp_handle_pkex_exchange_request(struct dpp_sm *dpp, memcpy(dpp->peer_addr, from, 6); + if (!dpp->pkex_key) { + /* + * "If an optional code identifier is used, it shall be a UTF-8 + * string not greater than eighty (80) octets" + */ + if (!id || id_len > 80 || !l_utf8_validate(id, id_len, NULL)) { + l_debug("Configurator started with agent but enrollee " + "sent invalid or no identifier, ignoring"); + return; + } + + dpp->pkex_id = l_strndup(id, id_len); + + /* Need to obtain code from agent */ + if (!dpp_pkex_agent_request(dpp)) { + l_debug("Failed to request code from agent!"); + dpp_free_pending_pkex_data(dpp); + return; + } + + /* Save the encrypted key/identifier for the agent callback */ + + dpp->peer_encr_key = l_steal_ptr(m); + + return; + } + dpp_process_pkex_exchange_request(dpp, m); return; @@ -3959,8 +4131,35 @@ invalid_args: return dbus_error_invalid_args(message); } +static void pkex_agent_disconnect(struct l_dbus *dbus, void *user_data) +{ + struct dpp_sm *dpp = user_data; + + l_debug("SharedCodeAgent %s disconnected", dpp->agent->path); + + dpp_reset(dpp); +} + +static void dpp_create_agent(struct dpp_sm *dpp, const char *path, + struct l_dbus_message *message) +{ + const char *sender = l_dbus_message_get_sender(message); + + dpp->agent = l_new(struct pkex_agent, 1); + dpp->agent->owner = l_strdup(sender); + dpp->agent->path = l_strdup(path); + dpp->agent->disconnect_watch = l_dbus_add_disconnect_watch( + dbus_get_bus(), + sender, + pkex_agent_disconnect, + dpp, NULL); + + l_debug("Registered a SharedCodeAgent on path %s", path); +} + static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp, const char *key, const char *identifier, + const char *agent_path, struct l_dbus_message *message) { struct handshake_state *hs = netdev_get_handshake(dpp->netdev); @@ -3988,6 +4187,9 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp, if (key) dpp->pkex_key = l_strdup(key); + if (agent_path) + dpp_create_agent(dpp, agent_path, message); + dpp->role = DPP_CAPABILITY_CONFIGURATOR; dpp->state = DPP_STATE_PKEX_EXCHANGE; dpp->interface = DPP_INTERFACE_PKEX; @@ -4000,7 +4202,10 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp, dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT); dpp_property_changed_notify(dpp); - l_debug("Starting PKEX configurator for single enrollee"); + if (dpp->pkex_key) + l_debug("Starting PKEX configurator for single enrollee"); + else + l_debug("Starting PKEX configurator with agent"); return l_dbus_message_new_method_return(message); } @@ -4019,7 +4224,21 @@ static struct l_dbus_message *dpp_dbus_pkex_configure_enrollee( if (!dpp_parse_pkex_args(message, &key, &id)) return dbus_error_invalid_args(message); - return dpp_start_pkex_configurator(dpp, key, id, message); + return dpp_start_pkex_configurator(dpp, key, id, NULL, message); +} + +static struct l_dbus_message *dpp_dbus_pkex_start_configurator( + struct l_dbus *dbus, + struct l_dbus_message *message, + void *user_data) +{ + struct dpp_sm *dpp = user_data; + const char *path; + + if (!l_dbus_message_get_arguments(message, "o", &path)) + return dbus_error_invalid_args(message); + + return dpp_start_pkex_configurator(dpp, NULL, NULL, path, message); } static void dpp_setup_interface(struct l_dbus_interface *interface) @@ -4059,6 +4278,8 @@ static void dpp_setup_pkex_interface(struct l_dbus_interface *interface) dpp_dbus_pkex_stop, "", ""); l_dbus_interface_method(interface, "ConfigureEnrollee", 0, dpp_dbus_pkex_configure_enrollee, "", "a{sv}", "args"); + l_dbus_interface_method(interface, "StartConfigurator", 0, + dpp_dbus_pkex_start_configurator, "", "o", "path"); l_dbus_interface_property(interface, "Started", 0, "b", dpp_pkex_get_started, NULL);