diff --git a/src/e_mod_main.c b/src/e_mod_main.c index 160316a..bcd8f60 100644 --- a/src/e_mod_main.c +++ b/src/e_mod_main.c @@ -42,6 +42,7 @@ e_modapi_shutdown(E_Module *m EINA_UNUSED) if (!e_iwd) return 1; e_iwd_gadget_shutdown(); + e_iwd_popup_shutdown(); if (e_iwd->manager) iwd_manager_free(e_iwd->manager); e_iwd_config_save(); if (e_iwd->conn) eldbus_connection_unref(e_iwd->conn); diff --git a/src/e_mod_popup.c b/src/e_mod_popup.c index c987cf2..94b777a 100644 --- a/src/e_mod_popup.c +++ b/src/e_mod_popup.c @@ -30,8 +30,39 @@ static Popup *_popup = NULL; static Iwd_Agent_Request *_pending_req = NULL; /* Tracked so iwd's Cancel(reason) can tear down the dialog. */ static Evas_Object *_pending_dialog = NULL; -/* One-shot passphrase pre-armed by the hidden-network dialog. */ -static char *_hidden_pending_pass = NULL; +/* One-shot passphrase pre-armed by the hidden-network dialog. Bound to the + * SSID it was entered for, with a timeout that wipes it if iwd never comes + * asking — so a stashed passphrase can never leak to an unrelated network. */ +static char *_hidden_pending_pass = NULL; +static char *_hidden_pending_ssid = NULL; +static Ecore_Timer *_hidden_pending_timer = NULL; +#define HIDDEN_PASS_TIMEOUT 30.0 /* seconds */ + +static void +_hidden_pending_clear(void) +{ + if (_hidden_pending_pass) + { + explicit_bzero(_hidden_pending_pass, strlen(_hidden_pending_pass)); + free(_hidden_pending_pass); + _hidden_pending_pass = NULL; + } + free(_hidden_pending_ssid); + _hidden_pending_ssid = NULL; + if (_hidden_pending_timer) + { + ecore_timer_del(_hidden_pending_timer); + _hidden_pending_timer = NULL; + } +} + +static Eina_Bool +_hidden_pending_timeout(void *data EINA_UNUSED) +{ + _hidden_pending_timer = NULL; + _hidden_pending_clear(); + return ECORE_CALLBACK_CANCEL; +} /* ----- helpers --------------------------------------------------------- */ @@ -249,10 +280,19 @@ _on_hidden_done(void *data EINA_UNUSED, const char *ssid, const char *pass, Eina if (!ok || !ssid || !*ssid) return; Iwd_Device *dev = _active_device(); if (!dev) return; - /* Pre-arm the agent reply so the next RequestPassphrase from iwd is - * answered automatically. If the network turns out to be open, the - * stashed passphrase is simply never consumed. */ - if (pass && *pass) _hidden_pending_pass = strdup(pass); + /* Pre-arm the agent reply so the next RequestPassphrase from iwd for + * *this SSID* is answered automatically. The stash is bound to the SSID + * and self-clears after HIDDEN_PASS_TIMEOUT seconds — so a typo'd or + * out-of-range SSID cannot cause the passphrase to leak to a later, + * unrelated network whose RequestPassphrase happens to land first. */ + _hidden_pending_clear(); + if (pass && *pass) + { + _hidden_pending_pass = strdup(pass); + _hidden_pending_ssid = strdup(ssid); + _hidden_pending_timer = ecore_timer_add(HIDDEN_PASS_TIMEOUT, + _hidden_pending_timeout, NULL); + } iwd_device_connect_hidden(dev, ssid); } @@ -267,7 +307,12 @@ static void _on_auth_done(void *data EINA_UNUSED, const char *pass, Eina_Bool ok) { _pending_dialog = NULL; - if (!_pending_req) return; + if (!_pending_req) + { + /* Request was already canceled by iwd; nothing to do. The caller + * (wifi_auth) wipes its own copy of `pass` after we return. */ + return; + } if (ok) iwd_agent_reply (_pending_req, pass ? pass : ""); else iwd_agent_cancel(_pending_req); _pending_req = NULL; @@ -303,15 +348,27 @@ e_iwd_popup_install_passphrase_handler(void) static void _on_passphrase_request(void *data EINA_UNUSED, Iwd_Agent_Request *req, const char *netpath) { - /* If the user just kicked off a hidden-network connect with a passphrase, - * answer this request automatically without prompting. */ - if (_hidden_pending_pass) + /* Resolve netpath -> network so we can both match the hidden stash + * against the requested SSID *and* show a friendly label in the dialog. */ + Iwd_Network *n = NULL; + if (e_iwd && e_iwd->manager) + { + const Eina_Hash *h = iwd_manager_networks(e_iwd->manager); + if (h) n = eina_hash_find(h, netpath); + } + const char *req_ssid = (n && n->ssid) ? n->ssid : NULL; + + /* Use the hidden-network stash *only* if iwd is asking for the same + * SSID we entered it for. Anything else: drop the stash on the floor + * and prompt normally. */ + if (_hidden_pending_pass && _hidden_pending_ssid && + req_ssid && !strcmp(req_ssid, _hidden_pending_ssid)) { iwd_agent_reply(req, _hidden_pending_pass); - free(_hidden_pending_pass); - _hidden_pending_pass = NULL; + _hidden_pending_clear(); return; } + if (_pending_req) { iwd_agent_cancel(req); @@ -319,27 +376,8 @@ _on_passphrase_request(void *data EINA_UNUSED, Iwd_Agent_Request *req, const cha } _pending_req = req; - /* Look up the network for a friendly SSID, if we have it. */ - const char *ssid = "network"; - if (e_iwd && e_iwd->manager) - { - const Eina_Hash *h = iwd_manager_networks(e_iwd->manager); - if (h) - { - Iwd_Network *n = eina_hash_find(h, netpath); - if (n && n->ssid) ssid = n->ssid; - } - } - const char *sec = NULL; - if (e_iwd && e_iwd->manager) - { - const Eina_Hash *h = iwd_manager_networks(e_iwd->manager); - if (h) - { - Iwd_Network *n = eina_hash_find(h, netpath); - if (n) sec = _sec_label(n->security); - } - } + const char *ssid = req_ssid ? req_ssid : "network"; + const char *sec = n ? _sec_label(n->security) : NULL; _pending_dialog = wifi_auth_prompt(_popup ? _popup->box : e_comp->elm, ssid, sec, _on_auth_done, NULL); } @@ -357,6 +395,13 @@ _destroy(void) _popup = NULL; } +void +e_iwd_popup_shutdown(void) +{ + _destroy(); + _hidden_pending_clear(); +} + void e_iwd_popup_close(void) { _destroy(); } diff --git a/src/e_mod_popup.h b/src/e_mod_popup.h index 33717a9..a053212 100644 --- a/src/e_mod_popup.h +++ b/src/e_mod_popup.h @@ -4,8 +4,9 @@ #include void e_iwd_popup_install_passphrase_handler(void); -void e_iwd_popup_toggle (E_Gadcon_Client *gcc); -void e_iwd_popup_close (void); -void e_iwd_popup_refresh(void); +void e_iwd_popup_toggle (E_Gadcon_Client *gcc); +void e_iwd_popup_close (void); +void e_iwd_popup_refresh (void); +void e_iwd_popup_shutdown(void); #endif diff --git a/src/iwd/iwd_agent.c b/src/iwd/iwd_agent.c index 6947f90..74c67f1 100644 --- a/src/iwd/iwd_agent.c +++ b/src/iwd/iwd_agent.c @@ -143,6 +143,14 @@ _on_register(void *data EINA_UNUSED, const Eldbus_Message *msg, fprintf(stderr, "e_iwd: agent register failed: %s: %s\n", en, em); } +void +iwd_agent_register(Iwd_Agent *a) +{ + if (!a || !a->am_proxy) return; + eldbus_proxy_call(a->am_proxy, "RegisterAgent", _on_register, NULL, -1, + "o", IWD_AGENT_PATH); +} + Iwd_Agent * iwd_agent_new(Eldbus_Connection *conn, Iwd_Agent_Passphrase_Cb cb, void *data) { @@ -158,12 +166,8 @@ iwd_agent_new(Eldbus_Connection *conn, Iwd_Agent_Passphrase_Cb cb, void *data) a->am_obj = eldbus_object_get(conn, IWD_BUS_NAME, "/net/connman/iwd"); if (a->am_obj) - { - a->am_proxy = eldbus_proxy_get(a->am_obj, IWD_IFACE_AGENT_MANAGER); - if (a->am_proxy) - eldbus_proxy_call(a->am_proxy, "RegisterAgent", _on_register, NULL, -1, - "o", IWD_AGENT_PATH); - } + a->am_proxy = eldbus_proxy_get(a->am_obj, IWD_IFACE_AGENT_MANAGER); + iwd_agent_register(a); return a; } diff --git a/src/iwd/iwd_agent.h b/src/iwd/iwd_agent.h index 9fcc168..49e6b65 100644 --- a/src/iwd/iwd_agent.h +++ b/src/iwd/iwd_agent.h @@ -21,6 +21,10 @@ Iwd_Agent *iwd_agent_new (Eldbus_Connection *conn, Iwd_Agent_Passphrase_Cb cb, void *data); void iwd_agent_set_cancel_cb(Iwd_Agent *a, Iwd_Agent_Cancel_Cb cb, void *data); +/* Re-issue RegisterAgent. Call after iwd reappears on the bus + * (NameOwnerChanged) — without this, every PSK connect silently hangs + * because no agent is registered against the new iwd instance. */ +void iwd_agent_register(Iwd_Agent *a); void iwd_agent_free(Iwd_Agent *a); void iwd_agent_reply (Iwd_Agent_Request *req, const char *passphrase); diff --git a/src/iwd/iwd_manager.c b/src/iwd/iwd_manager.c index 5cd78a2..49b9576 100644 --- a/src/iwd/iwd_manager.c +++ b/src/iwd/iwd_manager.c @@ -222,7 +222,14 @@ _on_iface_removed(void *data, const char *path, const char *iface) } static void -_on_name_appeared(void *data EINA_UNUSED) { /* GetManagedObjects will populate */ } +_on_name_appeared(void *data) +{ + /* GetManagedObjects will repopulate adapters/devices/networks; we just + * need to re-register our agent against the new iwd instance. Without + * this, PSK connects silently hang after `systemctl restart iwd`. */ + Iwd_Manager *m = data; + if (m && m->agent) iwd_agent_register(m->agent); +} static void _on_name_vanished(void *data) diff --git a/src/ui/wifi_auth.c b/src/ui/wifi_auth.c index 8fdb1e5..24d21d3 100644 --- a/src/ui/wifi_auth.c +++ b/src/ui/wifi_auth.c @@ -1,5 +1,6 @@ #include "wifi_auth.h" #include +#include typedef struct _Auth_Ctx { @@ -12,11 +13,27 @@ typedef struct _Auth_Ctx } Auth_Ctx; static void -_finish(Auth_Ctx *c, Eina_Bool ok, const char *pass) +_finish(Auth_Ctx *c, Eina_Bool ok) { if (c->fired) return; c->fired = EINA_TRUE; + + /* Copy the passphrase into a buffer we own so we can wipe it + * after the callback returns. The elm_entry's internal buffer + * is then overwritten before the window (and entry) are destroyed. */ + char *pass = NULL; + if (ok && c->entry) + { + const char *raw = elm_entry_entry_get(c->entry); + if (raw) pass = strdup(raw); + } if (c->cb) c->cb(c->data, pass, ok); + if (pass) + { + explicit_bzero(pass, strlen(pass)); + free(pass); + } + if (c->entry) elm_entry_entry_set(c->entry, ""); if (c->win) evas_object_del(c->win); free(c); } @@ -24,21 +41,25 @@ _finish(Auth_Ctx *c, Eina_Bool ok, const char *pass) static void _on_ok(void *data, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED) { - Auth_Ctx *c = data; - _finish(c, EINA_TRUE, elm_entry_entry_get(c->entry)); + _finish(data, EINA_TRUE); } static void _on_cancel(void *data, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED) { - _finish(data, EINA_FALSE, NULL); + _finish(data, EINA_FALSE); } static void _on_del(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED) { /* Window closed without cancel/ok — treat as cancel. */ - _finish(data, EINA_FALSE, NULL); + Auth_Ctx *c = data; + /* The window (and entry) are being destroyed; null entry to skip the + * post-cb entry_set in _finish. */ + c->win = NULL; + c->entry = NULL; + _finish(c, EINA_FALSE); } Evas_Object * diff --git a/src/ui/wifi_hidden.c b/src/ui/wifi_hidden.c index 1e936f8..3070944 100644 --- a/src/ui/wifi_hidden.c +++ b/src/ui/wifi_hidden.c @@ -1,5 +1,6 @@ #include "wifi_hidden.h" #include +#include typedef struct _Hidden_Ctx { @@ -17,9 +18,31 @@ _finish(Hidden_Ctx *c, Eina_Bool ok) { if (c->fired) return; c->fired = EINA_TRUE; - const char *ssid = ok ? elm_entry_entry_get(c->e_ssid) : NULL; - const char *pass = ok ? elm_entry_entry_get(c->e_pass) : NULL; + + /* Copy SSID + passphrase into buffers we own; wipe the passphrase + * (and overwrite the entry) before the window is destroyed. */ + char *ssid = NULL, *pass = NULL; + if (ok) + { + if (c->e_ssid) + { + const char *r = elm_entry_entry_get(c->e_ssid); + if (r) ssid = strdup(r); + } + if (c->e_pass) + { + const char *r = elm_entry_entry_get(c->e_pass); + if (r) pass = strdup(r); + } + } if (c->cb) c->cb(c->data, ssid, pass, ok); + if (pass) + { + explicit_bzero(pass, strlen(pass)); + free(pass); + } + free(ssid); + if (c->e_pass) elm_entry_entry_set(c->e_pass, ""); if (c->win) evas_object_del(c->win); free(c); } @@ -42,7 +65,11 @@ _on_cancel(void *data, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED) static void _on_del(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED) { - _finish(data, EINA_FALSE); + Hidden_Ctx *c = data; + c->win = NULL; + c->e_ssid = NULL; + c->e_pass = NULL; + _finish(c, EINA_FALSE); } static Evas_Object *