Skip to content

Commit

Permalink
wifi: make it possible to have a psk and an eap password simultaneously
Browse files Browse the repository at this point in the history
Originally, Netplan assumes the PSK and 802-1x identity password will
not be used simultaneously.

With this change, when both passwords are used, the PSK is expected to
be placed in "ssid".password and the identity in "ssid".auth.password.
"ssid".auth.password can also be used by the PSK if it's the only
password.

When 802-1x is used, the PMF setting will not be emitted if the PSK is
also present. NM will error out if both settings are used
simultaneously.
  • Loading branch information
daniloegea committed Oct 16, 2023
1 parent 4a5a9ed commit b4acb08
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 47 deletions.
2 changes: 2 additions & 0 deletions src/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ typedef enum {
NETPLAN_AUTH_EAP_TTLS,
NETPLAN_AUTH_EAP_LEAP,
NETPLAN_AUTH_EAP_PWD,
NETPLAN_AUTH_EAP_UNKNOWN,
NETPLAN_AUTH_EAP_METHOD_MAX,
} NetplanAuthEAPMethod;

Expand All @@ -142,6 +143,7 @@ typedef struct authentication_settings {
char* client_key;
char* client_key_password;
char* phase2_auth; /* netplan-feature: auth-phase2 */
char* psk;
} NetplanAuthenticationSettings;

typedef enum {
Expand Down
2 changes: 2 additions & 0 deletions src/netplan.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ write_access_points(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanN
}

YAML_UINT_0(def, event, emitter, "channel", ap->channel);
if (ap->auth.psk)
YAML_NONNULL_STRING(event, emitter, "password", ap->auth.psk);
if (ap->has_auth || DIRTY(def, ap->auth))
write_auth(event, emitter, ap->auth);
if (ap->mode != NETPLAN_WIFI_MODE_INFRASTRUCTURE || DIRTY(def, ap->mode))
Expand Down
63 changes: 38 additions & 25 deletions src/networkd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1122,33 +1122,46 @@ append_wpa_auth_conf(GString* s, const NetplanAuthenticationSettings* auth, cons
if (auth->anonymous_identity) {
g_string_append_printf(s, " anonymous_identity=\"%s\"\n", auth->anonymous_identity);
}
if (auth->password) {
if (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK ||
auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE) {
size_t len = strlen(auth->password);
if (len == 64) {
/* must be a hex-digit key representation */
for (unsigned i = 0; i < 64; ++i)
if (!isxdigit(auth->password[i])) {
g_set_error(error, NETPLAN_BACKEND_ERROR, NETPLAN_ERROR_UNSUPPORTED, "ERROR: %s: PSK length of 64 is only supported for hex-digit representation\n", id);
return FALSE;
}
/* this is required to be unquoted */
g_string_append_printf(s, " psk=%s\n", auth->password);
} else if (len < 8 || len > 63) {
/* per wpa_supplicant spec, passphrase needs to be between 8

char* psk = NULL;
if (auth->psk)
psk = auth->psk;
else if (auth->password
&& (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK
|| auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE)
)
psk = auth->password;

if (psk) {
size_t len = strlen(psk);
if (len == 64) {
/* must be a hex-digit key representation */
for (unsigned i = 0; i < 64; ++i)
if (!isxdigit(psk[i])) {
g_set_error(error, NETPLAN_BACKEND_ERROR, NETPLAN_ERROR_UNSUPPORTED, "ERROR: %s: PSK length of 64 is only supported for hex-digit representation\n", id);
return FALSE;
}
/* this is required to be unquoted */
g_string_append_printf(s, " psk=%s\n", psk);
} else if (len < 8 || len > 63) {
/* per wpa_supplicant spec, passphrase needs to be between 8
and 63 characters */
g_set_error(error, NETPLAN_BACKEND_ERROR, NETPLAN_ERROR_VALIDATION, "ERROR: %s: ASCII passphrase must be between 8 and 63 characters (inclusive)\n", id);
return FALSE;
} else {
g_string_append_printf(s, " psk=\"%s\"\n", auth->password);
}
g_set_error(error, NETPLAN_BACKEND_ERROR, NETPLAN_ERROR_VALIDATION, "ERROR: %s: ASCII passphrase must be between 8 and 63 characters (inclusive)\n", id);
return FALSE;
} else {
if (strncmp(auth->password, "hash:", 5) == 0) {
g_string_append_printf(s, " password=%s\n", auth->password);
} else {
g_string_append_printf(s, " password=\"%s\"\n", auth->password);
}
g_string_append_printf(s, " psk=\"%s\"\n", psk);
}
}

if (auth->password
&& (( auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK
&& auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE
)
|| auth->eap_method != NETPLAN_AUTH_EAP_NONE)) {
if (strncmp(auth->password, "hash:", 5) == 0) {
g_string_append_printf(s, " password=%s\n", auth->password);
} else {
g_string_append_printf(s, " password=\"%s\"\n", auth->password);
}
}
if (auth->ca_certificate) {
Expand Down
39 changes: 25 additions & 14 deletions src/nm.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,12 @@ write_dot1x_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile
g_key_file_set_string(kf, "802-1x", "identity", auth->identity);
if (auth->anonymous_identity)
g_key_file_set_string(kf, "802-1x", "anonymous-identity", auth->anonymous_identity);
if (auth->password && auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK)
if (auth->password
&& (( auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK
&& auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE
)
|| auth->eap_method != NETPLAN_AUTH_EAP_NONE)
)
g_key_file_set_string(kf, "802-1x", "password", auth->password);
if (auth->ca_certificate)
g_key_file_set_string(kf, "802-1x", "ca-cert", auth->ca_certificate);
Expand Down Expand Up @@ -470,23 +475,29 @@ write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *
default: break; // LCOV_EXCL_LINE
}

switch (auth->pmf_mode) {
case NETPLAN_AUTH_PMF_MODE_NONE:
case NETPLAN_AUTH_PMF_MODE_DISABLED:
break;
if (auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_8021X) {
switch (auth->pmf_mode) {
case NETPLAN_AUTH_PMF_MODE_NONE:
case NETPLAN_AUTH_PMF_MODE_DISABLED:
break;

case NETPLAN_AUTH_PMF_MODE_OPTIONAL:
g_key_file_set_integer(kf, "wifi-security", "pmf", 2);
break;
case NETPLAN_AUTH_PMF_MODE_OPTIONAL:
g_key_file_set_integer(kf, "wifi-security", "pmf", 2);
break;

case NETPLAN_AUTH_PMF_MODE_REQUIRED:
g_key_file_set_integer(kf, "wifi-security", "pmf", 3);
break;
case NETPLAN_AUTH_PMF_MODE_REQUIRED:
g_key_file_set_integer(kf, "wifi-security", "pmf", 3);
break;
}
}

if (auth->eap_method != NETPLAN_AUTH_EAP_NONE)
write_dot1x_auth_parameters(auth, kf);
else if (auth->password)
write_dot1x_auth_parameters(auth, kf);

if (auth->psk)
g_key_file_set_string(kf, "wifi-security", "psk", auth->psk);
else if (auth->password
&& (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK
|| auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE))
g_key_file_set_string(kf, "wifi-security", "psk", auth->password);
}

Expand Down
23 changes: 18 additions & 5 deletions src/parse-nm.c
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ parse_dot1x_auth(GKeyFile* kf, NetplanAuthenticationSettings* auth)
auth->eap_method = NETPLAN_AUTH_EAP_LEAP;
} else if (g_strcmp0(first_method, "pwd") == 0) {
auth->eap_method = NETPLAN_AUTH_EAP_PWD;
} else {
auth->eap_method = NETPLAN_AUTH_EAP_UNKNOWN;
}

/* If "method" (which is a list separated by ";") has more than one value,
Expand All @@ -398,16 +400,20 @@ parse_dot1x_auth(GKeyFile* kf, NetplanAuthenticationSettings* auth)
* but Netplan accepts only one.
*
* TODO: eap_method needs to be fixed to store multiple methods.
*
* If at this point the eap_method is still NONE we also keep the property because
* it's probably a setting we still don't support.
*/
if (split[1] == NULL || !g_strcmp0(split[1], ""))
if (auth->eap_method != NETPLAN_AUTH_EAP_UNKNOWN && (split[1] == NULL || !g_strcmp0(split[1], "")))
_kf_clear_key(kf, "802-1x", "eap");

g_strfreev(split);
}

keyfile_handle_generic_str(kf, "802-1x", "identity", &auth->identity);
keyfile_handle_generic_str(kf, "802-1x", "anonymous-identity", &auth->anonymous_identity);
if (!auth->password)
if (auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK &&
auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE)
keyfile_handle_generic_str(kf, "802-1x", "password", &auth->password);
keyfile_handle_generic_str(kf, "802-1x", "ca-cert", &auth->ca_certificate);
keyfile_handle_generic_str(kf, "802-1x", "client-cert", &auth->client_certificate);
Expand Down Expand Up @@ -1014,9 +1020,16 @@ netplan_parser_load_keyfile(NetplanParser* npp, const char* filename, GError** e
default: break;
}

keyfile_handle_generic_str(kf, "wifi-security", "psk", &ap->auth.password);
if (ap->auth.password)
ap->has_auth = TRUE;
if (ap->auth.key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK
|| ap->auth.key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE) {
keyfile_handle_generic_str(kf, "wifi-security", "psk", &ap->auth.password);
if (ap->auth.password)
ap->has_auth = TRUE;
} else {
keyfile_handle_generic_str(kf, "wifi-security", "psk", &ap->auth.psk);
if (ap->auth.psk)
ap->has_auth = TRUE;
}

parse_dot1x_auth(kf, &ap->auth);
if (ap->auth.eap_method != NETPLAN_AUTH_EAP_NONE)
Expand Down
8 changes: 5 additions & 3 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -1042,10 +1042,12 @@ handle_access_point_password(NetplanParser* npp, yaml_node_t* node, __unused con
g_assert(access_point);
/* shortcut for WPA-PSK */
access_point->has_auth = TRUE;
access_point->auth.key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK;
if (access_point->auth.key_management == NETPLAN_AUTH_KEY_MANAGEMENT_NONE)
access_point->auth.key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK;

access_point->auth.pmf_mode = NETPLAN_AUTH_PMF_MODE_OPTIONAL;
g_free(access_point->auth.password);
access_point->auth.password = g_strdup(scalar(node));
g_free(access_point->auth.psk);
access_point->auth.psk = g_strdup(scalar(node));
return TRUE;
}

Expand Down
1 change: 1 addition & 0 deletions src/types.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ reset_auth_settings(NetplanAuthenticationSettings* auth)
FREE_AND_NULLIFY(auth->identity);
FREE_AND_NULLIFY(auth->anonymous_identity);
FREE_AND_NULLIFY(auth->password);
FREE_AND_NULLIFY(auth->psk);
FREE_AND_NULLIFY(auth->ca_certificate);
FREE_AND_NULLIFY(auth->client_certificate);
FREE_AND_NULLIFY(auth->client_key);
Expand Down
68 changes: 68 additions & 0 deletions tests/generator/test_wifis.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,33 @@ def test_wifi_ieee8021x_eap_pwd(self):
}
""")

def test_wifi_ieee8021x_eap_and_psk(self):
self.generate('''network:
version: 2
wifis:
wl0:
access-points:
homenet:
password: psk_password
auth:
key-management: eap
method: leap
identity: some-id
password: "********"''')

self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant
network={
ssid="homenet"
key_mgmt=WPA-EAP
eap=LEAP
ieee80211w=1
identity="some-id"
psk="psk_password"
password="********"
}
""")


class TestNetworkManager(TestBase):

Expand Down Expand Up @@ -997,6 +1024,47 @@ def test_wifi_ieee8021x_pwd(self):
eap=pwd
identity=some-id
password=**********
'''})

def test_wifi_eap_and_psk(self):
self.generate('''network:
version: 2
renderer: NetworkManager
wifis:
wl0:
access-points:
homenet:
password: psk_password
auth:
key-management: eap
method: leap
identity: "some-id"
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
psk=psk_password
[802-1x]
eap=leap
identity=some-id
password=**********
'''})

def test_wifi_wowlan(self):
Expand Down

0 comments on commit b4acb08

Please sign in to comment.