wifi/iwd: merge branch 'balrog-kun:write-iwd-configs'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/695
This commit is contained in:
Thomas Haller 2021-03-23 14:25:31 +01:00
commit bf564937bb
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
8 changed files with 1134 additions and 26 deletions

View file

@ -474,6 +474,26 @@ no-auto-default=*
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>iwd-config-path</varname></term>
<listitem>
<para>
If the value points to an existing directory, Network
Manager will attempt to write copies of new or modified
Wi-Fi connection profiles, converted into the IWD
format, into this directory thus making IWD connection
properties editable. This will only happen if the IWD
backend is active meaning that at least one Wi-Fi device
must exist.
</para>
<para>
This allows editing connection profile settings such as
the 802.1x configuration using Network Manager clients.
Without it such changes have no effect in IWD.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View file

@ -8,6 +8,8 @@
#include "nm-iwd-manager.h"
#include <net/if.h>
#include <glib/gstdio.h>
#include <errno.h>
#include "libnm-core-intern/nm-core-internal.h"
#include "nm-manager.h"
@ -16,6 +18,7 @@
#include "libnm-glib-aux/nm-random-utils.h"
#include "settings/nm-settings.h"
#include "libnm-std-aux/nm-dbus-compat.h"
#include "nm-config.h"
/*****************************************************************************/
@ -28,6 +31,7 @@ typedef struct {
typedef struct {
GDBusProxy * known_network;
NMSettingsConnection *mirror_connection;
const KnownNetworkId *id;
} KnownNetworkData;
typedef struct {
@ -419,7 +423,7 @@ known_network_update_cb(GObject *source, GAsyncResult *res, gpointer user_data)
variant = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
if (!variant) {
nm_log_warn(LOGD_WIFI,
"Updating %s on IWD known network %s failed: %s",
"iwd: updating %s on IWD known network %s failed: %s",
(const char *) user_data,
g_dbus_proxy_get_object_path(G_DBUS_PROXY(source)),
error->message);
@ -427,17 +431,31 @@ known_network_update_cb(GObject *source, GAsyncResult *res, gpointer user_data)
}
static void
sett_conn_changed(NMSettingsConnection *sett_conn, guint update_reason, KnownNetworkData *data)
sett_conn_changed(NMSettingsConnection * sett_conn,
guint update_reason,
const KnownNetworkData *data)
{
NMSettingsConnectionIntFlags flags;
NMConnection * conn = nm_settings_connection_get_connection(sett_conn);
NMSettingConnection * s_conn = nm_connection_get_setting_connection(conn);
gboolean nm_autoconnectable = nm_setting_connection_get_autoconnect(s_conn);
gboolean iwd_autoconnectable = get_property_bool(data->known_network, "AutoConnect", TRUE);
NMConnection * conn = nm_settings_connection_get_connection(sett_conn);
NMSettingConnection * s_conn = nm_connection_get_setting_connection(conn);
NMSettingWireless * s_wifi = nm_connection_get_setting_wireless(conn);
nm_auto_unref_keyfile GKeyFile *iwd_config = NULL;
const char * iwd_dir;
gs_free char * filename = NULL;
gs_free char * full_path = NULL;
gs_free_error GError *error = NULL;
NMIwdNetworkSecurity security;
GBytes * ssid;
const guint8 * ssid_data;
gsize ssid_len;
gboolean removed;
nm_assert(sett_conn == data->mirror_connection);
if (iwd_autoconnectable == nm_autoconnectable)
if (update_reason
& (NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS
| NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
| NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT))
return;
/* If this is a generated connection it may be ourselves updating it */
@ -445,21 +463,113 @@ sett_conn_changed(NMSettingsConnection *sett_conn, guint update_reason, KnownNet
if (NM_FLAGS_HAS(flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
return;
iwd_dir = nm_config_data_get_iwd_config_path(NM_CONFIG_GET_DATA);
if (!iwd_dir || iwd_dir[0] == '\0' || !g_file_test(iwd_dir, G_FILE_TEST_IS_DIR)) {
gboolean nm_autoconnectable = nm_setting_connection_get_autoconnect(s_conn);
gboolean iwd_autoconnectable = get_property_bool(data->known_network, "AutoConnect", TRUE);
if (iwd_autoconnectable == nm_autoconnectable) {
nm_log_dbg(LOGD_WIFI,
"iwd: updating AutoConnect on known network at %s based on connection %s",
g_dbus_proxy_get_object_path(data->known_network),
nm_settings_connection_get_id(data->mirror_connection));
g_dbus_proxy_call(data->known_network,
DBUS_INTERFACE_PROPERTIES ".Set",
g_variant_new("(ssv)",
NM_IWD_KNOWN_NETWORK_INTERFACE,
"AutoConnect",
g_variant_new_boolean(nm_autoconnectable)),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
known_network_update_cb,
"AutoConnect");
}
return;
}
/* If the SSID and the security type in the NMSettingsConnection haven't
* changed, we just need to overwrite the original IWD config file.
* Otherwise we need to call Forget on the original KnownNetwork or
* remove its file. IWD will have to delete one D-Bus object and
* create another anyway because the SSID and security type are in the
* D-Bus object path, so no point renaming the file.
*/
ssid = nm_setting_wireless_get_ssid(s_wifi);
ssid_data = ssid ? g_bytes_get_data(ssid, &ssid_len) : NULL;
removed = FALSE;
if (!nm_wifi_connection_get_iwd_ssid_and_security(conn, NULL, &security)
|| security != data->id->security || !ssid_data || ssid_len != strlen(data->id->name)
|| memcmp(ssid_data, data->id->name, ssid_len)) {
gs_free char *orig_filename =
nm_wifi_utils_get_iwd_config_filename(data->id->name, -1, data->id->security);
gs_free char *orig_full_path = g_strdup_printf("%s/%s", iwd_dir, orig_filename);
if (g_remove(orig_full_path) == 0)
nm_log_dbg(LOGD_WIFI, "iwd: profile at %s removed", orig_full_path);
else if (errno != ENOENT)
nm_log_dbg(LOGD_WIFI,
"iwd: profile at %s not removed: %s (%i)",
orig_full_path,
strerror(errno),
errno);
removed = TRUE;
}
if (!nm_streq(nm_settings_connection_get_connection_type(sett_conn), "802-11-wireless")
|| !s_wifi)
return;
/* If the connection has any permissions other than the default we don't
* want to save it as an IWD profile. IWD will make it available for
* everybody to attempt a connection, remove, or toggle "autoconnectable".
*/
if (s_conn && nm_setting_connection_get_num_permissions(s_conn)) {
nm_log_dbg(
LOGD_WIFI,
"iwd: changed Wi-Fi connection %s not mirrored as IWD profile because of non-default "
"permissions",
nm_settings_connection_get_id(sett_conn));
return;
}
iwd_config = nm_wifi_utils_connection_to_iwd_config(conn, &filename, &error);
if (!iwd_config) {
/* The error message here is not translated and it only goes in
* the logs.
*/
nm_log_dbg(LOGD_WIFI,
"iwd: changed Wi-Fi connection %s not mirrored as IWD profile: %s",
nm_settings_connection_get_id(sett_conn),
error->message);
return;
}
full_path = g_strdup_printf("%s/%s", iwd_dir, filename);
if (removed && g_file_test(full_path, G_FILE_TEST_EXISTS)) {
nm_log_dbg(LOGD_WIFI,
"iwd: changed Wi-Fi connection %s not mirrored as IWD profile because %s "
"already exists",
nm_settings_connection_get_id(sett_conn),
full_path);
return;
}
if (!g_key_file_save_to_file(iwd_config, full_path, &error)) {
nm_log_dbg(LOGD_WIFI,
"iwd: changed Wi-Fi connection %s not mirrored as IWD profile: save error: %s",
nm_settings_connection_get_id(sett_conn),
error->message);
return;
}
nm_log_dbg(LOGD_WIFI,
"Updating AutoConnect on known network at %s based on connection %s",
g_dbus_proxy_get_object_path(data->known_network),
nm_settings_connection_get_id(data->mirror_connection));
g_dbus_proxy_call(data->known_network,
DBUS_INTERFACE_PROPERTIES ".Set",
g_variant_new("(ssv)",
NM_IWD_KNOWN_NETWORK_INTERFACE,
"AutoConnect",
g_variant_new_boolean(nm_autoconnectable)),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
known_network_update_cb,
"AutoConnect");
"iwd: changed Wi-Fi connection %s mirrored as IWD profile %s",
nm_settings_connection_get_id(sett_conn),
full_path);
}
/* Look up an existing NMSettingsConnection for a network that has been
@ -590,7 +700,7 @@ mirror_connection(NMIwdManager * self,
NULL);
g_object_set(G_OBJECT(s_wifi), NM_SETTING_WIRELESS_HIDDEN, hidden, NULL);
} else {
KnownNetworkData data = {known_network, settings_connection};
KnownNetworkData data = {known_network, settings_connection, id};
sett_conn_changed(settings_connection, 0, &data);
}
}
@ -779,6 +889,7 @@ interface_added(GDBusObjectManager *object_manager,
} else {
data = g_slice_new0(KnownNetworkData);
data->known_network = g_object_ref(proxy);
data->id = id;
g_hash_table_insert(priv->known_networks, id, data);
}
@ -907,6 +1018,9 @@ connection_removed(NMSettings *settings, NMSettingsConnection *sett_conn, gpoint
const guint8 * ssid_bytes;
gsize ssid_len;
NMSettingsConnection *new_mirror_conn;
const char * iwd_dir;
gs_free char * filename = NULL;
gs_free char * full_path = NULL;
if (!nm_wifi_connection_get_iwd_ssid_and_security(conn, NULL, &id.security))
return;
@ -923,8 +1037,12 @@ connection_removed(NMSettings *settings, NMSettingsConnection *sett_conn, gpoint
ssid_buf[ssid_len] = '\0';
id.name = ssid_buf;
data = g_hash_table_lookup(priv->known_networks, &id);
if (!data)
return;
if (!data) {
if (!g_utf8_validate((const char *) ssid_bytes, ssid_len, NULL))
return;
goto try_delete_file;
}
if (data->mirror_connection != sett_conn)
return;
@ -941,7 +1059,7 @@ connection_removed(NMSettings *settings, NMSettingsConnection *sett_conn, gpoint
}
if (!priv->running)
return;
goto try_delete_file;
g_dbus_proxy_call(data->known_network,
"Forget",
@ -951,6 +1069,91 @@ connection_removed(NMSettings *settings, NMSettingsConnection *sett_conn, gpoint
NULL,
NULL,
NULL);
return;
try_delete_file:
if (mirror_connection(self, &id, FALSE, NULL))
return;
iwd_dir = nm_config_data_get_iwd_config_path(NM_CONFIG_GET_DATA);
if (!iwd_dir || iwd_dir[0] == '\0' || !g_file_test(iwd_dir, G_FILE_TEST_IS_DIR))
return;
filename = nm_wifi_utils_get_iwd_config_filename(id.name, ssid_len, id.security);
full_path = g_strdup_printf("%s/%s", iwd_dir, filename);
if (g_remove(full_path) == 0)
_LOGD("IWD profile at %s removed", full_path);
else if (errno != ENOENT)
_LOGD("IWD profile at %s not removed: %s (%i)", full_path, strerror(errno), errno);
}
static void
connection_added(NMSettings *settings, NMSettingsConnection *sett_conn, gpointer user_data)
{
NMIwdManager * self = user_data;
NMConnection * conn = nm_settings_connection_get_connection(sett_conn);
NMSettingConnection *s_conn = nm_connection_get_setting_connection(conn);
const char * iwd_dir;
gs_free char * filename = NULL;
gs_free char * full_path = NULL;
gs_free_error GError *error = NULL;
nm_auto_unref_keyfile GKeyFile *iwd_config = NULL;
NMSettingsConnectionIntFlags flags;
if (!nm_streq(nm_settings_connection_get_connection_type(sett_conn), "802-11-wireless"))
return;
iwd_dir = nm_config_data_get_iwd_config_path(NM_CONFIG_GET_DATA);
if (!iwd_dir || iwd_dir[0] == '\0' || !g_file_test(iwd_dir, G_FILE_TEST_IS_DIR))
return;
/* If this is a generated connection it may be ourselves creating it and
* directly assigning it to a KnownNetwork's .mirror_connection.
*/
flags = nm_settings_connection_get_flags(sett_conn);
if (NM_FLAGS_HAS(flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
return;
/* If the connection has any permissions other than the default we don't
* want to save it as an IWD profile. IWD will make it available for
* everybody to attempt a connection, remove, or toggle "autoconnectable".
*/
if (s_conn && nm_setting_connection_get_num_permissions(s_conn)) {
_LOGD("New Wi-Fi connection %s not mirrored as IWD profile because of non-default "
"permissions",
nm_settings_connection_get_id(sett_conn));
return;
}
iwd_config = nm_wifi_utils_connection_to_iwd_config(conn, &filename, &error);
if (!iwd_config) {
/* The error message here is not translated and it only goes in
* the logs.
*/
_LOGD("New Wi-Fi connection %s not mirrored as IWD profile: %s",
nm_settings_connection_get_id(sett_conn),
error->message);
return;
}
full_path = g_strdup_printf("%s/%s", iwd_dir, filename);
if (g_file_test(full_path, G_FILE_TEST_EXISTS)) {
_LOGD("New Wi-Fi connection %s not mirrored as IWD profile because %s already exists",
nm_settings_connection_get_id(sett_conn),
full_path);
return;
}
if (!g_key_file_save_to_file(iwd_config, full_path, &error)) {
_LOGD("New Wi-Fi connection %s not mirrored as IWD profile: save error: %s",
nm_settings_connection_get_id(sett_conn),
error->message);
return;
}
_LOGD("New Wi-Fi connection %s mirrored as IWD profile %s",
nm_settings_connection_get_id(sett_conn),
full_path);
}
static gboolean
@ -1305,11 +1508,31 @@ nm_iwd_manager_init(NMIwdManager *self)
g_signal_connect(priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK(device_added), self);
g_signal_connect(priv->manager, NM_MANAGER_DEVICE_REMOVED, G_CALLBACK(device_removed), self);
/* The current logic is that we track all creations and removals but
* for modifications we only listen to those connections that are
* currently a KnownNetwork's mirror_connection. There may be multiple
* NMSettingsConnections referring to the same SSID+Security type tuple
* so to the same KnownNetwork. So to make connection profile editing
* work at least for the simple cases, we track one NMSettingsConnection
* out of those, and we map its changes to the IWD KnownNetwork.
*
* When an NMSettingsConnection is created by a user for a completely
* new network and the settings are compatible with IWD, we create an
* IWD KnownNetwork config file for it. IWD will notice that and a
* KnownNetwork objects pops up on D-Bus. We look up a suitable
* mirror_connection for it and only then subscribe to modification
* signals. There are various different ways that this could be done,
* it's not clear which one's the best.
*/
priv->settings = g_object_ref(NM_SETTINGS_GET);
g_signal_connect(priv->settings,
NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
G_CALLBACK(connection_removed),
self);
g_signal_connect(priv->settings,
NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
G_CALLBACK(connection_added),
self);
priv->cancellable = g_cancellable_new();

View file

@ -7,10 +7,13 @@
#include "nm-wifi-utils.h"
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <stdlib.h>
#include "nm-utils.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "libnm-core-aux-intern/nm-common-macros.h"
static gboolean
verify_no_wep(NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error)
@ -943,3 +946,838 @@ nm_wifi_connection_get_iwd_ssid_and_security(NMConnection * connection,
return TRUE;
}
/* Builds the IWD network configuration file name for a given SSID
* and security type pair. The SSID should be valid UTF-8 and in
* any case must contain no NUL-bytes. If @ssid is NUL-terminated,
* @ssid_len can be -1 instead of actual SSID length.
*/
char *
nm_wifi_utils_get_iwd_config_filename(const char * ssid,
gssize ssid_len,
NMIwdNetworkSecurity security)
{
const char *security_suffix;
const char *ptr;
gboolean alnum_ssid = TRUE;
for (ptr = ssid; ssid_len != 0 && *ptr != '\0'; ptr++, ssid_len--)
if (!g_ascii_isalnum(*ptr) && !strchr("-_ ", *ptr))
alnum_ssid = FALSE;
g_return_val_if_fail(ptr != ssid && ptr - ssid <= NM_IW_ESSID_MAX_SIZE, NULL);
switch (security) {
case NM_IWD_NETWORK_SECURITY_OPEN:
security_suffix = "open";
break;
case NM_IWD_NETWORK_SECURITY_PSK:
security_suffix = "psk";
break;
case NM_IWD_NETWORK_SECURITY_8021X:
security_suffix = "8021x";
break;
default:
return NULL;
}
if (alnum_ssid) {
return g_strdup_printf("%.*s.%s", (int) (ptr - ssid), ssid, security_suffix);
} else {
char ssid_buf[NM_IW_ESSID_MAX_SIZE * 2 + 1];
return g_strdup_printf("=%s.%s",
nm_utils_bin2hexstr_full(ssid, ptr - ssid, '\0', FALSE, ssid_buf),
security_suffix);
}
}
static gboolean
psk_setting_to_iwd_config(GKeyFile *file, NMSettingWirelessSecurity *s_wsec, GError **error)
{
NMSettingSecretFlags psk_flags = nm_setting_wireless_security_get_psk_flags(s_wsec);
const char * psk = nm_setting_wireless_security_get_psk(s_wsec);
gsize psk_len;
guint8 buffer[32];
const char * key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec);
if (!psk || (psk_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
g_key_file_set_comment(file,
"Security",
NULL,
"The passphrase is to be queried through the agent",
NULL);
if (psk_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) {
nm_log_info(
LOGD_WIFI,
"IWD network config is being created wihout the PSK but IWD will save the PSK on "
"successful activation not honoring the psk-flags property");
}
return TRUE;
}
psk_len = strlen(psk);
if (nm_streq0(key_mgmt, "sae")) {
g_key_file_set_string(file, "Security", "Passphrase", psk);
} else if (psk_len >= 8 && psk_len <= 63) {
g_key_file_set_string(file, "Security", "Passphrase", psk);
} else if (psk_len == 64 && nm_utils_hexstr2bin_buf(psk, FALSE, FALSE, NULL, buffer)) {
g_key_file_set_string(file, "Security", "PreSharedKey", psk);
} else {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Unknown PSK format");
return FALSE;
}
return TRUE;
}
static gboolean
eap_certs_to_iwd_config(GKeyFile * file,
NMSetting8021x *s_8021x,
bool phase2,
char * iwd_prefix,
GError ** error)
{
NMSetting8021xCKScheme ca_cert_scheme =
phase2 ? nm_setting_802_1x_get_phase2_ca_cert_scheme(s_8021x)
: nm_setting_802_1x_get_ca_cert_scheme(s_8021x);
NMSetting8021xCKScheme client_cert_scheme =
phase2 ? nm_setting_802_1x_get_phase2_client_cert_scheme(s_8021x)
: nm_setting_802_1x_get_ca_cert_scheme(s_8021x);
NMSetting8021xCKScheme key_scheme;
NMSettingSecretFlags key_password_flags;
const char * ca_path = phase2 ? nm_setting_802_1x_get_phase2_ca_path(s_8021x)
: nm_setting_802_1x_get_ca_path(s_8021x);
const char * cert_path;
const char * key_path = NULL;
const char * key_password;
const char * domain_suffix_match;
const char * domain_match;
char setting_buf[128];
/* TODO: should check that all certificates and the key are RSA */
/* Note: up to IWD 1.9 only the PEM encoding was supported for certificates
* and only PKCS#8 PEM for keys but we don't know the IWD version here.
* From IWD 1.10 raw (DER) X.509 certificates and PKCS#12 are also supported
* for certificates but a certificate list or chain still has to be PEM
* (i.e. if it contains more than one certificate.) Raw PKCS#12 and
* old-style OpenSSL PEM formats are also supported for keys. Hopefully
* this is in practice the same set of file:// formats as supported by
* nm_crypto_* / wpa_supplicant so we need no conversions here.
*/
if (nm_setting_802_1x_get_system_ca_certs(s_8021x)) {
/* Either overrides or is added to the certificates in (phase2-)ca-cert
* and ca-path depending on whether it points to a file or a directory.
* We can't ignore this property so it's an error if it is set.
* Fortunately not used by nm-connection-editor.
*/
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"The system-ca-certs property is not supported");
return FALSE;
}
if (ca_path) {
/* To support this (and this could be applied to system-ca-certs as
* well) we'd have to scan the directory, parse the certificates and
* write a new certificate-list file to point to in the IWD config.
* This is going to create issues of where to store these files, for
* how long and with what permission bits. Fortunately this doesn't
* seem to be used by nm-connection-editor either.
*
* That file would also have to contain whatever the (phase2-)ca-cert
* propterty points to because IWD has only one CACert setting per
* phase.
*/
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"The (phase2-)ca-path property is not supported");
return FALSE;
}
if (ca_cert_scheme != NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
if (ca_cert_scheme != NM_SETTING_802_1X_CK_SCHEME_PATH) {
/* To support the blob scheme we'd have to either convert the
* certificate data into a PEM payload and embed the PEM file in
* the IWD config file, which is not supported by GKeyFile, or write
* it into a new file to point to in the IWD config. This is again
* is going to create issues of where to store these files, for how
* long and with what permission bits. Fortunately this scheme isn't
* used in nm-connection-editor either.
*
* PKCS#11 is not supported by IWD in any way so we don't need to
* support the PKCS#11 URI scheme.
*
* If scheme is unknown, assume no value is set.
*/
g_set_error_literal(
error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"(phase2-)ca-cert property schemes other than file:// not supported");
return FALSE;
}
cert_path = phase2 ? nm_setting_802_1x_get_phase2_ca_cert_path(s_8021x)
: nm_setting_802_1x_get_ca_cert_path(s_8021x);
if (cert_path)
g_key_file_set_string(file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "CACert"),
cert_path);
}
if (client_cert_scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN)
goto private_key_done;
if (client_cert_scheme != NM_SETTING_802_1X_CK_SCHEME_PATH) {
g_set_error_literal(
error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"(phase2-)client-cert property schemes other than file:// not supported");
return FALSE;
}
cert_path = phase2 ? nm_setting_802_1x_get_phase2_client_cert_path(s_8021x)
: nm_setting_802_1x_get_client_cert_path(s_8021x);
if (!cert_path)
goto private_key_done;
g_key_file_set_string(file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "ClientCert"),
cert_path);
key_scheme = phase2 ? nm_setting_802_1x_get_phase2_private_key_scheme(s_8021x)
: nm_setting_802_1x_get_private_key_scheme(s_8021x);
if (key_scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
key_path = phase2 ? nm_setting_802_1x_get_phase2_private_key_path(s_8021x)
: nm_setting_802_1x_get_private_key_path(s_8021x);
if (key_scheme != NM_SETTING_802_1X_CK_SCHEME_PATH || !key_path) {
/* The same comments apply to writing the key into a temporary file
* as for the certificates (above), except this is even more
* sensitive.
*/
g_set_error_literal(
error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"(phase2-)private-key property schemes other than file:// not supported");
return FALSE;
}
g_key_file_set_string(file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "ClientKey"),
key_path);
key_password = phase2 ? nm_setting_802_1x_get_phase2_private_key_password(s_8021x)
: nm_setting_802_1x_get_private_key_password(s_8021x);
key_password_flags = phase2 ? nm_setting_802_1x_get_phase2_private_key_password_flags(s_8021x)
: nm_setting_802_1x_get_private_key_password_flags(s_8021x);
if (!key_password || (key_password_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
g_key_file_set_comment(
file,
"Security",
setting_buf,
"ClientKeyPassphrase not to be saved, will be queried through the agent if needed",
NULL);
goto private_key_done;
}
g_key_file_set_string(file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "ClientKeyPassphrase"),
key_password);
private_key_done:
if (phase2 ? nm_setting_802_1x_get_phase2_subject_match(s_8021x)
: nm_setting_802_1x_get_subject_match(s_8021x)) {
g_set_error_literal(
error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"(phase2-)subject-match not supported, use domain-match or domain-suffix-match");
return FALSE;
}
if (phase2 ? nm_setting_802_1x_get_num_phase2_altsubject_matches(s_8021x)
: nm_setting_802_1x_get_num_altsubject_matches(s_8021x)) {
/* We could convert the "DNS:" entries into a ServerDomainMask but we'd
* have to leave out the "EMAIL:" and "URI:" types or report error.
* The interpretation still wouldn't be exactly the same as in
* wpa_supplicant.
*/
g_set_error_literal(
error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"(phase2-)altsubject-matches not supported, use domain-match or domain-suffix-match");
return FALSE;
}
domain_suffix_match = phase2 ? nm_setting_802_1x_get_phase2_domain_suffix_match(s_8021x)
: nm_setting_802_1x_get_domain_suffix_match(s_8021x);
domain_match = phase2 ? nm_setting_802_1x_get_phase2_domain_match(s_8021x)
: nm_setting_802_1x_get_domain_match(s_8021x);
if (domain_suffix_match || domain_match) {
GString * s = g_string_sized_new(128);
const char *ptr;
const char *end;
for (ptr = domain_suffix_match; ptr; ptr = *end == ';' ? end + 1 : NULL) {
if (s->len)
g_string_append_c(s, ';');
end = strchrnul(ptr, ';');
/* Use *.<suffix> to get the suffix match effect */
g_string_append(s, "*.");
g_string_append_len(s, ptr, end - ptr);
}
/* domain-match can be appended as-is */
if (domain_match) {
if (s->len)
g_string_append_c(s, ';');
g_string_append(s, domain_match);
}
g_key_file_set_string(file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "ServerDomainMask"),
s->str);
g_string_free(s, TRUE);
}
return TRUE;
}
static void
eap_method_name_to_iwd_config(GKeyFile *file, const char *iwd_prefix, const char *method)
{
char setting_buf[128];
g_key_file_set_string(file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Method"),
method);
}
static void
eap_optional_identity_to_iwd_config(GKeyFile *file, const char *iwd_prefix, const char *identity)
{
char setting_buf[128];
/* The identity is optional for some methods where an authenticator may
* in theory not ask for it. For our usage here we treat it as always
* optional because it can be omitted in the config file if the user
* wants IWD to query for it on every connection.
*/
if (identity) {
g_key_file_set_string(file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Identity"),
identity);
} else {
g_key_file_set_comment(
file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Method"),
"Identity not to be saved, will be queried through the agent if needed",
NULL);
}
}
static gboolean
eap_optional_password_to_iwd_config(GKeyFile * file,
const char * iwd_prefix,
NMSetting8021x *s_8021x,
GError ** error)
{
char setting_buf[128];
const char * password = nm_setting_802_1x_get_password(s_8021x);
NMSettingSecretFlags flags = nm_setting_802_1x_get_password_flags(s_8021x);
if (!password && nm_setting_802_1x_get_password_raw(s_8021x)) {
/* IWD doesn't support passwords that can't be encoded in the config
* file, i.e. containing NUL characters. Those that don't have NULs
* could in theory be written to the config file but GKeyFile may not
* like that if they're no UTF-8, and the password-raw property is
* not written by nm-connection-editor anyway.
*/
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Non-UTF-8 passwords are not supported, if the password is UTF-8 set "
"the \"password\" property");
return FALSE;
}
if (!password || (flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
return g_key_file_set_comment(file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Method"),
"Password not to be saved, will be queried through the agent",
error);
} else {
g_key_file_set_string(file,
"Security",
nm_sprintf_buf(setting_buf, "%s%s", iwd_prefix, "Password"),
password);
return TRUE;
}
}
static gboolean
eap_method_config_to_iwd_config(GKeyFile * file,
NMSetting8021x *s_8021x,
gboolean phase2,
const char * method,
const char * iwd_prefix,
GError ** error)
{
char prefix_buf[128];
if (nm_streq0(method, "tls")) {
eap_method_name_to_iwd_config(file, iwd_prefix, "TLS");
eap_optional_identity_to_iwd_config(file,
iwd_prefix,
nm_setting_802_1x_get_identity(s_8021x));
return eap_certs_to_iwd_config(file,
s_8021x,
phase2,
nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "TLS-"),
error);
} else if (nm_streq0(method, "ttls") && !phase2) {
const char *noneap_method = nm_setting_802_1x_get_phase2_auth(s_8021x);
eap_method_name_to_iwd_config(file, iwd_prefix, "TTLS");
eap_optional_identity_to_iwd_config(file,
iwd_prefix,
nm_setting_802_1x_get_anonymous_identity(s_8021x));
if (!eap_certs_to_iwd_config(file,
s_8021x,
phase2,
nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "TTLS-"),
error))
return FALSE;
nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "TTLS-Phase2-");
if (nm_setting_802_1x_get_phase2_autheap(s_8021x)) {
if (noneap_method) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Only one TTLS phase 2 method can be set");
return FALSE;
}
return eap_method_config_to_iwd_config(file,
s_8021x,
TRUE,
nm_setting_802_1x_get_phase2_autheap(s_8021x),
prefix_buf,
error);
}
if (NM_IN_STRSET(noneap_method, "chap", "mschap", "mschapv2", "pap")) {
const char *iwd_method;
if (nm_streq0(noneap_method, "chap")) {
iwd_method = "Tunneled-CHAP";
} else if (nm_streq0(noneap_method, "mschap")) {
iwd_method = "Tunneled-MSCHAP";
} else if (nm_streq0(noneap_method, "mschapv2")) {
iwd_method = "Tunneled-MSCHAPv2";
} else {
iwd_method = "Tunneled-PAP";
}
eap_method_name_to_iwd_config(file, prefix_buf, iwd_method);
eap_optional_identity_to_iwd_config(file,
prefix_buf,
nm_setting_802_1x_get_identity(s_8021x));
return eap_optional_password_to_iwd_config(file, prefix_buf, s_8021x, error);
}
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Unsupported TTLS non-EAP inner method");
return FALSE;
} else if (nm_streq0(method, "peap") && !phase2) {
eap_method_name_to_iwd_config(file, iwd_prefix, "PEAP");
eap_optional_identity_to_iwd_config(file,
iwd_prefix,
nm_setting_802_1x_get_anonymous_identity(s_8021x));
if (!eap_certs_to_iwd_config(file,
s_8021x,
phase2,
nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "PEAP-"),
error))
return FALSE;
if (nm_setting_802_1x_get_phase1_peapver(s_8021x)
|| nm_setting_802_1x_get_phase1_peaplabel(s_8021x))
nm_log_info(LOGD_WIFI,
"IWD network config will not honour the PEAP version and label properties "
"in the 802.1x setting (unsupported)");
if (!nm_setting_802_1x_get_phase2_auth(s_8021x)) {
/* Apparently PEAP can be used without a phase 2 but this is not
* supported by either NM or IWD.
*/
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"PEAP without an inner method is unsupported");
return FALSE;
}
return eap_method_config_to_iwd_config(
file,
s_8021x,
TRUE,
nm_setting_802_1x_get_phase2_auth(s_8021x),
nm_sprintf_buf(prefix_buf, "%s%s", iwd_prefix, "PEAP-Phase2-"),
error);
} else if (nm_streq0(method, "md5") && phase2) {
eap_method_name_to_iwd_config(file, iwd_prefix, "MD5");
eap_optional_identity_to_iwd_config(file,
iwd_prefix,
nm_setting_802_1x_get_identity(s_8021x));
return eap_optional_password_to_iwd_config(file, iwd_prefix, s_8021x, error);
} else if (nm_streq0(method, "gtc") && phase2) {
eap_method_name_to_iwd_config(file, iwd_prefix, "GTC");
eap_optional_identity_to_iwd_config(file,
iwd_prefix,
nm_setting_802_1x_get_identity(s_8021x));
return eap_optional_password_to_iwd_config(file, iwd_prefix, s_8021x, error);
} else if (nm_streq0(method, "pwd")) {
eap_method_name_to_iwd_config(file, iwd_prefix, "PWD");
eap_optional_identity_to_iwd_config(file,
iwd_prefix,
nm_setting_802_1x_get_identity(s_8021x));
return eap_optional_password_to_iwd_config(file, iwd_prefix, s_8021x, error);
} else if (nm_streq0(method, "mschapv2")) {
eap_method_name_to_iwd_config(file, iwd_prefix, "MSCHAPV2");
eap_optional_identity_to_iwd_config(file,
iwd_prefix,
nm_setting_802_1x_get_identity(s_8021x));
/* In this case we can support password-raw but would have to
* MD4-hash it and set as <iwd_prefix>Password-Hash
*/
return eap_optional_password_to_iwd_config(file, iwd_prefix, s_8021x, error);
} else if (nm_streq0(method, "external")) {
/* This may be a connection created by NMIwdManager in whch case there
* may be no need to be convert it back to the IWD format. Ideally we
* would still rewrite the other sections/groups in the IWD settings
* file and preserve the [Security] group -- TODO. Possibly this should
* also not be reported as an error.
*/
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Connection contains no EAP method configuration");
return FALSE;
} else {
/* Some methods are only allowed in phase 1 or only phase 2.
* OTP, LEAP and FAST are not supported by IWD at all.
*/
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
phase2 ? "Unsupported phase 2 EAP method"
: "Unsupported phase 1 EAP method");
return FALSE;
}
return TRUE;
}
static gboolean
eap_setting_to_iwd_config(GKeyFile *file, NMSetting8021x *s_8021x, GError **error)
{
const char *method;
if (!s_8021x || nm_setting_802_1x_get_num_eap_methods(s_8021x) == 0) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"The 802.1x setting is missing or no EAP method set");
return FALSE;
}
if (!nm_setting_verify(NM_SETTING(s_8021x), NULL, error))
return FALSE;
method = nm_setting_802_1x_get_eap_method(s_8021x, 0);
if (nm_setting_802_1x_get_num_eap_methods(s_8021x) > 1)
nm_log_info(LOGD_WIFI,
"IWD network config will only contain the first EAP method: %s",
method);
if (nm_setting_802_1x_get_phase1_auth_flags(s_8021x))
nm_log_info(LOGD_WIFI,
"IWD network config will not honour the TLSv1.x-disable flags in the 802.1x "
"setting (unsupported)");
if (nm_setting_802_1x_get_auth_timeout(s_8021x))
nm_log_info(LOGD_WIFI,
"IWD network config will not honour the auth-timeout property in the 802.1x "
"setting (unsupported)");
return eap_method_config_to_iwd_config(file, s_8021x, FALSE, method, "EAP-", error);
}
static gboolean
ip4_config_to_iwd_config(GKeyFile *file, NMSettingIPConfig *s_ip, GError **error)
{
guint num;
struct in_addr ip;
/* These settings are not acutally used unless global
* [General].EnableNetworkConfiguration is true, which we don't support.
* We add them for sake of completness, although many NMSettingIPConfig
* configurations can't be mapped to IWD configs and we simply ignore
* them. If they were to be used we'd need to add a few warnings.
*/
if (!s_ip)
return TRUE;
num = nm_setting_ip_config_get_num_dns(s_ip);
if (num) {
GString *s = g_string_sized_new(128);
int i;
for (i = 0; i < num; i++) {
if (s->len)
g_string_append_c(s, ' ');
g_string_append(s, nm_setting_ip_config_get_dns(s_ip, i));
}
/* It doesn't matter whether we add the DNS under [IPv4] or [IPv6]
* except that with method=auto the list will override the
* DNS addresses received over the DHCP version corresponing to
* v4 or v6.
* Note ignore-auto-dns=false isn't supported, this list always
* overrides the DHCP DNSes.
*/
g_key_file_set_string(file, "IPv4", "DNS", s->str);
g_string_free(s, TRUE);
}
if (!nm_streq0(nm_setting_ip_config_get_method(s_ip), NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
return TRUE;
num = nm_setting_ip_config_get_num_addresses(s_ip);
if (num) {
NMIPAddress *addr = nm_setting_ip_config_get_address(s_ip, 0);
guint prefix = nm_ip_address_get_prefix(addr);
in_addr_t netmask = htonl(0xffffffffu << (32 - prefix));
char buf[INET_ADDRSTRLEN];
nm_ip_address_get_address_binary(addr, &ip);
g_key_file_set_string(file, "IPv4", "Address", nm_ip_address_get_address(addr));
g_key_file_set_string(file, "IPv4", "Netmask", _nm_utils_inet4_ntop(netmask, buf));
} else {
inet_pton(AF_INET, "10.42.0.100", &ip);
g_key_file_set_string(file, "IPv4", "Address", "10.42.0.100");
}
if (nm_setting_ip_config_get_gateway(s_ip)) {
g_key_file_set_string(file, "IPv4", "Gateway", nm_setting_ip_config_get_gateway(s_ip));
} else {
uint32_t val;
char buf[INET_ADDRSTRLEN];
/* IWD won't enable static IP unless both Address and Gateway are
* set so generate a gateway address if not known.
*/
val = (ntohl(ip.s_addr) & 0xfffffff0) + 1;
if (val == ntohl(ip.s_addr))
val += 1;
g_key_file_set_string(file, "IPv4", "Gateway", _nm_utils_inet4_ntop(htonl(val), buf));
}
return TRUE;
}
static gboolean
ip6_config_to_iwd_config(GKeyFile *file, NMSettingIPConfig *s_ip, GError **error)
{
guint num;
NMIPAddress *addr;
char buf[INET6_ADDRSTRLEN + 10];
if (!s_ip)
return TRUE;
num = nm_setting_ip_config_get_num_dns(s_ip);
if (num) {
GString *s = g_string_sized_new(128);
int i;
for (i = 0; i < num; i++) {
if (s->len)
g_string_append_c(s, ' ');
g_string_append(s, nm_setting_ip_config_get_dns(s_ip, i));
}
g_key_file_set_string(file, "IPv6", "DNS", s->str);
g_string_free(s, TRUE);
}
if (!NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip),
NM_SETTING_IP6_CONFIG_METHOD_AUTO,
NM_SETTING_IP6_CONFIG_METHOD_DHCP,
NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
return TRUE;
g_key_file_set_boolean(file, "IPv6", "Enabled", TRUE);
if (!nm_streq0(nm_setting_ip_config_get_method(s_ip), NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
return TRUE;
if (!nm_setting_ip_config_get_num_addresses(s_ip)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"IP address required for IPv6 manual config");
return FALSE;
}
addr = nm_setting_ip_config_get_address(s_ip, 0);
g_key_file_set_string(file,
"IPv6",
"Address",
nm_sprintf_buf(buf,
"%s/%u",
nm_ip_address_get_address(addr),
nm_ip_address_get_prefix(addr)));
if (nm_setting_ip_config_get_gateway(s_ip))
g_key_file_set_string(file, "IPv6", "Gateway", nm_setting_ip_config_get_gateway(s_ip));
return TRUE;
}
GKeyFile *
nm_wifi_utils_connection_to_iwd_config(NMConnection *connection,
char ** out_filename,
GError ** error)
{
NMSettingConnection * s_conn = nm_connection_get_setting_connection(connection);
NMSettingWireless * s_wifi = nm_connection_get_setting_wireless(connection);
GBytes * ssid;
const guint8 * ssid_data;
gsize ssid_len;
NMIwdNetworkSecurity security;
const char * cloned_mac_addr;
nm_auto_unref_keyfile GKeyFile *file = NULL;
if (!s_conn || !s_wifi
|| !nm_streq(nm_setting_connection_get_connection_type(s_conn),
NM_SETTING_WIRELESS_SETTING_NAME)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Connection and/or wireless settings are missing");
return NULL;
}
if (!NM_IN_STRSET(nm_setting_wireless_get_mode(s_wifi), NULL, NM_SETTING_WIRELESS_MODE_INFRA)) {
g_set_error_literal(
error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Non-infrastructure-mode connections don't have IWD profiles (or aren't supported)");
return NULL;
}
ssid = nm_setting_wireless_get_ssid(s_wifi);
ssid_data = ssid ? g_bytes_get_data(ssid, &ssid_len) : NULL;
if (!ssid_data || ssid_len <= 0 || ssid_len > NM_IW_ESSID_MAX_SIZE
|| !g_utf8_validate((const char *) ssid_data, ssid_len, NULL)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Empty or non-UTF-8 SSIDs not supported by IWD");
return NULL;
}
if (!nm_wifi_connection_get_iwd_ssid_and_security(connection, NULL, &security)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Connection's security type unrecognised");
return NULL;
}
file = g_key_file_new();
if (!nm_setting_connection_get_autoconnect(s_conn))
g_key_file_set_boolean(file, "Settings", "AutoConnect", FALSE);
if (nm_setting_wireless_get_hidden(s_wifi))
g_key_file_set_boolean(file, "Settings", "Hidden", TRUE);
/* Only effective if IWD's global [General].AddressRandomization is set
* to "network". "random" maps to [Settings].AlwaysRandomizeAddress=true,
* "stable" is the default, specific address maps to
* [Settings].AddressOverride set to that address. "permanent" is not
* supported and "preserve" can only be achieved using the global
* [General].AddressRandomization=disabled setting. We don't print
* warnings when we can't map the value here because we don't know what
* IWD's [General].AddressRandomization is set to.
*/
cloned_mac_addr = nm_setting_wireless_get_cloned_mac_address(s_wifi);
if (nm_streq0(cloned_mac_addr, NM_CLONED_MAC_RANDOM))
g_key_file_set_boolean(file, "Settings", "AlwaysRandomizeAddress", TRUE);
else if (cloned_mac_addr && nm_utils_hwaddr_valid(cloned_mac_addr, ETH_ALEN))
g_key_file_set_string(file, "Settings", "AddressOverride", cloned_mac_addr);
if (!ip4_config_to_iwd_config(
file,
NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip4_config(connection)),
error))
return NULL;
if (!ip6_config_to_iwd_config(
file,
NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip6_config(connection)),
error))
return NULL;
switch (security) {
case NM_IWD_NETWORK_SECURITY_OPEN:
break;
case NM_IWD_NETWORK_SECURITY_PSK:
if (!psk_setting_to_iwd_config(file,
nm_connection_get_setting_wireless_security(connection),
error))
return NULL;
break;
case NM_IWD_NETWORK_SECURITY_8021X:
if (!eap_setting_to_iwd_config(file, nm_connection_get_setting_802_1x(connection), error))
return NULL;
break;
default:
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
"Connection security type is not supported");
return NULL;
}
if (out_filename)
*out_filename =
nm_wifi_utils_get_iwd_config_filename((const char *) ssid_data, ssid_len, security);
return g_steal_pointer(&file);
}

View file

@ -36,5 +36,11 @@ gboolean nm_wifi_utils_is_manf_default_ssid(GBytes *ssid);
gboolean nm_wifi_connection_get_iwd_ssid_and_security(NMConnection * connection,
char ** ssid,
NMIwdNetworkSecurity *security);
char * nm_wifi_utils_get_iwd_config_filename(const char * ssid,
gssize ssid_len,
NMIwdNetworkSecurity security);
GKeyFile *
nm_wifi_utils_connection_to_iwd_config(NMConnection *conn, char **out_filename, GError **error);
#endif /* __NM_WIFI_UTILS_H__ */

View file

@ -97,6 +97,8 @@ typedef struct {
NMGlobalDnsConfig *global_dns;
bool systemd_resolved : 1;
char *iwd_config_path;
} NMConfigDataPrivate;
struct _NMConfigData {
@ -341,6 +343,12 @@ nm_config_data_get_systemd_resolved(const NMConfigData *self)
return NM_CONFIG_DATA_GET_PRIVATE(self)->systemd_resolved;
}
const char *
nm_config_data_get_iwd_config_path(const NMConfigData *self)
{
return NM_CONFIG_DATA_GET_PRIVATE(self)->iwd_config_path;
}
gboolean
nm_config_data_get_ignore_carrier(const NMConfigData *self, NMDevice *device)
{
@ -684,6 +692,7 @@ static const struct {
NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT},
{NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_DHCP, NM_CONFIG_DEFAULT_MAIN_DHCP},
{NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH, ""},
{NM_CONFIG_KEYFILE_GROUP_LOGGING, "backend", NM_CONFIG_DEFAULT_LOGGING_BACKEND},
{NM_CONFIG_KEYFILE_GROUP_LOGGING, "audit", NM_CONFIG_DEFAULT_LOGGING_AUDIT},
};
@ -1910,6 +1919,12 @@ constructed(GObject *object)
if (!priv->global_dns)
priv->global_dns = load_global_dns(priv->keyfile_intern, TRUE);
priv->iwd_config_path =
nm_strstrip(g_key_file_get_string(priv->keyfile,
NM_CONFIG_KEYFILE_GROUP_MAIN,
NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH,
NULL));
G_OBJECT_CLASS(nm_config_data_parent_class)->constructed(object);
}
@ -1996,6 +2011,8 @@ finalize(GObject *gobject)
nm_global_dns_config_free(priv->global_dns);
g_free(priv->iwd_config_path);
_match_section_infos_free(priv->connection_infos);
_match_section_infos_free(priv->device_infos);

View file

@ -190,6 +190,8 @@ int nm_config_data_get_sriov_num_vfs(const NMConfigData *self, NMDevice *de
NMGlobalDnsConfig *nm_config_data_get_global_dns_config(const NMConfigData *self);
const char *nm_config_data_get_iwd_config_path(const NMConfigData *self);
extern const char *__start_connection_defaults[];
extern const char *__stop_connection_defaults[];

View file

@ -843,7 +843,8 @@ static const ConfigGroup config_groups[] = {
NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS,
NM_CONFIG_KEYFILE_KEY_MAIN_RC_MANAGER,
NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER,
NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED, ),
NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED,
NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH, ),
},
{
.group = NM_CONFIG_KEYFILE_GROUP_LOGGING,

View file

@ -34,6 +34,7 @@
#define NM_CONFIG_KEYFILE_KEY_MAIN_RC_MANAGER "rc-manager"
#define NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER "slaves-order"
#define NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED "systemd-resolved"
#define NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH "iwd-config-path"
#define NM_CONFIG_KEYFILE_KEY_LOGGING_AUDIT "audit"
#define NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND "backend"