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);