The single-use password is apparently sent in plaintext over the network
but at least try to prevent it from staying in the memory until we know
it's been used.
station.c generates the IEs we will need to use for the
Authenticate/Associate and EAPoL frames and sets them into the
handshake_state object. However the driver may modify some of them
during CMD_CONNECT and we need to use those update values so the AP
isn't confused about differing IEs in diffent frames from us.
Specifically the "wl" driver seems to do this at least for the RSN IE.
The KDF function processes data in 32 byte chunks so for groups which
primes are not divisible by 32 bytes, you will get a buffer overflow
when copying the last chunk of data.
Now l_checksum_get_digest is limited to the bytes remaining in the
buffer, or 32, whichever is the smallest.
Since eapol_encrypt_key_data already calculates the key data length and
encodes it into the key frame, we can just return this length and avoid
having to obtain it again from the frame.
Similar to SAE, EAP-PWD derives an ECC point (PWE). It is possible
for information to be gathered from the timing of this derivation,
which could be used to to recover the password.
This change adapts EAP-PWD to use the same mitigation technique as
SAE where we continue to derive ECC points even after we have found
a valid point. This derivation loop continues for a set number of
iterations (20 in this case), so anyone timing it will always see
the same timings for every run of the protocol.
This is not used by any of the scan notify callback implementations and
for P2P we're going to need to scan on an interface without an ifindex
so without this the other changes should be mostly contained in scan.
Also add a mask parameter to wiphy_get_supported_iftypes to make sure
the SupportedModes property only contains the values that can be used
as Device.Mode.
dbus_iftype_to_string returns NULL for unknown iftypes, the strdup will
also return NULL and ret[i] will be assigned a NULL. As a result
the l_strjoinv will not print the known iftypes that might have come
after that and will the l_strfreev will leak the strduped strings.
sc->state would get set when the TRIGGERED event arrived or when the
triggered callback for our own SCAN_TRIGGER command is received.
However it would not get reset to NOT_RUNNING when the NEW_SCAN_RESULTS
event is received, instead we'd first request the results with GET_SCAN
and only reset sc->state when that returns. If during that command a
new scan gets triggered, the GET_SCAN callback would still reset
sc->state and clobber the value set by the new scan.
To fix that repurpose sc->state to only track that period from the
TRIGGERED signal to the NEW_SCAN_RESULTS signal. sc->triggered can be
used to check if we're still waiting for the GET_SCAN command and
sc->start_cmd_id to check if we're waiting for the scan to get
triggered, so one of these three variables will now always indicate if
a scan is in progress.
We can crash if we abort the connection, but the connect command has
already gone through. In this case we will get a sequence of
authenticate_event, associate_event, connect_event. The first and last
events don't crash since they check whether netdev->connected is true.
However, this causes an annoying warning to be printed.
Fix this by introducing an 'aborting' flag and ignore all connection
related events if it is set.
++++++++ backtrace ++++++++
Now that the OWE failure/retry is handled in netdev, we can catch
all associate error status' inside owe_rx_associate rather than only
catching UNSUPP_FINITE_CYCLIC_GROUP.
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
OWE processing can be completely taken care of inside
netdev_authenticate_event and netdev_associate_event. This removes
the need for OWE specific checks inside netdev_connect_event. We can
now return early out of the connect event if OWE is in progress.
Several Auth/Assoc failure status codes indicate that the connection
failed for reasons such as bandwidth issues, poor channel conditions
etc. These conditions should not result in the BSS being blacklisted
since its likely only a temporary issue and the AP is not actually
"broken" per-se.
This adds support in station.c to temporarily blacklist these BSS's
on a per-network basis. After the connection has completed we clear
out these blacklist entries.
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.
Several netdev events benefit from including event data in the callback.
This is similar to how the connect callback works as well. The content
of the event data is documented in netdev.h (netdev_event_func_t).
By including event data for the two disconnect events, we can pass the
reason code to better handle the failure in station.c. Now, inside
station_disconnect_event, we still check if there is a pending connection,
and if so we can call the connect callback directly with HANDSHAKE_FAILED.
Doing it this way unifies the code path into a single switch statment to
handle all failures.
In addition, we pass the RSSI level index as event data to
RSSI_LEVEL_NOTIFY. This removes the need for a getter to be exposed in
netdev.h.
On successful send, scan_send_start(..) used to set msg to NULL,
therefore the further management of the command by the caller was
impossible. This patch removes wrapper around l_genl_family_send()
and lets the callers to take responsibility for the command.
This change cleans up the mess of status vs reason codes. The two
types of codes have already been separated into different enumerations,
but netdev was still treating them the same (with last_status_code).
A new 'event_data' argument was added to the connect callback, which
has a different meaning depending on the result of the connection
(described inside netdev.h, netdev_connect_cb_t). This allows for the
removal of netdev_get_last_status_code since the status or reason
code is now passed via event_data.
Inside the netdev object last_status_code was renamed to last_code, for
the purpose of storing either status or reason. This is only used when
a disconnect needs to be emitted before failing the connection. In all
other cases we just pass the code directly into the connect_cb and do
not store it.
All ocurrences of netdev_connect_failed were updated to use the proper
code depending on the netdev result. Most of these simply changed from
REASON_CODE_UNSPECIFIED to STATUS_CODE_UNSPECIFIED. This was simply for
consistency (both codes have the same value).
netdev_[authenticate|associate]_event's were updated to parse the
status code and, if present, use that if their was a failure rather
than defaulting to UNSPECIFIED.
Even though .check_settings in our EAP method implementations does the
settings validation, .load_settings also has minimum sanity checks to
rule out segfaults if the settings have changed since the last
.check_settings call.
If OWE fails in association there is no reason to send a disconnect
since its already known that we failed. Instead we can directly
call netdev_connect_failed
Instead of sending a reason_code to netdev_setting_keys_failed, make it
take an errno (negative) instead. Since key setting failures are
entirely a system / software issue, and not a protocol issue, it makes
no sense to use a protocol error code.
Some users may need their own control over 2.4/5GHz preference. This
adds a new user option, 'rank_5g_factor', which allows users to increase
or decrease their 5G preference.
This adds support for parsing the VHT IE, which allows a BSS supporting
VHT (80211ac) to be ranked higher than a BSS supporting only HT/basic
rates. Now, with basic/HT/VHT parsing we can calculate the theoretical
maximum data rate for all three and rank the BSS based on that.
This adds HT IE parsing and data rate calculation for HT (80211n)
rates. Now, a BSS supporting HT rates will be ranked higher than
a basic rate BSS, assuming the RSSI is at an acceptable level.
The spec dictates RSSI thresholds for different modulation schemes, which
correlate to different data rates. Until now were were ranking a BSS with
only looking at its advertised data rate, which may not even be possible
if the RSSI does not meet the threshold.
Now, RSSI is taken into consideration and the data rate returned from
parsing (Ext) Supported Rates IE(s) will reflect that.
All over the place we do "ie[1] + 2" for getting the IE length. It
is much clearer to use a macro to do this. The macro also checks
for NULL, and returns zero in this case.
Supported rates will soon be parsed along with HT/VHT capabilities
to determine the best data rate. This will remove the need for the
supported_rates uintset element in scan_bss, as well as the single
API to only parse the supported rates IE. AP still does rely on
this though (since it only supports basic rates), so the parsing
function was moved into ap.c.
In the methods' check_settings do a more complete early check for
possible certificate / private key misconfiguration, including check
that the certificate and the private key are always present or absent
together and that they actually match each other. Do this by encrypting
and decrypting a small buffer because we have no better API for that.
A method's .check_settings method checks for inconsistent setting files
and prints readable errors so there's no need to do that again in
.load_settings, although at some point after removing the duplicate
error messages from the load_settings methods we agreed to keep minimum
checks that could cause a crash e.g. in a corner case like when the
setting file got modified between the check_settings and the
load_settings call. Some error messages have been re-added to
load_settings after that (e.g. in
bb4e1ebd4f) but they're incomplete and not
useful so remove them.
Previously, the storage dir has only been created after a successful
network connection, causing removal of Known Network interface from
Dbus and failure to register dir watcher until daemon is restarted.
A length check was still assuming the 256 bit ECC group. This
was updated to scale with the group. The commit buffer was also
not properly sized. This was changed to allow for the largest
ECC group supported.
SAE was hardcoded to work only with group 19. This change fixes up the
hard coded lengths to allow it to work with group 20 since ELL supports
it. There was also good amount of logic added to support negotiating
groups. Before, since we only supported group 19, we would just reject
the connection to an AP unless it only supported group 19.
This did lead to a discovery of a potential bug in hostapd, which was
worked around in SAE in order to properly support group negotiation.
If an AP receives a commit request with a group it does not support it
should reject the authentication with code 77. According to the spec
it should also include the group number which it is rejecting. This is
not the case with hostapd. To fix this we needed to special case a
length check where we would otherwise fail the connection.
Most of this work was already done after moving ECC into ELL, but
there were still a few places where the 256-bit group was assumed.
This allows the 384-bit group to be used, and theoretically any
other group added to ELL in the future.
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.
This makes __station_connect_network even less intelligent by JUST
making it connect to a network, without any state changes. This makes
the rekey logic much cleaner.
We were also changing dbus properties when setting the state to
CONNECTING, so those dbus property change calls were moved into
station_enter_state.
A new driver extended feature bit was added signifying if the driver
supports PTK replacement/rekeying. During a connect, netdev checks
for the driver feature and sets the handshakes 'no_rekey' flag
accordingly.
At some point the AP will decide to rekey which is handled inside
eapol. If no_rekey is unset we rekey as normal and the connection
remains open. If we have set no_rekey eapol will emit
HANDSHAKE_EVENT_REKEY_FAILED, which is now caught inside station. If
this happens our only choice is to fully disconnect and reconnect.
If we receive handshake message 1/4 after we are already connected
the AP is attempting to rekey. This may not be allowed and if not
we do not process the rekey and emit HANDSHAKE_EVENT_REKEY_FAILED
so any listeners can handle accordingly.
The AP structure was getting cleaned up twice. When the DBus stop method came
in we do AP_STOP on nl80211. In this callback the AP was getting freed in
ap_reset. Also when the DBus interface was cleaned up it triggered ap_reset.
Since ap->started gets set to false in ap_reset, we now check this and bail
out if the AP is already stopped.
Fixes:
++++++++ backtrace ++++++++
0 0x7f099c11ef20 in /lib/x86_64-linux-gnu/libc.so.6
1 0x43fed0 in l_queue_foreach() at ell/queue.c:441 (discriminator 3)
2 0x423a6c in ap_reset() at src/ap.c:140
3 0x423b69 in ap_free() at src/ap.c:162
4 0x44ee86 in interface_instance_free() at ell/dbus-service.c:513
5 0x451730 in _dbus_object_tree_remove_interface() at ell/dbus-service.c:1650
6 0x405c07 in netdev_newlink_notify() at src/netdev.c:4449 (discriminator 9)
7 0x440775 in l_hashmap_foreach() at ell/hashmap.c:534
8 0x4455d3 in process_broadcast() at ell/netlink.c:158
9 0x4439b3 in io_callback() at ell/io.c:126
10 0x442c4e in l_main_iterate() at ell/main.c:473
11 0x442d1c in l_main_run() at ell/main.c:516
12 0x442f2b in l_main_run_with_signal() at ell/main.c:644
13 0x403ab3 in main() at src/main.c:504
14 0x7f099c101b97 in /lib/x86_64-linux-gnu/libc.so.6
+++++++++++++++++++++++++++
This will allow for blacklisting a BSS if the connection fails. The
actual blacklist module is simple and must be driven by station. All
it does is add BSS addresses, a timestamp, and a timeout to a queue.
Entries can also be removed, or checked if they exist. The blacklist
timeout is configuratble in main.conf, as well as the blacklist
timeout multiplier and maximum timeout. The multiplier is used after
a blacklisted BSS timeout expires but we still fail to connect on the
next connection attempt. We multiply the current timeout by the
multiplier so the BSS remains in the blacklist for a larger growing
amount of time until it reaches the maximum (24 hours by default).
Soon BSS blacklisting will be added, and in order to properly decide if
a BSS should be blacklisted we need the status code on a failed
connection. This change stores the status code when there is a failure
in netdev and adds a getter to retrieve later. In many cases we have
the actual status code from the AP, but in some corner cases its not
obtainable (e.g. an error sending an NL80211 command) in which case we
just default to MMPDU_REASON_CODE_UNSPECIFIED.
Rather than continue with the pattern of setting netdev->result and
now netdev->last_status_code, the netdev_connect_failed function was
redefined so its no longer used as both a NL80211 callback and called
directly. Instead a new function was added, netdev_disconnect_cb which
just calls netdev_connect_failed. netdev_disconnect_cb should not be
used for all the NL80211 disconnect commands. Now netdev_connect_failed
takes both a result and status code which it sets in the netdev object.
In the case where we were using netdev_connect_failed as a callback we
still need to set the result and last_status_code but at least this is
better than having to set those in all cases.
Remove an unneeded buffer and its memcpy, remove the now unneeded use of
l_checksum_digest_length and use l_checksum_reset instead of creating a
new l_checksum for each chunk.
ELL ECC supports group 20 (P384) so OWE can also support it. This also
adds group negotiation, where OWE can choose a different group than the
default if the AP requests it.
A check needed to be added in netdev in order for the negotiation to work.
The RFC says that if a group is not supported association should be rejected
with code 77 (unsupported finite cyclic group) and association should be
started again. This rejection was causing a connect event to be emitted by
the kernel (in addition to an associate event) which would result in netdev
terminating the connection, which we didn't want. Since OWE receives the
rejected associate event it can intelligently decide whether it really wants
to terminate (out of supported groups) or try the next available group.
This also utilizes the new MIC/KEK/KCK length changes, since OWE dictates
the lengths of those keys.
Rather than hard coding to SHA256, we can pass in l_checksum_type
and use that SHA. This will allow for OWE/SAE/PWD to support more
curves that use different SHA algorithms for hashing.
OWE defines KEK/KCK lengths depending on group. This change adds a
case into handshake_get_key_sizes. With OWE we can determine the
key lengths based on the PMK length in the handshake.
In preparation for OWE supporting multiple groups eapol needed some
additional cases to handle the OWE AKM since OWE dictates the KEK,
KCK and MIC key lengths (depending on group).
Right now the PMK is hard coded to 32 bytes, which works for the vast
majority of cases. The only outlier is OWE which can generate a PMK
of 32, 48 or 64 bytes depending on the ECC group used. The PMK length
is already stored in the handshake, so now we can just pass that to
crypto_derive_pairwise_ptk
The crypto_ptk was hard coded for 16 byte KCK/KEK. Depending on the
AKM these can be up to 32 bytes. This changes completely removes the
crypto_ptk struct and adds getters to the handshake object for the
kck and kek. Like before the PTK is derived into a continuous buffer,
and the kck/kek getters take care of returning the proper key offset
depending on AKM.
To allow for larger than 16 byte keys aes_unwrap needed to be
modified to take the kek length.
The MIC length was hard coded to 16 bytes everywhere, and since several
AKMs require larger MIC's (24/32) this needed to change. The main issue
was that the MIC was hard coded to 16 bytes inside eapol_key. Instead
of doing this, the MIC, key_data_length, and key_data elements were all
bundled into key_data[0]. In order to retrieve the MIC, key_data_len,
or key_data several macros were introduced which account for the MIC
length provided.
A consequence of this is that all the verify functions inside eapol now
require the MIC length as a parameter because without it they cannot
determine the byte offset of key_data or key_data_length.
The MIC length for a given handshake is set inside the SM when starting
EAPoL. This length is determined by the AKM for the handshake.
Non-802.11 AKMs can define their own key lengths. Currently only OWE does
this, and the MIC/KEK/KCK lengths will be determined by the PMK length so
we need to save it.
Make sure we don't pass NULLs to memcmp or l_memdup when the prefix
buffer is NULL. There's no point having callers pass dummy buffers if
they need to watch frames independent of the frame data.
Start using l_key_generate_dh_private and l_key_validate_dh_payload to
check for the disallowed corner case values in the DH private/public
values generated/received.
Some of the EAP methods don't require a clear-text identity to
be sent with the Identity Response packet. The mandatory identity
filed has resulted in unnecessary transmission of the garbage
values. This patch makes the Identity field to be optional and
shift responsibility to ensure its existence to the individual
methods if the field is required. All necessary identity checks
have been previously propagated to individual methods.
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]'
==25412==ERROR: AddressSanitizer: global-buffer-overflow on address 0x000000421ab0 at pc 0x000000402faf bp 0x7fffffffdb00 sp 0x7fffffffdaf0
READ of size 4 at 0x000000421ab0 thread T0
#0 0x402fae in validate_mgmt_ies src/mpdu.c:128
#1 0x403ce8 in validate_probe_request_mmpdu src/mpdu.c:370
#2 0x404ef2 in validate_mgmt_mpdu src/mpdu.c:662
#3 0x405166 in mpdu_validate src/mpdu.c:706
#4 0x402529 in ie_order_test unit/test-mpdu.c:156
#5 0x418f49 in l_test_run ell/test.c:83
#6 0x402715 in main unit/test-mpdu.c:171
#7 0x7ffff5d43ed9 in __libc_start_main (/lib64/libc.so.6+0x20ed9)
#8 0x4019a9 in _start (/home/denkenz/iwd-master/unit/test-mpdu+0x4019a9)
This fixes the valgrind warning:
==14804== Conditional jump or move depends on uninitialised value(s)
==14804== at 0x402E56: sae_is_quadradic_residue (sae.c:218)
==14804== by 0x402E56: sae_compute_pwe (sae.c:272)
==14804== by 0x402E56: sae_build_commit (sae.c:333)
==14804== by 0x402E56: sae_send_commit (sae.c:591)
==14804== by 0x401CC3: test_confirm_after_accept (test-sae.c:454)
==14804== by 0x408A28: l_test_run (test.c:83)
==14804== by 0x401427: main (test-sae.c:566)
The return from l_ecc_point_from_data was not being checked for NULL,
which would cause a segfault if the peer sent an invalid point.
This adds a check and fails the protocol if p_element is NULL, as the
spec defines.
src/eap-ttls.c:766:50: error: ‘Password’ directive output may be truncated writing 8 bytes into a region of size between 1 and 72 [-Werror=format-truncation=]
snprintf(password_key, sizeof(password_key), "%sPassword", prefix);
^~~~~~~~
In file included from /usr/include/stdio.h:862,
from src/eap-ttls.c:28:
/usr/include/bits/stdio2.h:64:10: note: ‘__builtin___snprintf_chk’ output between 9 and 80 bytes into a destination of size 72
return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__bos (__s), __fmt, __va_arg_pack ());
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Stop using l_pem_load_certificate which has been removed from ell, use
the same functions to load certificate files to validate them as those
used by the TLS implementation itself.
Check that the TLS logic has verified the server is trusted by the CA if
one was configured. This is more of an assert as ell intentionally only
allows empty certificate chains from the peer in server mode (if a CA
certficate is set) although this could be made configurable.
This should not change the behaviour except for fixing a rare crash
due to scan_cancel not working correctly when cancelling the first scan
request in the queue while a periodic scan was running, and potentially
other corner cases. To be able to better distinguish between a periodic
scan in progress and a scan request in progress add a sc->current_sr
field that points either at a scan request or is NULL when a periodic
scan is in ongoing. Move the triggered flag from scan_request and
scan_preiodic directly to scan_context so it's there together with
start_cmd_id. Hopefully make scan_cancel simpler/clearer.
Note sc->state and sc->triggered have similar semantics so one of them
may be easily removed. Also the wiphy_id parameter to the scan callback
is rather useless, note I temporarily pass 0 as the value on error but
perhaps it should be dropped.
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.