From 17a4aa962fb85b54c0292cd8b9f8e9bf2d200d64 Mon Sep 17 00:00:00 2001 From: Wim de With Date: Thu, 9 Nov 2023 21:08:26 +0100 Subject: [PATCH] Fix connection bug with non-ASCII SSIDs in wpa_supplicant (#11089) * Bump base includes: koreader/koreader-base#1691 koreader/koreader-base#1692 koreader/koreader-base#1689 koreader/koreader-base#1690 koreader/koreader-base#1693 * Integrate decoding of SSIDs within wpa_supplicant The UTF-8 decoding of SSIDs is specific to wpa_supplicant. In this patch, we move all of this decoding logic to the wpa_supplicant module. We expose the raw bytes of the SSID to the NetworkMgr code, and make sure to always fix bad UTF-8 before we display the SSID to the user. Within the wpa_supplicant module, we replace the call to the wpa_passphrase binary to get the PSK with a direct function call to OpenSSL. This allows us to calculate the PSK over any arbitrary bytes, including UTF-8. In the same vein, we use the hex-encoded SSID to communicate with wpa_supplicant when setting up the network to support arbitrary bytes in the SSID. Unfortunately, we also remove the tests, as there is no way to unit test local functions. --- base | 2 +- frontend/ui/network/manager.lua | 19 +------------- frontend/ui/network/wpa_supplicant.lua | 35 ++++++++++++++++++-------- frontend/ui/widget/networksetting.lua | 13 +++++----- spec/unit/network_manager_spec.lua | 24 ------------------ 5 files changed, 34 insertions(+), 59 deletions(-) diff --git a/base b/base index 90681b01b..99578f792 160000 --- a/base +++ b/base @@ -1 +1 @@ -Subproject commit 90681b01b97b80fa64abfb895854789ab9c5f199 +Subproject commit 99578f7921fa63e326f9efdeca0c43c3897785d0 diff --git a/frontend/ui/network/manager.lua b/frontend/ui/network/manager.lua index db7399abb..da089d411 100644 --- a/frontend/ui/network/manager.lua +++ b/frontend/ui/network/manager.lua @@ -1067,7 +1067,7 @@ function NetworkMgr:reconnectOrShowNetworkMenu(complete_callback, interactive) end UIManager:show(InfoMessage:new{ tag = "NetworkMgr", -- for crazy KOSync purposes - text = T(_("Connected to network %1"), BD.wrap(self.decodeSSID(ssid))), + text = T(_("Connected to network %1"), BD.wrap(util.fixUtf8(ssid, "�"))), timeout = 3, }) else @@ -1119,23 +1119,6 @@ function NetworkMgr:setWirelessBackend(name, options) require("ui/network/"..name).init(self, options) end -function NetworkMgr.decodeSSID(text) - local decode = function(b) - local c = string.char(tonumber(b, 16)) - -- This is a hack that allows us to make sure that any decoded backslash - -- does not get replaced in the step that replaces double backslashes. - if c == "\\" then - return "\\\\" - else - return c - end - end - - local decoded = text:gsub("%f[\\]\\x(%x%x)", decode) - decoded = decoded:gsub("\\\\", "\\") - return util.fixUtf8(decoded, "�") -end - -- set network proxy if global variable G_defaults:readSetting("NETWORK_PROXY") is defined if G_defaults:readSetting("NETWORK_PROXY") then NetworkMgr:setHTTPProxy(G_defaults:readSetting("NETWORK_PROXY")) diff --git a/frontend/ui/network/wpa_supplicant.lua b/frontend/ui/network/wpa_supplicant.lua index bf183b53a..5bf2f177c 100644 --- a/frontend/ui/network/wpa_supplicant.lua +++ b/frontend/ui/network/wpa_supplicant.lua @@ -2,6 +2,8 @@ WPA client helper for Kobo. ]] +local crypto = require("ffi/crypto") +local bin_to_hex = require("ffi/sha2").bin_to_hex local FFIUtil = require("ffi/util") local InfoMessage = require("ui/widget/infomessage") local WpaClient = require("lj-wpaclient/wpaclient") @@ -13,6 +15,23 @@ local CLIENT_INIT_ERR_MSG = _("Failed to initialize network control client: %1." local WpaSupplicant = {} +local function decodeSSID(ssid) + local decode = function(b) + local c = string.char(tonumber(b, 16)) + -- This is a hack that allows us to make sure that any decoded backslash + -- does not get replaced in the step that replaces double backslashes. + if c == "\\" then + return "\\\\" + else + return c + end + end + + local decoded = ssid:gsub("%f[\\]\\x(%x%x)", decode) + decoded = decoded:gsub("\\\\", "\\") + return decoded +end + --- Gets network list. function WpaSupplicant:getNetworkList() local wcli, err = WpaClient.new(self.wpa_supplicant.ctrl_interface) @@ -31,6 +50,7 @@ function WpaSupplicant:getNetworkList() local curr_network = self:getCurrentNetwork() for _, network in ipairs(list) do + network.ssid = decodeSSID(network.ssid) network.signal_quality = network:getSignalQuality() local saved_nw = saved_networks:readSetting(network.ssid) if saved_nw then @@ -49,15 +69,7 @@ function WpaSupplicant:getNetworkList() end local function calculatePsk(ssid, pwd) - --- @todo calculate PSK with native function instead of shelling out - -- hostap's reference implementation is available at: - -- * /wpa_supplicant/wpa_passphrase.c - -- * /src/crypto/sha1-pbkdf2.c - -- see: - local fp = io.popen(("wpa_passphrase %q %q"):format(ssid, pwd)) - local out = fp:read("*a") - fp:close() - return string.match(out, "psk=([a-f0-9]+)") + return bin_to_hex(crypto.pbkdf2_hmac_sha1(pwd, ssid, 4096, 32)) end --- Authenticates network. @@ -75,7 +87,7 @@ function WpaSupplicant:authenticateNetwork(network) end local nw_id = reply - reply, err = wcli:setNetwork(nw_id, "ssid", string.format("\"%s\"", network.ssid)) + reply, err = wcli:setNetwork(nw_id, "ssid", bin_to_hex(network.ssid)) if reply == nil or reply == "FAIL" then wcli:removeNetwork(nw_id) return false, T("An error occurred while selecting network: %1.", err) @@ -185,6 +197,9 @@ function WpaSupplicant:getCurrentNetwork() end local nw = wcli:getCurrentNetwork() wcli:close() + if nw ~= nil then + nw.ssid = decodeSSID(nw.ssid) + end return nw end diff --git a/frontend/ui/widget/networksetting.lua b/frontend/ui/widget/networksetting.lua index 5e7890f3c..eb16cccd7 100644 --- a/frontend/ui/widget/networksetting.lua +++ b/frontend/ui/widget/networksetting.lua @@ -56,6 +56,7 @@ local OverlapGroup = require("ui/widget/overlapgroup") local Size = require("ui/size") local TextWidget = require("ui/widget/textwidget") local UIManager = require("ui/uimanager") +local util = require("util") local VerticalGroup = require("ui/widget/verticalgroup") local Widget = require("ui/widget/widget") local _ = require("gettext") @@ -106,7 +107,7 @@ local NetworkItem = InputContainer:extend{ icon_size = Screen:scaleBySize(32), width = nil, info = nil, - decoded_ssid = nil, + display_ssid = nil, background = Blitbuffer.COLOR_WHITE, } @@ -115,7 +116,7 @@ function NetworkItem:init() if not self.info.ssid then self.info.ssid = "[hidden]" end - self.decoded_ssid = NetworkMgr.decodeSSID(self.info.ssid) + self.display_ssid = util.fixUtf8(self.info.ssid, "�") local wifi_icon if string.find(self.info.flags, "WPA") then @@ -151,7 +152,7 @@ function NetworkItem:init() }, horizontal_space, TextWidget:new{ - text = self.decoded_ssid, + text = self.display_ssid, face = Font:getFace("cfont"), }, }, @@ -285,7 +286,7 @@ end function NetworkItem:onEditNetwork() local password_input password_input = InputDialog:new{ - title = self.decoded_ssid, + title = self.display_ssid, input = self.info.password, input_hint = _("password (leave empty for open networks)"), input_type = "text", @@ -328,7 +329,7 @@ end function NetworkItem:onAddNetwork() local password_input password_input = InputDialog:new{ - title = self.decoded_ssid, + title = self.display_ssid, input = "", input_hint = _("password (leave empty for open networks)"), input_type = "text", @@ -490,7 +491,7 @@ function NetworkSetting:init() UIManager:close(self, "ui", self.dimen) end UIManager:show(InfoMessage:new{ - text = T(_("Connected to network %1"), BD.wrap(connected_item.decoded_ssid)), + text = T(_("Connected to network %1"), BD.wrap(connected_item.display_ssid)), timeout = 3, }) if self.connect_callback then diff --git a/spec/unit/network_manager_spec.lua b/spec/unit/network_manager_spec.lua index 91be3d358..e0e795dd7 100644 --- a/spec/unit/network_manager_spec.lua +++ b/spec/unit/network_manager_spec.lua @@ -73,30 +73,6 @@ describe("network_manager module", function() assert.is.same(release_ip_called, 0) end) - describe("decodeSSID()", function() - local NetworkMgr = require("ui/network/manager") - - it("should correctly unescape emoji", function() - assert.is_equal("📚", NetworkMgr.decodeSSID("\\xf0\\x9f\\x93\\x9a")) - end) - - it("should correctly unescape multiple characters", function() - assert.is_equal("神舟五号", NetworkMgr.decodeSSID("\\xe7\\xa5\\x9e\\xe8\\x88\\x9f\\xe4\\xba\\x94\\xe5\\x8f\\xb7")) - end) - - it("should ignore escaped backslashes", function() - assert.is_equal("\\x61", NetworkMgr.decodeSSID("\\\\x61")) - end) - - it("should not remove encoded backslashes", function() - assert.is_equal("\\\\", NetworkMgr.decodeSSID("\\x5c\\")) - end) - - it("should deal with invalid UTF-8 (relatively) gracefully", function() - assert.is_equal("��", NetworkMgr.decodeSSID("\\xe2\\x82")) - end) - end) - teardown(function() function Device:initNetworkManager() end function Device:hasWifiRestore() return false end