From 4ab8e4c9263291ac1e21018af209d7709f835058 Mon Sep 17 00:00:00 2001 From: Danilo Egea Gondolfo Date: Tue, 22 Aug 2023 16:40:24 +0100 Subject: [PATCH] netplan: add support for WPA3-Enterprise and PMF These changes introduce support for enabling PMF (Protected Management Frames) through Netplan configuration. PMF is required by WPA3 and was previously implicitly set to "required" for WPA3-Personal (via SAE). There are occasions where the user might not want to restrict the configuration to WPA3-only (WPA3-Enterprise transition mode) Network Manager will enable different EAP methods simultaneously when we set it to "eap", such as WPA-EAP and WPA-EAP-SHA256. NM doesn't allow the user to set the method as "eap-sha256" only. As we want to allow the user to use WPA-EAP-SHA256 to enable WPA3, we can't set the PMF to required or it will break WPA2. We could set it implicitly to "optional" but it would change the default configuration we generate. Because of that, a new key was added to Netplan to enable users to set the PMF mode manually. These changes also add two new EAP methods to Netplan: "eap-sha256" and "eap-suite-b-192". They are both used with WPA3-Enterprise. PMF is mandatory when using "eap-suite-b-192" so it's implicitly set to "required". --- doc/netplan-yaml.md | 14 +- src/abi.h | 10 ++ src/names.c | 10 ++ src/names.h | 3 + src/netplan.c | 1 + src/networkd.c | 28 +++- src/nm.c | 22 +++ src/parse-nm.c | 20 +++ src/parse.c | 31 +++- src/types.c | 1 + tests/generator/test_wifis.py | 260 +++++++++++++++++++++++++++++++++- tests/parser/test_keyfile.py | 200 ++++++++++++++++++++++++++ 12 files changed, 589 insertions(+), 11 deletions(-) diff --git a/doc/netplan-yaml.md b/doc/netplan-yaml.md index 11e7c4142..870606f13 100644 --- a/doc/netplan-yaml.md +++ b/doc/netplan-yaml.md @@ -850,8 +850,9 @@ interfaces, as well as individual wifi networks, by means of the `auth` block. > The supported key management modes are `none` (no key management); > `psk` (WPA with pre-shared key, common for home wifi); `eap` (WPA - > with EAP, common for enterprise wifi); `sae` (used by WPA3); and `802.1x` - > (used primarily for wired Ethernet connections). + > with EAP, common for enterprise wifi); `eap-sha256` (used with WPA3-Enterprise); + > `eap-suite-b-192` (used with WPA3-Enterprise); `sae` (used by WPA3); + > and `802.1x` (used primarily for wired Ethernet connections). - **password** (scalar) @@ -863,7 +864,14 @@ interfaces, as well as individual wifi networks, by means of the `auth` block. - **method** (scalar) > The EAP method to use. The supported EAP methods are `tls` (TLS), - > `peap` (Protected EAP), and `ttls` (Tunneled TLS). + > `peap` (Protected EAP), and `ttls` (Tunneled TLS). + + - **pmf** (scalar) + + > Protected Management Frames (802.11w) mode. Valid values are `optional` and `required`. + > PMF is required by WPA3 and will be set to `required` by default when + > `key-management` is set to `sae` or `eap-suite-b-192`. WPA3-Enterprise with + > `eap-sha256` also requires PMF and must be enabled manually. - **identity** (scalar) diff --git a/src/abi.h b/src/abi.h index 81905a202..1992fab0e 100644 --- a/src/abi.h +++ b/src/abi.h @@ -106,6 +106,8 @@ typedef enum { NETPLAN_AUTH_KEY_MANAGEMENT_NONE, NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK, NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP, + NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSHA256, + NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSUITE_B_192, NETPLAN_AUTH_KEY_MANAGEMENT_8021X, NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE, NETPLAN_AUTH_KEY_MANAGEMENT_MAX, @@ -119,9 +121,17 @@ typedef enum { NETPLAN_AUTH_EAP_METHOD_MAX, } NetplanAuthEAPMethod; +typedef enum { + NETPLAN_AUTH_PMF_MODE_DISABLED, + NETPLAN_AUTH_PMF_MODE_OPTIONAL, + NETPLAN_AUTH_PMF_MODE_REQUIRED, + NETPLAN_AUTH_PMF_MODE_MAX, +} NetplanAuthPMFMode; + typedef struct authentication_settings { NetplanAuthKeyManagementType key_management; NetplanAuthEAPMethod eap_method; + NetplanAuthPMFMode pmf_mode; char* identity; char* anonymous_identity; char* password; diff --git a/src/names.c b/src/names.c index f9a30f9d3..060c9aace 100644 --- a/src/names.c +++ b/src/names.c @@ -60,6 +60,8 @@ netplan_auth_key_management_type_to_str[NETPLAN_AUTH_KEY_MANAGEMENT_MAX] = { [NETPLAN_AUTH_KEY_MANAGEMENT_NONE] = "none", [NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK] = "psk", [NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP] = "eap", + [NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSHA256] = "eap-sha256", + [NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSUITE_B_192] = "eap-suite-b-192", [NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE] = "sae", [NETPLAN_AUTH_KEY_MANAGEMENT_8021X] = "802.1x", }; @@ -72,6 +74,13 @@ netplan_auth_eap_method_to_str[NETPLAN_AUTH_EAP_METHOD_MAX] = { [NETPLAN_AUTH_EAP_TTLS] = "ttls", }; +static const char* const +netplan_auth_pmf_mode_to_str[NETPLAN_AUTH_PMF_MODE_MAX] = { + [NETPLAN_AUTH_PMF_MODE_DISABLED] = NULL, + [NETPLAN_AUTH_PMF_MODE_OPTIONAL] = "optional", + [NETPLAN_AUTH_PMF_MODE_REQUIRED] = "required", +}; + static const char* const netplan_tunnel_mode_to_str[NETPLAN_TUNNEL_MODE_MAX_] = { [NETPLAN_TUNNEL_MODE_UNKNOWN] = NULL, @@ -129,6 +138,7 @@ NAME_FUNCTION(backend, NetplanBackend); NAME_FUNCTION(def_type, NetplanDefType); NAME_FUNCTION(auth_key_management_type, NetplanAuthKeyManagementType); NAME_FUNCTION(auth_eap_method, NetplanAuthEAPMethod); +NAME_FUNCTION(auth_pmf_mode, NetplanAuthPMFMode); NAME_FUNCTION(tunnel_mode, NetplanTunnelMode); NAME_FUNCTION(addr_gen_mode, NetplanAddrGenMode); NAME_FUNCTION(wifi_mode, NetplanWifiMode); diff --git a/src/names.h b/src/names.h index 0e1e4d15a..5d34d4e71 100644 --- a/src/names.h +++ b/src/names.h @@ -32,6 +32,9 @@ netplan_auth_key_management_type_name(NetplanAuthKeyManagementType val); const char* netplan_auth_eap_method_name(NetplanAuthEAPMethod val); +const char* +netplan_auth_pmf_mode_name(NetplanAuthPMFMode val); + const char* netplan_tunnel_mode_name(NetplanTunnelMode val); diff --git a/src/netplan.c b/src/netplan.c index 81bddb534..4663dc3d3 100644 --- a/src/netplan.c +++ b/src/netplan.c @@ -128,6 +128,7 @@ write_auth(yaml_event_t* event, yaml_emitter_t* emitter, NetplanAuthenticationSe YAML_MAPPING_OPEN(event, emitter); YAML_NONNULL_STRING(event, emitter, "key-management", netplan_auth_key_management_type_name(auth.key_management)); YAML_NONNULL_STRING(event, emitter, "method", netplan_auth_eap_method_name(auth.eap_method)); + YAML_NONNULL_STRING(event, emitter, "pmf", netplan_auth_pmf_mode_name(auth.pmf_mode)); YAML_NONNULL_STRING(event, emitter, "anonymous-identity", auth.anonymous_identity); YAML_NONNULL_STRING(event, emitter, "identity", auth.identity); YAML_NONNULL_STRING(event, emitter, "ca-certificate", auth.ca_certificate); diff --git a/src/networkd.c b/src/networkd.c index 602b472bb..2eaf8a6af 100644 --- a/src/networkd.c +++ b/src/networkd.c @@ -1049,13 +1049,16 @@ append_wpa_auth_conf(GString* s, const NetplanAuthenticationSettings* auth, cons g_string_append(s, " key_mgmt=WPA-EAP\n"); break; + case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSHA256: + g_string_append(s, " key_mgmt=WPA-EAP-SHA256\n"); + break; + + case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSUITE_B_192: + g_string_append(s, " key_mgmt=WPA-EAP-SUITE-B-192\n"); + break; + case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE: - /* - * SAE is used by WPA3 and Management Frame Protection - * (ieee80211w) is mandatory. - */ g_string_append(s, " key_mgmt=SAE\n"); - g_string_append(s, " ieee80211w=2\n"); break; case NETPLAN_AUTH_KEY_MANAGEMENT_8021X: @@ -1084,6 +1087,21 @@ append_wpa_auth_conf(GString* s, const NetplanAuthenticationSettings* auth, cons default: break; // LCOV_EXCL_LINE } + switch (auth->pmf_mode) { + case NETPLAN_AUTH_PMF_MODE_DISABLED: + break; + + case NETPLAN_AUTH_PMF_MODE_OPTIONAL: + g_string_append(s, " ieee80211w=1\n"); + break; + + case NETPLAN_AUTH_PMF_MODE_REQUIRED: + g_string_append(s, " ieee80211w=2\n"); + break; + + default: break; // LCOV_EXCL_LINE + } + if (auth->identity) { g_string_append_printf(s, " identity=\"%s\"\n", auth->identity); } diff --git a/src/nm.c b/src/nm.c index a8ae1a63f..20951e764 100644 --- a/src/nm.c +++ b/src/nm.c @@ -449,6 +449,13 @@ write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile * case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP: g_key_file_set_string(kf, "wifi-security", "key-mgmt", "wpa-eap"); break; + case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSHA256: + /* NM uses "wpa-eap" to enable both EAP and EAP-SHA256 */ + g_key_file_set_string(kf, "wifi-security", "key-mgmt", "wpa-eap"); + break; + case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSUITE_B_192: + g_key_file_set_string(kf, "wifi-security", "key-mgmt", "wpa-eap-suite-b-192"); + break; case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE: g_key_file_set_string(kf, "wifi-security", "key-mgmt", "sae"); break; @@ -458,6 +465,21 @@ write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile * default: break; // LCOV_EXCL_LINE } + switch (auth->pmf_mode) { + case NETPLAN_AUTH_PMF_MODE_DISABLED: + break; + + case NETPLAN_AUTH_PMF_MODE_OPTIONAL: + g_key_file_set_string(kf, "wifi-security", "pmf", "2"); + break; + + case NETPLAN_AUTH_PMF_MODE_REQUIRED: + g_key_file_set_string(kf, "wifi-security", "pmf", "3"); + break; + + default: break; // LCOV_EXCL_LINE + } + if (auth->eap_method != NETPLAN_AUTH_EAP_NONE) write_dot1x_auth_parameters(auth, kf); else if (auth->password) diff --git a/src/parse-nm.c b/src/parse-nm.c index a97a4665b..9cc72b3d8 100644 --- a/src/parse-nm.c +++ b/src/parse-nm.c @@ -609,6 +609,7 @@ netplan_parser_load_keyfile(NetplanParser* npp, const char* filename, GError** e g_autofree gchar* netdef_id = NULL; ssize_t netdef_id_size = 0; gchar *tmp_str = NULL; + gint pmf = 0; NetplanNetDefinition* nd = NULL; NetplanWifiAccessPoint* ap = NULL; g_autoptr(GKeyFile) kf = g_key_file_new(); @@ -970,6 +971,10 @@ netplan_parser_load_keyfile(NetplanParser* npp, const char* filename, GError** e ap->auth.key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP; ap->has_auth = TRUE; _kf_clear_key(kf, "wifi-security", "key-mgmt"); + } else if (tmp_str && g_strcmp0(tmp_str, "wpa-eap-suite-b-192") == 0) { + ap->auth.key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSUITE_B_192; + ap->has_auth = TRUE; + _kf_clear_key(kf, "wifi-security", "key-mgmt"); } else if (tmp_str && g_strcmp0(tmp_str, "sae") == 0) { ap->auth.key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE; ap->has_auth = TRUE; @@ -981,6 +986,21 @@ netplan_parser_load_keyfile(NetplanParser* npp, const char* filename, GError** e } g_free(tmp_str); + pmf = g_key_file_get_integer(kf, "wifi-security", "pmf", NULL); + switch (pmf) { + case 2: + ap->auth.pmf_mode = NETPLAN_AUTH_PMF_MODE_OPTIONAL; + _kf_clear_key(kf, "wifi-security", "pmf"); + break; + + case 3: + ap->auth.pmf_mode = NETPLAN_AUTH_PMF_MODE_REQUIRED; + _kf_clear_key(kf, "wifi-security", "pmf"); + break; + + default: break; + } + keyfile_handle_generic_str(kf, "wifi-security", "psk", &ap->auth.password); if (ap->auth.password) ap->has_auth = TRUE; diff --git a/src/parse.c b/src/parse.c index a1730e51f..8b06fd3a8 100644 --- a/src/parse.c +++ b/src/parse.c @@ -915,8 +915,22 @@ handle_auth_key_management(NetplanParser* npp, yaml_node_t* node, __unused const auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK; else if (strcmp(scalar(node), "eap") == 0) auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP; - else if (strcmp(scalar(node), "sae") == 0) + else if (strcmp(scalar(node), "eap-sha256") == 0) + auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSHA256; + else if (strcmp(scalar(node), "eap-suite-b-192") == 0) { + /* Settings for WPA3-Enterprise for sensitive enterprise environments. + * Management Frame Protection (ieee80211w) is mandatory. + */ + auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAPSUITE_B_192; + auth->pmf_mode = NETPLAN_AUTH_PMF_MODE_REQUIRED; + } + else if (strcmp(scalar(node), "sae") == 0) { + /* SAE is used by WPA3 and Management Frame Protection + * (ieee80211w) is mandatory. + */ auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE; + auth->pmf_mode = NETPLAN_AUTH_PMF_MODE_REQUIRED; + } else if (strcmp(scalar(node), "802.1x") == 0) auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_8021X; else @@ -940,9 +954,24 @@ handle_auth_method(NetplanParser* npp, yaml_node_t* node, __unused const void* _ return TRUE; } +static gboolean +handle_pmf_mode(NetplanParser* npp, yaml_node_t* node, __unused const void* _, GError** error) +{ + NetplanAuthenticationSettings* auth = npp->current.auth; + g_assert(auth); + if (strcmp(scalar(node), "optional") == 0) + auth->pmf_mode = NETPLAN_AUTH_PMF_MODE_OPTIONAL; + else if (strcmp(scalar(node), "required") == 0) + auth->pmf_mode = NETPLAN_AUTH_PMF_MODE_REQUIRED; + else + return yaml_error(npp, node, error, "unknown PMF mode '%s'. Valid entries are \"optional\" and \"disabled\"", scalar(node)); + return TRUE; +} + static const mapping_entry_handler auth_handlers[] = { {"key-management", YAML_SCALAR_NODE, {.generic=handle_auth_key_management}, NULL}, {"method", YAML_SCALAR_NODE, {.generic=handle_auth_method}, NULL}, + {"pmf", YAML_SCALAR_NODE, {.generic=handle_pmf_mode}, NULL}, {"identity", YAML_SCALAR_NODE, {.generic=handle_auth_str}, auth_offset(identity)}, {"anonymous-identity", YAML_SCALAR_NODE, {.generic=handle_auth_str}, auth_offset(anonymous_identity)}, {"password", YAML_SCALAR_NODE, {.generic=handle_auth_str}, auth_offset(password)}, diff --git a/src/types.c b/src/types.c index 744bbaca9..922063697 100644 --- a/src/types.c +++ b/src/types.c @@ -114,6 +114,7 @@ reset_auth_settings(NetplanAuthenticationSettings* auth) FREE_AND_NULLIFY(auth->phase2_auth); auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_NONE; auth->eap_method = NETPLAN_AUTH_EAP_NONE; + auth->pmf_mode = NETPLAN_AUTH_PMF_MODE_DISABLED; } void diff --git a/tests/generator/test_wifis.py b/tests/generator/test_wifis.py index 5840119b9..3c2236cca 100644 --- a/tests/generator/test_wifis.py +++ b/tests/generator/test_wifis.py @@ -342,7 +342,7 @@ def test_wifi_wowlan_default(self): self.assertTrue(os.path.islink(os.path.join( self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service'))) - def test_wifi_wpa3(self): + def test_wifi_wpa3_personal(self): self.generate('''network: version: 2 wifis: @@ -351,6 +351,7 @@ def test_wifi_wpa3(self): homenet: auth: key-management: sae + pmf: required password: "********"''') self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant @@ -363,6 +364,96 @@ def test_wifi_wpa3(self): } """) + def test_wifi_wpa3_personal_pmf_optional(self): + self.generate('''network: + version: 2 + wifis: + wl0: + access-points: + homenet: + auth: + key-management: sae + pmf: optional + password: "********"''') + + self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant + +network={ + ssid="homenet" + key_mgmt=SAE + ieee80211w=1 + psk="********" +} +""") + + def test_wifi_wpa3_enterprise_eap_sha256(self): + self.generate('''network: + version: 2 + wifis: + wl0: + access-points: + homenet: + auth: + key-management: eap-sha256 + pmf: required + method: tls + anonymous-identity: "@cust.example.com" + identity: "cert-joe@cust.example.com" + ca-certificate: /etc/ssl/cust-cacrt.pem + client-certificate: /etc/ssl/cust-crt.pem + client-key: /etc/ssl/cust-key.pem + client-key-password: "**********"''') + + self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant + +network={ + ssid="homenet" + key_mgmt=WPA-EAP-SHA256 + eap=TLS + ieee80211w=2 + identity="cert-joe@cust.example.com" + anonymous_identity="@cust.example.com" + ca_cert="/etc/ssl/cust-cacrt.pem" + client_cert="/etc/ssl/cust-crt.pem" + private_key="/etc/ssl/cust-key.pem" + private_key_passwd="**********" +} +""") + + def test_wifi_wpa3_enterprise_eap_suite_b_192(self): + self.generate('''network: + version: 2 + wifis: + wl0: + access-points: + homenet: + auth: + key-management: eap-suite-b-192 + pmf: required + method: tls + anonymous-identity: "@cust.example.com" + identity: "cert-joe@cust.example.com" + ca-certificate: /etc/ssl/cust-cacrt.pem + client-certificate: /etc/ssl/cust-crt.pem + client-key: /etc/ssl/cust-key.pem + client-key-password: "**********"''') + + self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant + +network={ + ssid="homenet" + key_mgmt=WPA-EAP-SUITE-B-192 + eap=TLS + ieee80211w=2 + identity="cert-joe@cust.example.com" + anonymous_identity="@cust.example.com" + ca_cert="/etc/ssl/cust-cacrt.pem" + client_cert="/etc/ssl/cust-crt.pem" + private_key="/etc/ssl/cust-key.pem" + private_key_passwd="**********" +} +""") + class TestNetworkManager(TestBase): @@ -671,7 +762,7 @@ def test_wifi_adhoc_wpa_5ghz(self): } """) - def test_wifi_wpa3(self): + def test_wifi_wpa3_personal(self): self.generate('''network: version: 2 renderer: NetworkManager @@ -681,6 +772,7 @@ def test_wifi_wpa3(self): homenet: auth: key-management: sae + pmf: required password: "********"''') self.assert_nm({'wl0-homenet': '''[connection] @@ -700,7 +792,171 @@ def test_wifi_wpa3(self): [wifi-security] key-mgmt=sae +pmf=3 psk=******** +'''}) + + def test_wifi_wpa_invalid_pmf_mode(self): + out = self.generate('''network: + version: 2 + renderer: NetworkManager + wifis: + wl0: + access-points: + homenet: + auth: + key-management: eap-sha256 + method: tls + pmf: invalid + anonymous-identity: "@cust.example.com" + identity: "cert-joe@cust.example.com" + ca-certificate: /etc/ssl/cust-cacrt.pem + client-certificate: /etc/ssl/cust-crt.pem + client-key: /etc/ssl/cust-key.pem + client-key-password: "**********"''', expect_fail=True) + + self.assertIn("Error in network definition: unknown PMF mode 'invalid'", out) + + def test_wifi_wpa3_enterprise_eap_sha256(self): + self.generate('''network: + version: 2 + renderer: NetworkManager + wifis: + wl0: + access-points: + homenet: + auth: + key-management: eap-sha256 + method: tls + anonymous-identity: "@cust.example.com" + identity: "cert-joe@cust.example.com" + ca-certificate: /etc/ssl/cust-cacrt.pem + client-certificate: /etc/ssl/cust-crt.pem + client-key: /etc/ssl/cust-key.pem + client-key-password: "**********"''') + + self.assert_nm({'wl0-homenet': '''[connection] +id=netplan-wl0-homenet +type=wifi +interface-name=wl0 + +[ipv4] +method=link-local + +[ipv6] +method=ignore + +[wifi] +ssid=homenet +mode=infrastructure + +[wifi-security] +key-mgmt=wpa-eap + +[802-1x] +eap=tls +identity=cert-joe@cust.example.com +anonymous-identity=@cust.example.com +ca-cert=/etc/ssl/cust-cacrt.pem +client-cert=/etc/ssl/cust-crt.pem +private-key=/etc/ssl/cust-key.pem +private-key-password=********** +'''}) + + def test_wifi_wpa3_enterprise_eap_sha256_pmf_optional(self): + self.generate('''network: + version: 2 + renderer: NetworkManager + wifis: + wl0: + access-points: + homenet: + auth: + key-management: eap-sha256 + method: tls + pmf: optional + anonymous-identity: "@cust.example.com" + identity: "cert-joe@cust.example.com" + ca-certificate: /etc/ssl/cust-cacrt.pem + client-certificate: /etc/ssl/cust-crt.pem + client-key: /etc/ssl/cust-key.pem + client-key-password: "**********"''') + + self.assert_nm({'wl0-homenet': '''[connection] +id=netplan-wl0-homenet +type=wifi +interface-name=wl0 + +[ipv4] +method=link-local + +[ipv6] +method=ignore + +[wifi] +ssid=homenet +mode=infrastructure + +[wifi-security] +key-mgmt=wpa-eap +pmf=2 + +[802-1x] +eap=tls +identity=cert-joe@cust.example.com +anonymous-identity=@cust.example.com +ca-cert=/etc/ssl/cust-cacrt.pem +client-cert=/etc/ssl/cust-crt.pem +private-key=/etc/ssl/cust-key.pem +private-key-password=********** +'''}) + + def test_wifi_wpa3_enterprise_eap_suite_b_192(self): + self.generate('''network: + version: 2 + renderer: NetworkManager + wifis: + wl0: + access-points: + homenet: + auth: + key-management: eap-suite-b-192 + pmf: required + method: tls + anonymous-identity: "@cust.example.com" + identity: "cert-joe@cust.example.com" + ca-certificate: /etc/ssl/cust-cacrt.pem + client-certificate: /etc/ssl/cust-crt.pem + client-key: /etc/ssl/cust-key.pem + client-key-password: "**********"''') + + self.assert_nm({'wl0-homenet': '''[connection] +id=netplan-wl0-homenet +type=wifi +interface-name=wl0 + +[ipv4] +method=link-local + +[ipv6] +method=ignore + +[wifi] +ssid=homenet +mode=infrastructure + +[wifi-security] +key-mgmt=wpa-eap-suite-b-192 +pmf=3 + +[802-1x] +eap=tls +identity=cert-joe@cust.example.com +anonymous-identity=@cust.example.com +ca-cert=/etc/ssl/cust-cacrt.pem +client-cert=/etc/ssl/cust-crt.pem +private-key=/etc/ssl/cust-key.pem +private-key-password=********** '''}) def test_wifi_wowlan(self): diff --git a/tests/parser/test_keyfile.py b/tests/parser/test_keyfile.py index 7d15412dd..9bd637ccf 100644 --- a/tests/parser/test_keyfile.py +++ b/tests/parser/test_keyfile.py @@ -1259,6 +1259,7 @@ def test_keyfile_wpa3_sae(self): [wifi-security] key-mgmt=sae psk=test1234 +pmf=3 [ipv4] method=auto @@ -1283,6 +1284,7 @@ def test_keyfile_wpa3_sae(self): "ubuntu-wpa2-wpa3-mixed": auth: key-management: "sae" + pmf: "required" password: "test1234" networkmanager: uuid: "ff9d6ebc-226d-4f82-a485-b7ff83b9607f" @@ -1295,6 +1297,204 @@ def test_keyfile_wpa3_sae(self): name: "test2" '''.format(UUID, UUID)}) + def test_keyfile_wpa3_enterprise_eap_sha256(self): + self.generate_from_keyfile('''[connection] +id=test2 +uuid={} +type=wifi +interface-name=wlan0 + +[wifi] +mode=infrastructure +ssid=enterprisenet + +[wifi-security] +key-mgmt=wpa-eap +pmf=3 + +[802-1x] +eap=tls +identity=cert-joe@cust.example.com +anonymous-identity=@cust.example.com +ca-cert=/etc/ssl/cust-cacrt.pem +client-cert=/etc/ssl/cust-crt.pem +private-key=/etc/ssl/cust-key.pem +private-key-password=********** + +[ipv4] +method=auto + +[ipv6] +addr-gen-mode=stable-privacy +method=auto + +[proxy] +'''.format(UUID)) + self.assert_netplan({UUID: '''network: + version: 2 + wifis: + NM-{}: + renderer: NetworkManager + match: + name: "wlan0" + dhcp4: true + dhcp6: true + ipv6-address-generation: "stable-privacy" + access-points: + "enterprisenet": + auth: + key-management: "eap" + method: "tls" + pmf: "required" + anonymous-identity: "@cust.example.com" + identity: "cert-joe@cust.example.com" + ca-certificate: "/etc/ssl/cust-cacrt.pem" + client-certificate: "/etc/ssl/cust-crt.pem" + client-key: "/etc/ssl/cust-key.pem" + client-key-password: "**********" + networkmanager: + uuid: "ff9d6ebc-226d-4f82-a485-b7ff83b9607f" + name: "test2" + passthrough: + ipv6.ip6-privacy: "-1" + proxy._: "" + networkmanager: + uuid: "{}" + name: "test2" +'''.format(UUID, UUID)}) + + def test_keyfile_wpa3_enterprise_eap_sha256_pmf_optional(self): + self.generate_from_keyfile('''[connection] +id=test2 +uuid={} +type=wifi +interface-name=wlan0 + +[wifi] +mode=infrastructure +ssid=enterprisenet + +[wifi-security] +key-mgmt=wpa-eap +pmf=2 + +[802-1x] +eap=tls +identity=cert-joe@cust.example.com +anonymous-identity=@cust.example.com +ca-cert=/etc/ssl/cust-cacrt.pem +client-cert=/etc/ssl/cust-crt.pem +private-key=/etc/ssl/cust-key.pem +private-key-password=********** + +[ipv4] +method=auto + +[ipv6] +addr-gen-mode=stable-privacy +method=auto + +[proxy] +'''.format(UUID)) + self.assert_netplan({UUID: '''network: + version: 2 + wifis: + NM-{}: + renderer: NetworkManager + match: + name: "wlan0" + dhcp4: true + dhcp6: true + ipv6-address-generation: "stable-privacy" + access-points: + "enterprisenet": + auth: + key-management: "eap" + method: "tls" + pmf: "optional" + anonymous-identity: "@cust.example.com" + identity: "cert-joe@cust.example.com" + ca-certificate: "/etc/ssl/cust-cacrt.pem" + client-certificate: "/etc/ssl/cust-crt.pem" + client-key: "/etc/ssl/cust-key.pem" + client-key-password: "**********" + networkmanager: + uuid: "ff9d6ebc-226d-4f82-a485-b7ff83b9607f" + name: "test2" + passthrough: + ipv6.ip6-privacy: "-1" + proxy._: "" + networkmanager: + uuid: "{}" + name: "test2" +'''.format(UUID, UUID)}) + + def test_keyfile_wpa3_enterprise_eap_suite_b_192(self): + self.generate_from_keyfile('''[connection] +id=test2 +uuid={} +type=wifi +interface-name=wlan0 + +[wifi] +mode=infrastructure +ssid=enterprisenet + +[wifi-security] +key-mgmt=wpa-eap-suite-b-192 +pmf=3 + +[802-1x] +eap=tls +identity=cert-joe@cust.example.com +anonymous-identity=@cust.example.com +ca-cert=/etc/ssl/cust-cacrt.pem +client-cert=/etc/ssl/cust-crt.pem +private-key=/etc/ssl/cust-key.pem +private-key-password=********** + +[ipv4] +method=auto + +[ipv6] +addr-gen-mode=stable-privacy +method=auto + +[proxy] +'''.format(UUID)) + self.assert_netplan({UUID: '''network: + version: 2 + wifis: + NM-{}: + renderer: NetworkManager + match: + name: "wlan0" + dhcp4: true + dhcp6: true + ipv6-address-generation: "stable-privacy" + access-points: + "enterprisenet": + auth: + key-management: "eap-suite-b-192" + method: "tls" + pmf: "required" + anonymous-identity: "@cust.example.com" + identity: "cert-joe@cust.example.com" + ca-certificate: "/etc/ssl/cust-cacrt.pem" + client-certificate: "/etc/ssl/cust-crt.pem" + client-key: "/etc/ssl/cust-key.pem" + client-key-password: "**********" + networkmanager: + uuid: "ff9d6ebc-226d-4f82-a485-b7ff83b9607f" + name: "test2" + passthrough: + ipv6.ip6-privacy: "-1" + proxy._: "" + networkmanager: + uuid: "{}" + name: "test2" +'''.format(UUID, UUID)}) + def test_keyfile_dns_search_ip4_ip6_conflict(self): self.generate_from_keyfile('''[connection] id=Work Wired