refactor(networkmanager): rewrite connection actions, list sorting and Wi-Fi security checks

pull/18/head
reionwong 1 week ago
parent 8ffd16336e
commit 640cfcdd60

@ -1,8 +1,5 @@
// SPDX-FileCopyrightText: 2026 Reion Wong <reionwong@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Derived from plasma-nm's Handler (LGPL-2.1-or-later, Copyright 2013-2014
// Jan Grulich <jgrulich@redhat.com>).
#include "cutefish/networkmanager/handler.h"
#include "cutefish/networkmanager/client.h"
@ -55,6 +52,47 @@ static bool contains(const std::vector<std::string>& list, const std::string& va
return std::find(list.begin(), list.end(), value) != list.end();
}
// The profile for running a hotspot as an access point, or as an ad-hoc
// network on an adapter that cannot.
static Settings hotspotSettings(const std::string& name, const std::string& uuid, const std::string& password,
uint32_t capabilities)
{
const bool accessPoint = capabilities & CapAp;
Settings settings;
settings["connection"] = {{"id", Variant(name)},
{"uuid", Variant(uuid)},
{"type", Variant(std::string("802-11-wireless"))},
{"autoconnect", Variant(false)}};
// NetworkManager picks a channel that is valid where the machine is.
settings["802-11-wireless"] = {{"ssid", bytesValue(wifi::ssidBytes(name))},
{"mode", Variant(std::string(accessPoint ? "ap" : "adhoc"))}};
settings["ipv4"] = {{"method", Variant(std::string("shared"))}};
settings["ipv6"] = {{"method", Variant(std::string("ignore"))}};
if (password.empty())
return settings;
VariantMap& security = settings[wirelessSecurity];
if (!accessPoint) {
// Ad-hoc networks only know WEP; type 2 hashes the password into the key.
security["key-mgmt"] = Variant(std::string("none"));
security["wep-key-type"] = Variant(uint32_t(2));
security["wep-key0"] = Variant(password);
return settings;
}
security["key-mgmt"] = Variant(std::string("wpa-psk"));
security["psk"] = Variant(password);
// WPA2 with AES alone when the adapter has it.
if (capabilities & CapRsn) {
const Variant ccmp(std::vector<std::string>{"ccmp"});
security["proto"] = Variant(std::vector<std::string>{"rsn"});
security["pairwise"] = ccmp;
security["group"] = ccmp;
}
return settings;
}
Handler::Handler()
: m_client(Client::instance())
{
@ -431,41 +469,52 @@ void Handler::reportVpnFailure(const std::string& active, uint32_t reason)
void Handler::deactivateConnection(const std::string& connection, const std::string& device)
{
const auto takeDown = [this](const std::string& active) {
m_client->call(managerPath, managerInterface, "DeactivateConnection", {path(active)}, &m_context, {},
Client::authorizedTimeout);
};
// Unlike taking the connection down, this keeps the device from connecting again by itself.
const auto disconnect = [this](const std::string& device) {
m_client->call(device, deviceInterface, "Disconnect", {}, &m_context, {}, Client::authorizedTimeout);
};
for (const std::string& active : m_client->activeConnections()) {
const std::string profile = objectPath(m_client->property(active, activeInterface, "Connection"));
const std::vector<std::string> devices = toStringList(m_client->property(active, activeInterface, "Devices"));
const bool vpn = toBool(m_client->property(active, activeInterface, "Vpn"));
const bool matches = connection.empty() ? contains(devices, device) : profile == connection;
if (!matches)
const bool onDevice = contains(devices, device);
const std::string profile = objectPath(m_client->property(active, activeInterface, "Connection"));
if (connection.empty() ? !onDevice : profile != connection)
continue;
// A VPN, or a profile named without a device (WireGuard brings its
// own), goes down alone; disconnecting a device also keeps it from
// connecting again by itself.
if (vpn || device.empty()) {
m_client->call(managerPath, managerInterface, "DeactivateConnection", {path(active)}, &m_context, {},
Client::authorizedTimeout);
} else if (contains(devices, device)) {
m_client->call(device, deviceInterface, "Disconnect", {}, &m_context, {}, Client::authorizedTimeout);
}
// A VPN rides on another device's connection, and a profile named
// without a device (WireGuard's) has an interface of its own.
if (device.empty() || toBool(m_client->property(active, activeInterface, "Vpn")))
takeDown(active);
else if (onDevice)
disconnect(device);
}
}
void Handler::removeConnection(const std::string& connection)
{
const std::string uuid = toString(m_client->setting(connection, "connection", "uuid"));
const auto remove = [this](const std::string& profile) {
m_client->call(profile, connectionInterface, "Delete", {}, &m_context, {}, Client::authorizedTimeout);
};
// Port profiles go with the profile they belong to.
// A bond's or bridge's ports name it as their controller ("master" in
// older NetworkManager) and would be left without it.
const std::string uuid = toString(m_client->setting(connection, "connection", "uuid"));
const auto isPort = [this, &uuid](const std::string& profile) {
return toString(m_client->setting(profile, "connection", "controller")) == uuid
|| toString(m_client->setting(profile, "connection", "master")) == uuid;
};
if (!uuid.empty()) {
for (const std::string& port : m_client->connections()) {
if (toString(m_client->setting(port, "connection", "controller")) == uuid
|| toString(m_client->setting(port, "connection", "master")) == uuid)
m_client->call(port, connectionInterface, "Delete", {}, &m_context, {}, Client::authorizedTimeout);
for (const std::string& profile : m_client->connections()) {
if (isPort(profile))
remove(profile);
}
}
m_client->call(connection, connectionInterface, "Delete", {}, &m_context, {}, Client::authorizedTimeout);
remove(connection);
}
void Handler::requestScan(const std::string& interface)
@ -475,20 +524,21 @@ void Handler::requestScan(const std::string& interface)
m_scanInterface = interface;
return;
}
if (!toBool(m_client->manager("WirelessEnabled")))
return;
// NetworkManager rate-limits and defers scans itself; it only refuses
// devices that cannot scan, and one in AP mode would drop the hotspot's clients.
for (const std::string& device : m_client->devices()) {
if (m_client->deviceType(device) != DeviceWifi || m_client->deviceState(device) < DeviceDisconnected
|| toUInt(m_client->property(device, wirelessInterface, "Mode")) == ModeAp)
continue;
// NetworkManager rate-limits and defers scans itself. It refuses only
// devices that cannot scan, and a scan on one running a hotspot would drop
// the hotspot's clients.
const auto scans = [this, &interface](const std::string& device) {
if (!interface.empty() && toString(m_client->property(device, deviceInterface, "Interface")) != interface)
continue;
m_client->call(device, wirelessInterface, "RequestScan", {Variant(VariantMap())}, &m_context);
return false;
return m_client->deviceType(device) == DeviceWifi && m_client->deviceState(device) >= DeviceDisconnected
&& toUInt(m_client->property(device, wirelessInterface, "Mode")) != ModeAp;
};
for (const std::string& device : m_client->devices()) {
if (scans(device))
m_client->call(device, wirelessInterface, "RequestScan", {Variant(VariantMap())}, &m_context);
}
}
@ -528,41 +578,11 @@ void Handler::createHotspot()
return;
}
const bool apMode = capabilities & CapAp;
const HotspotConfiguration* configuration = HotspotConfiguration::instance();
const std::string name = configuration->name();
const std::string password = configuration->password();
const std::string existing = m_client->hotspotConnection();
const std::string uuid = existing.empty() ? text::newUuid()
: toString(m_client->setting(existing, "connection", "uuid"));
Settings settings;
settings["connection"] = {{"id", Variant(name)},
{"uuid", Variant(uuid)},
{"type", Variant(std::string("802-11-wireless"))},
{"autoconnect", Variant(false)}};
settings["802-11-wireless"] = {{"ssid", bytesValue(wifi::ssidBytes(name))},
{"mode", Variant(std::string(apMode ? "ap" : "adhoc"))}};
// NetworkManager picks a channel that is valid where the machine is.
if (!password.empty()) {
VariantMap security;
if (apMode) {
security["key-mgmt"] = Variant(std::string("wpa-psk"));
security["psk"] = Variant(password);
if (capabilities & CapRsn) {
security["proto"] = Variant(std::vector<std::string>{"rsn"});
security["pairwise"] = Variant(std::vector<std::string>{"ccmp"});
security["group"] = Variant(std::vector<std::string>{"ccmp"});
}
} else {
security["key-mgmt"] = Variant(std::string("none"));
security["wep-key-type"] = Variant(uint32_t(2));
security["wep-key0"] = Variant(password);
}
settings[wirelessSecurity] = security;
}
settings["ipv4"] = {{"method", Variant(std::string("shared"))}};
settings["ipv6"] = {{"method", Variant(std::string("ignore"))}};
const Settings settings = hotspotSettings(configuration->name(), uuid, configuration->password(), capabilities);
// One profile, kept and updated, rather than a new one each time.
if (existing.empty()) {

@ -1,8 +1,5 @@
// SPDX-FileCopyrightText: 2026 Reion Wong <reionwong@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Derived from plasma-nm's NetworkModel and AppletProxyModel (LGPL-2.1-or-later,
// Copyright 2013-2018 Jan Grulich <jgrulich@redhat.com>).
#include "cutefish/networkmanager/network_model.h"
#include "cutefish/networkmanager/client.h"
@ -15,7 +12,9 @@
#include <algorithm>
#include <cstdio>
#include <endian.h>
#include <iterator>
#include <memory>
#include <tuple>
#include "base/locale.h"
@ -253,7 +252,6 @@ void NetworkModel::addWired(const std::string& device, std::vector<NetworkItem>&
for (const std::string& profile : profilesFor(*m_client, device, "802-3-ethernet")) {
NetworkItem item;
item.type = ConnectionType::Wired;
item.kind = NetworkItem::Kind::AvailableConnection;
item.uuid = toString(m_client->setting(profile, connectionSetting, "uuid"));
item.key = device + "|" + item.uuid;
item.name = toString(m_client->setting(profile, connectionSetting, "id"));
@ -406,10 +404,8 @@ void NetworkModel::addWireless(const std::string& device, std::vector<NetworkIte
item.signal = std::max(network.strength, 0);
item.icon = wifi::strengthIcon(item.signal);
if (network.profile.empty()) {
item.kind = NetworkItem::Kind::AvailableAccessPoint;
item.name = item.ssid;
} else {
item.kind = NetworkItem::Kind::AvailableConnection;
item.connectionPath = network.profile;
item.uuid = toString(m_client->setting(network.profile, connectionSetting, "uuid"));
item.name = toString(m_client->setting(network.profile, connectionSetting, "id"));
@ -464,7 +460,6 @@ void NetworkModel::addVpns(std::vector<NetworkItem>& items) const
NetworkItem item;
item.type = ConnectionType::Vpn;
item.kind = NetworkItem::Kind::AvailableConnection;
item.uuid = toString(m_client->setting(profile, connectionSetting, "uuid"));
item.key = "vpn|" + item.uuid;
item.name = toString(m_client->setting(profile, connectionSetting, "id"));
@ -543,50 +538,29 @@ int compareNames(const std::string& a, const std::string& b)
return base::Locale::system().compare(a, b);
}
// Sorted descending, as the list wants it: whatever compares greater comes first.
static bool lessThan(const NetworkItem& left, const NetworkItem& right, ConnectionType type)
// Greater ranks are listed first. The signal counts by the icon's step, so
// rows move only when what they show changes; a lower state is further along.
static auto rank(const NetworkItem& item)
{
// A VPN keeps its place, whether it is up or not.
if (type == ConnectionType::Vpn)
return compareNames(left.name, right.name) > 0;
const bool leftConnected = left.connectionState == ConnectionStatus::Activated;
const bool rightConnected = right.connectionState == ConnectionStatus::Activated;
if (leftConnected != rightConnected)
return rightConnected;
if (left.connectionState != right.connectionState)
return int(left.connectionState) > int(right.connectionState);
const bool leftSaved = !left.uuid.empty();
const bool rightSaved = !right.uuid.empty();
if (leftSaved != rightSaved)
return rightSaved;
if (left.timestamp != right.timestamp)
return left.timestamp < right.timestamp;
// By the icon's step, so rows move only when what they show changes.
const int leftSignal = wifi::strengthStep(left.signal);
const int rightSignal = wifi::strengthStep(right.signal);
if (leftSignal != rightSignal)
return leftSignal < rightSignal;
return compareNames(left.name, right.name) > 0;
return std::make_tuple(item.connectionState == ConnectionStatus::Activated, -int(item.connectionState),
!item.uuid.empty(), item.timestamp, wifi::strengthStep(item.signal));
}
std::vector<NetworkItem> listItems(const std::vector<NetworkItem>& items, ConnectionType type)
{
std::vector<NetworkItem> list;
for (const NetworkItem& item : items) {
if (item.type != type || type == ConnectionType::Unknown)
continue;
if (item.kind != NetworkItem::Kind::AvailableConnection && item.kind != NetworkItem::Kind::AvailableAccessPoint)
continue;
list.push_back(item);
}
std::stable_sort(list.begin(), list.end(),
[type](const NetworkItem& a, const NetworkItem& b) { return lessThan(b, a, type); });
if (type == ConnectionType::Unknown)
return list;
std::copy_if(items.begin(), items.end(), std::back_inserter(list),
[type](const NetworkItem& item) { return item.type == type; });
// A VPN keeps its place by name, whether it is up or not.
const bool byNameOnly = type == ConnectionType::Vpn;
std::stable_sort(list.begin(), list.end(), [byNameOnly](const NetworkItem& a, const NetworkItem& b) {
if (!byNameOnly && rank(a) != rank(b))
return rank(a) > rank(b);
return compareNames(a.name, b.name) < 0;
});
return list;
}

@ -18,21 +18,15 @@ class Client;
// One row of the network lists.
struct NetworkItem
{
enum class Kind {
UnavailableConnection = 0,
AvailableConnection,
AvailableAccessPoint,
};
// Stays the same while the row's access points come and go.
std::string key;
ConnectionType type = ConnectionType::Unknown;
Kind kind = Kind::UnavailableConnection;
std::string name;
// The name with its device added when two rows share it.
std::string uniqueName;
std::string ssid;
std::string uuid;
// The saved profile; empty for a Wi-Fi network never joined.
std::string connectionPath;
std::string devicePath;
std::string deviceName;

@ -323,7 +323,7 @@ TEST_F(ClientTest, ListsNetworksByName)
// Both of its access points make one network, shown by the stronger one.
const auto home = row(model, "Home");
ASSERT_TRUE(home);
EXPECT_EQ(home->kind, NetworkItem::Kind::AvailableConnection);
EXPECT_FALSE(home->connectionPath.empty());
EXPECT_EQ(home->connectionPath, homeProfile);
EXPECT_EQ(home->specificPath, homeStrong);
EXPECT_EQ(home->signal, 70);
@ -331,7 +331,7 @@ TEST_F(ClientTest, ListsNetworksByName)
const auto cafeRow = row(model, "Cafe");
ASSERT_TRUE(cafeRow);
EXPECT_EQ(cafeRow->kind, NetworkItem::Kind::AvailableAccessPoint);
EXPECT_TRUE(cafeRow->connectionPath.empty());
EXPECT_EQ(cafeRow->securityType, SecurityType::None);
EXPECT_EQ(row(model, "Library")->securityType, SecurityType::Wpa2Eap);

@ -1,14 +1,12 @@
// SPDX-FileCopyrightText: 2026 Reion Wong <reionwong@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// The SSID charset table and security checks are ported from NetworkManager's
// src/libnm-core-impl/nm-utils.c (LGPL-2.1-or-later, Copyright 2005 - 2017 Red Hat, Inc.).
#include "cutefish/networkmanager/wifi_utils.h"
#include <algorithm>
#include <cctype>
#include <iconv.h>
#include <span>
#include <sys/random.h>
#include <vector>
@ -16,59 +14,43 @@
namespace cutefish::networkmanager::wifi {
struct LocaleEncodings
// SSIDs set before UTF-8 took over are in the legacy charset of the router
// owner's language. The code pages most setup pages wrote in come before
// the ISO and KOI8 sets, and wider supersets before what they contain.
struct LegacyCharsets
{
const char* locale;
std::vector<const char*> encodings;
std::vector<const char*> languages;
std::vector<const char*> charsets;
};
// NetworkManager's table in libnm-core-impl/nm-utils.c, by language and then country.
static const LocaleEncodings localeEncodings[] = {
{"zh_cn", {"GB18030", "EUC-CN", "GB2312"}},
{"zh_sg", {"GB18030", "EUC-CN", "GB2312"}},
{"zh_hk", {"BIG5", "BIG5-HKSCS", "EUC-TW"}},
{"zh_mo", {"BIG5", "EUC-TW"}},
{"zh_tw", {"BIG5", "EUC-TW"}},
{"ar", {"ISO-8859-6", "WINDOWS-1256"}},
{"be", {"KOI8-R", "WINDOWS-1251", "ISO-8859-5"}},
{"bg", {"WINDOWS-1251", "KOI8-R", "ISO-8859-5"}},
{"cs", {"ISO-8859-2", "WINDOWS-1250"}},
{"el", {"ISO-8859-7", "WINDOWS-1253"}},
{"et", {"ISO-8859-4", "WINDOWS-1257"}},
{"he", {"ISO-8859-8", "WINDOWS-1255"}},
{"hr", {"ISO-8859-2", "WINDOWS-1250"}},
{"hu", {"ISO-8859-2", "WINDOWS-1250"}},
{"iw", {"ISO-8859-8", "WINDOWS-1255"}},
{"ja", {"EUC-JP", "SHIFT_JIS", "ISO-2022-JP"}},
{"ko", {"EUC-KR", "ISO-2022-KR", "JOHAB"}},
{"lt", {"ISO-8859-4", "WINDOWS-1257"}},
{"lv", {"ISO-8859-4", "WINDOWS-1257"}},
{"mk", {"KOI8-R", "WINDOWS-1251", "ISO-8859-5"}},
{"pl", {"ISO-8859-2", "WINDOWS-1250"}},
{"ro", {"ISO-8859-2", "WINDOWS-1250"}},
{"ru", {"KOI8-R", "WINDOWS-1251", "ISO-8859-5"}},
{"sh", {"ISO-8859-2", "WINDOWS-1250"}},
{"sk", {"ISO-8859-2", "WINDOWS-1250"}},
{"sl", {"ISO-8859-2", "WINDOWS-1250"}},
{"sr", {"KOI8-R", "WINDOWS-1251", "ISO-8859-5"}},
{"th", {"ISO-8859-11", "WINDOWS-874"}},
{"tr", {"ISO-8859-9", "WINDOWS-1254"}},
{"uk", {"KOI8-U", "KOI8-R", "WINDOWS-1251"}},
static const LegacyCharsets legacyCharsets[] = {
{{"zh_cn", "zh_sg"}, {"GB18030"}},
{{"zh_tw", "zh_hk", "zh_mo"}, {"BIG5-HKSCS", "EUC-TW"}},
{{"ja"}, {"CP932", "EUC-JP", "ISO-2022-JP"}},
{{"ko"}, {"CP949", "ISO-2022-KR", "JOHAB"}},
{{"uk"}, {"WINDOWS-1251", "KOI8-U"}},
{{"ru", "be", "bg", "mk", "sr"}, {"WINDOWS-1251", "KOI8-R", "ISO-8859-5"}},
{{"cs", "hr", "hu", "pl", "ro", "sh", "sk", "sl"}, {"WINDOWS-1250", "ISO-8859-2"}},
{{"et", "lt", "lv"}, {"WINDOWS-1257", "ISO-8859-13"}},
{{"el"}, {"WINDOWS-1253", "ISO-8859-7"}},
{{"he", "iw"}, {"WINDOWS-1255", "ISO-8859-8"}},
{{"ar"}, {"WINDOWS-1256", "ISO-8859-6"}},
{{"th"}, {"WINDOWS-874"}},
{{"tr"}, {"WINDOWS-1254", "ISO-8859-9"}},
};
static std::vector<const char*> encodingsForLocale(const std::string& language)
// By language and country first, then by language alone.
static std::vector<const char*> charsetsFor(const std::string& language)
{
std::string name = language.empty() ? base::Locale::system().name() : language;
name = name.substr(0, name.find_first_of(".@"));
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); });
for (const auto& entry : localeEncodings) {
if (name == entry.locale)
return entry.encodings;
}
const std::string languageCode = name.substr(0, 2);
for (const auto& entry : localeEncodings) {
if (languageCode == entry.locale)
return entry.encodings;
for (const std::string& key : {name, name.substr(0, 2)}) {
for (const LegacyCharsets& entry : legacyCharsets) {
if (std::find(entry.languages.begin(), entry.languages.end(), key) != entry.languages.end())
return entry.charsets;
}
}
return {};
}
@ -166,67 +148,79 @@ static std::string printable(const std::string& utf8)
return text;
}
static bool supportsCiphers(uint32_t caps, uint32_t flags, bool staticWep)
// A cipher an access point lists, and the adapter capability it needs.
struct Cipher
{
bool pair = staticWep;
if (!staticWep) {
pair = ((caps & CapWep40) && (flags & PairWep40)) || ((caps & CapWep104) && (flags & PairWep104))
|| ((caps & CapTkip) && (flags & PairTkip)) || ((caps & CapCcmp) && (flags & PairCcmp));
}
uint32_t flag;
uint32_t capability;
};
bool group = ((caps & CapWep40) && (flags & GroupWep40)) || ((caps & CapWep104) && (flags & GroupWep104));
if (!staticWep)
group = group || ((caps & CapTkip) && (flags & GroupTkip)) || ((caps & CapCcmp) && (flags & GroupCcmp));
static constexpr Cipher pairwiseCiphers[] = {
{PairWep40, CapWep40}, {PairWep104, CapWep104}, {PairTkip, CapTkip}, {PairCcmp, CapCcmp}};
static constexpr Cipher groupCiphers[] = {
{GroupWep40, CapWep40}, {GroupWep104, CapWep104}, {GroupTkip, CapTkip}, {GroupCcmp, CapCcmp}};
return pair && group;
static bool anyUsable(uint32_t caps, uint32_t flags, std::span<const Cipher> ciphers)
{
return std::any_of(ciphers.begin(), ciphers.end(), [caps, flags](const Cipher& cipher) {
return (flags & cipher.flag) && (caps & cipher.capability);
});
}
// The adapter can take part in what a WPA or RSN element lists. With static
// WEP the keys are fixed, so only a WEP group cipher has to match.
static bool ciphersUsable(uint32_t caps, uint32_t flags, bool staticWep)
{
if (staticWep)
return anyUsable(caps, flags, std::span(groupCiphers).first(2));
return anyUsable(caps, flags, pairwiseCiphers) && anyUsable(caps, flags, groupCiphers);
}
static bool pskCipher(uint32_t caps, uint32_t flags)
// A pre-shared key needs TKIP or CCMP as the pairwise cipher.
static bool pskUsable(uint32_t caps, uint32_t flags)
{
return ((flags & PairTkip) && (caps & CapTkip)) || ((flags & PairCcmp) && (caps & CapCcmp));
return anyUsable(caps, flags, std::span(pairwiseCiphers).last(2));
}
// nm_utils_security_valid() for an access point that is in range.
static bool valid(SecurityType type, uint32_t caps, bool adhoc, uint32_t apFlags, uint32_t wpa, uint32_t rsn)
// Whether the adapter and an access point in range can use `type` together.
static bool usable(SecurityType type, uint32_t caps, bool adhoc, uint32_t apFlags, uint32_t wpa, uint32_t rsn)
{
const bool privacy = apFlags & ApPrivacy;
const bool wpaCapable = caps & CapWpa;
const bool rsnCapable = caps & CapRsn;
const bool rsnCcmp = (rsn & PairCcmp) && (caps & CapCcmp);
switch (type) {
case SecurityType::None:
return !(apFlags & ApPrivacy) && !wpa && !rsn;
case SecurityType::Leap:
if (adhoc)
return false;
[[fallthrough]];
return !privacy && wpa == 0 && rsn == 0;
case SecurityType::StaticWep:
if (!(apFlags & ApPrivacy))
case SecurityType::Leap:
// LEAP works on infrastructure networks only.
if (!privacy || (adhoc && type == SecurityType::Leap))
return false;
if (wpa || rsn)
return supportsCiphers(caps, wpa, true) || supportsCiphers(caps, rsn, true);
return true;
return (wpa == 0 && rsn == 0) || ciphersUsable(caps, wpa, true) || ciphersUsable(caps, rsn, true);
case SecurityType::DynamicWep:
if (adhoc || rsn || !(apFlags & ApPrivacy))
if (adhoc || rsn != 0 || !privacy)
return false;
// Some access points broadcast minimal WPA-enabled beacons.
if (wpa)
return (wpa & KeyMgmt8021x) && supportsCiphers(caps, wpa, false);
return true;
// Some access points also send a WPA element that offers 802.1X.
return wpa == 0 || ((wpa & KeyMgmt8021x) && ciphersUsable(caps, wpa, false));
case SecurityType::WpaPsk:
return !adhoc && (caps & CapWpa) && (wpa & KeyMgmtPsk) && pskCipher(caps, wpa);
return !adhoc && wpaCapable && (wpa & KeyMgmtPsk) && pskUsable(caps, wpa);
case SecurityType::Wpa2Psk:
if (!(caps & CapRsn))
return false;
// An ad-hoc RSN network has no key management to advertise.
if (adhoc)
return (caps & CapIbssRsn) && (rsn & PairCcmp) && (caps & CapCcmp);
return (rsn & KeyMgmtPsk) && pskCipher(caps, rsn);
return rsnCapable && (caps & CapIbssRsn) && rsnCcmp;
return rsnCapable && (rsn & KeyMgmtPsk) && pskUsable(caps, rsn);
case SecurityType::WpaEap:
return !adhoc && (caps & CapWpa) && (wpa & KeyMgmt8021x) && supportsCiphers(caps, wpa, false);
return !adhoc && wpaCapable && (wpa & KeyMgmt8021x) && ciphersUsable(caps, wpa, false);
case SecurityType::Wpa2Eap:
return !adhoc && (caps & CapRsn) && (rsn & KeyMgmt8021x) && supportsCiphers(caps, rsn, false);
return !adhoc && rsnCapable && (rsn & KeyMgmt8021x) && ciphersUsable(caps, rsn, false);
case SecurityType::Sae:
return !adhoc && (caps & CapRsn) && (rsn & KeyMgmtSae) && (rsn & PairCcmp) && (caps & CapCcmp);
return !adhoc && rsnCapable && (rsn & KeyMgmtSae) && rsnCcmp;
case SecurityType::Owe:
return !adhoc && (caps & CapRsn) && (rsn & (KeyMgmtOwe | KeyMgmtOweTm));
return !adhoc && rsnCapable && (rsn & (KeyMgmtOwe | KeyMgmtOweTm));
case SecurityType::Wpa3SuiteB192:
return !adhoc && (caps & CapRsn) && (rsn & KeyMgmtEapSuiteB192);
return !adhoc && rsnCapable && (rsn & KeyMgmtEapSuiteB192);
default:
return false;
}
@ -260,7 +254,7 @@ std::string ssidLabel(const std::string& ssid, const std::string& language)
if (decodeUtf8(bytes, nullptr))
return printable(bytes);
for (const char* encoding : encodingsForLocale(language)) {
for (const char* encoding : charsetsFor(language)) {
std::string decoded;
if (decode(bytes, encoding, &decoded))
return printable(decoded);
@ -308,7 +302,7 @@ SecurityType security(uint32_t deviceCapabilities, uint32_t apFlags, uint32_t wp
const bool adhoc = mode == ModeAdhoc;
for (const auto type : order) {
if (valid(type, deviceCapabilities, adhoc, apFlags, wpaFlags, rsnFlags))
if (usable(type, deviceCapabilities, adhoc, apFlags, wpaFlags, rsnFlags))
return type;
}
return advertised(apFlags, wpaFlags, rsnFlags);

@ -22,9 +22,8 @@ std::string ssidKey(const std::string& ssid);
// At most 32 bytes, cut on a character boundary.
std::string ssidBytes(const std::string& name);
// The strongest scheme both the device and the access point support, as
// NetworkManager's nm_utils_security_valid() decides; the AP's flags alone
// when the device reports no capabilities.
// The strongest scheme both the device and the access point support; the
// AP's flags alone when the device reports no capabilities.
SecurityType security(uint32_t deviceCapabilities, uint32_t apFlags, uint32_t wpaFlags, uint32_t rsnFlags,
uint32_t mode);
bool enterprise(SecurityType type);

Loading…
Cancel
Save