nixos/wireless: implement opportunistic WPA3

It turns out it's actually possible to fall back to WPA2 in case the
authentication fails with WPA3. This was suggested to me in the hostapd
mailing list: add another network block with only WPA2 and lower
priority, for each network with WPA3. For clients with missing/broken
WPA3, wpa_supplicant will:

1. try the network block with higher priority first
2. fail and temporarily disable the network block
3. try the fallback network block and connect

This takes a little more time (still <5s) because wpa_supplicant
retries a couple times before disabling the network block, but it allows
old client to gracefully fall back to WPA2 on mixed WPA2/WPA3 networks.

To avoid downgrade attacks, clients with proper WPA3 should disable
this; in the future we may want to disable this option by default.
This commit is contained in:
rnhmjoj
2022-01-11 22:01:32 +01:00
parent 2f5ced6d7c
commit 2eed89bbe1

View File

@@ -10,9 +10,35 @@ let
cfg = config.networking.wireless;
opt = options.networking.wireless;
wpa3Protocols = [ "SAE" "FT-SAE" ];
hasWPA3 = opts: !mutuallyExclusive opts.authProtocols wpa3Protocols;
# Gives a WPA3 network higher priority
increaseWPA3Priority = opts:
opts // optionalAttrs (hasWPA3 opts)
{ priority = if opts.priority == null
then 1
else opts.priority + 1;
};
# Creates a WPA2 fallback network
mkWPA2Fallback = opts:
opts // { authProtocols = subtractLists wpa3Protocols opts.authProtocols; };
# Networks attrset as a list
networkList = mapAttrsToList (ssid: opts: opts // { inherit ssid; })
cfg.networks;
# List of all networks (normal + generated fallbacks)
allNetworks =
if cfg.fallbackToWPA2
then map increaseWPA3Priority networkList
++ map mkWPA2Fallback (filter hasWPA3 networkList)
else networkList;
# Content of wpa_supplicant.conf
generatedConfig = concatStringsSep "\n" (
(mapAttrsToList mkNetwork cfg.networks)
(map mkNetwork allNetworks)
++ optional cfg.userControlled.enable (concatStringsSep "\n"
[ "ctrl_interface=/run/wpa_supplicant"
"ctrl_interface_group=${cfg.userControlled.group}"
@@ -34,7 +60,7 @@ let
finalConfig = ''"$RUNTIME_DIRECTORY"/wpa_supplicant.conf'';
# Creates a network block for wpa_supplicant.conf
mkNetwork = ssid: opts:
mkNetwork = opts:
let
quote = x: ''"${x}"'';
indent = x: " " + x;
@@ -44,7 +70,7 @@ let
else opts.pskRaw;
options = [
"ssid=${quote ssid}"
"ssid=${quote opts.ssid}"
(if pskString != null || opts.auth != null
then "key_mgmt=${concatStringsSep " " opts.authProtocols}"
else "key_mgmt=NONE")
@@ -176,6 +202,18 @@ in {
'';
};
fallbackToWPA2 = mkOption {
type = types.bool;
default = true;
description = ''
Whether to fall back to WPA2 authentication protocols if WPA3 failed.
This allows old wireless cards (that lack recent features required by
WPA3) to connect to mixed WPA2/WPA3 access points.
To avoid possible downgrade attacks, disable this options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;