Isolate the known_frequency queue management to a function and place
that function in knownnetworks.c where it now belongs. Since we no
longer have network_info objects for unknown networks, only frequencies
for known networks are tracked
networks queue was intended to share basic network information between
multiple adapters running simultaneously. The network_info object was
also serving double duty to carry known network information. This made
things overly complicated and really didn't result in much savings.
This setup also made managing hotspot networks challenging as we would
have ended up with multiple network_info objects for each known hotspot
network.
So get rid of the networks queue and the is_known bit from the
network_info structure.
network_find_rank_index was used to find the offset of the selected
network_info among known networks so as to compute a modifier based on
the rankmod table. Instead of using known_networks_foreach for this,
moove it to knownnetworks.c where it can be coded and optimized
separately.
For now provide a simple for loop implementation.
For (Re)Association the HS20 indication element was passed exactly as
it was found in the scan results. The spec defines what bits can be
set and what cannot when this IE is used in (Re)Association. Instead
of assuming the AP's IE conforms to the spec, we now parse the IE and
re-build it for use with (Re)Association.
Since the full IE is no longer used, it was removed from scan_bss, and
replaced with a bit for HS20 support (hs20_capable). This member is
now used the same as hs20_ie was.
The version parsed during scan results is now used when building the
(Re)Association IE.
Previously, iwd used to throw net.connman.iwd.Busy when connection
attempt was made while connected. The new behavior allows iwd to
seamlessly disconnect from the connected network and attempt a new
connection.
Since NAI realms, Roaming Consortium and HESSID are defined in 802.11,
they are not a guarentee that the network is Hotspot 2.0. The indication
element in addition to these IE's gives a better idea of Hotspot 2.0
support. Now, when a BSS is added this is_hs20 boolean will get set to
true if the HS20 IE was found in the BSS.
Now, if is_hs20 is set AND one of NAI realms, roaming consortium, or
HESSID is set we know this is a hotspot 2.0 network.
This is duplicated when the first scan_bss is added to a network
object that contains the IE. Any future BSS's added will not re-add
the IE. Its assumed that all BSS's under a network will contain the
same roaming consortium OIs.
Hotspot networks are supposed to include an HESSID in the scan
results. This is more or less an identifier for the overall
network. In addition, the NAI Realms can be obtained via ANQP
and should be the same for each BSS. Since both HESSID and NAI
realms should be the same for a given network in range we can
store these values in the network object itself. This also allows
us to easily find hotspot configuration files by looking at
the HESSID/NAI Realms directly in the network object as opposed
to individual scan_bss's.
Each known network (previously connected) will have a set
of known frequencies associated with it, e.g. a set of
frequencies from all BSSs observed. The list of known
frequencies is sorted with the most recently observed
frequency in the head.
Certain error conditions require that a BSS be blacklisted only for
the duration of the current connection. The existing blacklist
does not allow for this, and since this blacklist is shared between
all interfaces it doesnt make sense to use it for this purpose.
Instead, each network object can contain its own blacklist of
scan_bss elements. New elements can be added with network_blacklist_add.
The blacklist is cleared when the connection completes, either
successfully or not.
Now inside network_bss_select both the per-network blacklist as well as
the global blacklist will be checked before returning a BSS.
If we have a BSS list where all BSS's have been blacklisted we still
need a way to force a connection to that network, instead of having
to wait for the blacklist entry to expire. network_bss_select now
takes a boolean 'fallback_to_blacklist' which causes the selection
to still return a connectable BSS even if the entire list was
blacklisted.
In most cases this is set to true, as these cases are initiated by
DBus calls. The only case where this is not true is inside
station_try_next_bss, where we do want to honor the blacklist.
This both prevents an explicit connect call (where all BSS's are
blacklisted) from trying all the blacklisted BSS's, as well as the
autoconnect case where we simply should not try to connect if all
the BSS's are blacklisted.
There are is some implied behavior here that may not be obvious:
On an explicit DBus connect call IWD will attempt to connect to
any non-blacklisted BSS found under the network. If unsuccessful,
the current BSS will be blacklisted and IWD will try the next
in the list. This will repeat until all BSS's are blacklisted,
and in this case the connect call will fail.
If a connect is tried again when all BSS's are blacklisted IWD
will attempt to connect to the first connectable blacklisted
BSS, and if this fails the connect call will fail. No more
connection attempts will happen until the next DBus call.
If IWD fails to connect to a BSS we can attempt to connect to a different
BSS under the same network and blacklist the first BSS. In the case of an
incorrect PSK (MMPDU code 2 or 23) we will still fail the connection.
station_connect_cb was refactored to better handle the dbus case. Now the
netdev result switch statement is handled before deciding whether to send
a dbus reply. This allows for both cases where we are trying to connect
to the next BSS in autoconnect, as well as in the dbus case.
If a network is being forgotten, then make sure to reset connected_time.
Otherwise the rank logic thinks that the network is known which can
result in network_find_rank_index returning -1.
Found by sanitizer:
src/network.c:1329:23: runtime error: index -1 out of bounds for type
'double [64]'
In the name of failing earlier try to generate the PSK from the
passphrase as soon as we receive the passphrase or read it from the
file, mainly to validate it has the right number of characters.
The passphrase length currently gets validates inside
crypto_psk_from_passphrase which will be called when we receive a new
passphrase from the agent or when the config file has no PSK in it. We
do not do this when there's already both the PSK and the passphrase
available in the settings -- we can add that separately if needed.
In the current version SECURITY_PSK was handled inside the is_rsn block
while the SECURITY_8021X was off in its own block. This was weird and a
bit misleading. Simplify the code flow through the use of a goto and
decrease the nesting level.
Also optimize out unnecessary use of scan_bss_get_rsn_info
In network_autoconnect, when the network was SECURITY_8021X there was no
check (for SECURITY_PSK) before calling network_load_psk. Since the
provisioning file was for an 8021x network neither PreSharedKey or
Passphrase existed so this would always fail. This fixes the 8021x failure
in testConnectAutoconnect.
Refactor the network->psk and network->passphrase loading and saving
logic to not require the PreSharedKey entry in the psk config file and
to generate network->psk lazily on request. Still cache the computed
PSK in memory and in the .psk file to avoid recomputing it which uses
many syscalls. While there update the ask_psk variable to
ask_passphrase because we're specifically asking for the passphrase.
In the case of the open networks with hidden SSIDs
the settings object is already created.
Valgrind:
==4084== at 0x4C2EB6B: malloc (vg_replace_malloc.c:299)
==4084== by 0x43B44D: l_malloc (util.c:62)
==4084== by 0x43E3FA: l_settings_new (settings.c:83)
==4084== by 0x41D101: network_connect_new_hidden_network (network.c:1053)
==4084== by 0x4105B7: station_hidden_network_scan_results (station.c:1733)
==4084== by 0x419817: scan_finished (scan.c:1165)
==4084== by 0x419CAA: get_scan_done (scan.c:1191)
==4084== by 0x443562: destroy_request (genl.c:139)
==4084== by 0x4437F7: process_unicast (genl.c:424)
==4084== by 0x4437F7: received_data (genl.c:534)
==4084== by 0x440958: io_callback (io.c:123)
==4084== by 0x43FDED: l_main_iterate (main.c:376)
==4084== by 0x43FEAB: l_main_run (main.c:423)
For an SAE network, the raw passphrase is required. For this reason,
known network psk files should now always contain a 'Passphrase' entry.
If a psk file is found without a Passphrase entry the agent will be asked
for the Passphrase before connecting. This will update the legacy psk
file with the Passphrase entry.
The previous change did not consider the case of the PSK being written
for the very first time. In this case storage_network_open would return
NULL and an empty file would be written.
Change this so that if storage_network_open fails, then the current
network settings are written to disk and not a temporary.
Reload the network settings from disk before calling
storage_network_sync in network_sync_psk to avoid potentially
overwriting changes made to the storage by user since the connection
attempt started. This won't account for all situations but it
covers some of them and doesn't cost us much.
Update the known networks list and network properties on file creations,
removals and modifications. We watch for these filesystem events using
ell's fswatch and react accordingly.
eap_append_secret now takes a new cache_policy parameter which can be
used by the EAP method to signal that the value received from the agent
is to never be cached, i.e. each value can only be used once. The
parameter value should be EAP_CACHE_NEVER for this and we use this in
value EAP-GTC where the secret tokens are one time use. The
EAP_CACHE_TEMPORARY value is used in other methods, it preserves the
default behaviour where a secret can be cached for as long as the
network stays in range (this is the current implementation more than a
design choice I believe, I didn't go for a more specific enum name as
this may still change I suppose).