/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2006 - 2012 Red Hat, Inc. * Copyright (C) 2007 - 2008 Novell, Inc. */ #include "src/core/nm-default-daemon.h" #include "nm-supplicant-config.h" #include #include "libnm-glib-aux/nm-str-buf.h" #include "libnm-core-intern/nm-core-internal.h" #include "nm-supplicant-settings-verify.h" #include "nm-setting.h" #include "libnm-core-aux-intern/nm-auth-subject.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-setting-ip4-config.h" typedef struct { char *value; guint32 len; NMSupplOptType type; } ConfigOption; /*****************************************************************************/ typedef struct { GHashTable *config; GHashTable *blobs; NMSupplCapMask capabilities; guint32 ap_scan; bool fast_required : 1; bool dispose_has_run : 1; bool ap_isolation : 1; } NMSupplicantConfigPrivate; struct _NMSupplicantConfig { GObject parent; NMSupplicantConfigPrivate _priv; }; struct _NMSupplicantConfigClass { GObjectClass parent; }; G_DEFINE_TYPE(NMSupplicantConfig, nm_supplicant_config, G_TYPE_OBJECT) #define NM_SUPPLICANT_CONFIG_GET_PRIVATE(self) \ _NM_GET_PRIVATE(self, NMSupplicantConfig, NM_IS_SUPPLICANT_CONFIG) /*****************************************************************************/ static gboolean _get_capability(NMSupplicantConfigPrivate *priv, NMSupplCapType type) { return NM_SUPPL_CAP_MASK_GET(priv->capabilities, type) == NM_TERNARY_TRUE; } NMSupplicantConfig * nm_supplicant_config_new(NMSupplCapMask capabilities) { NMSupplicantConfigPrivate *priv; NMSupplicantConfig *self; self = g_object_new(NM_TYPE_SUPPLICANT_CONFIG, NULL); priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self); priv->capabilities = capabilities; return self; } static void config_option_free(ConfigOption *opt) { g_free(opt->value); g_slice_free(ConfigOption, opt); } static void nm_supplicant_config_init(NMSupplicantConfig *self) { NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self); priv->config = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, (GDestroyNotify) config_option_free); priv->ap_scan = 1; priv->dispose_has_run = FALSE; } static gboolean nm_supplicant_config_add_option_with_type(NMSupplicantConfig *self, const char *key, const char *value, gint32 len, NMSupplOptType opt_type, const char *display_value, GError **error) { NMSupplicantConfigPrivate *priv; ConfigOption *old_opt; ConfigOption *opt; NMSupplOptType type; g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE); g_return_val_if_fail(key != NULL, FALSE); g_return_val_if_fail(value != NULL, FALSE); nm_assert(!error || !*error); priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self); if (len < 0) len = strlen(value); if (opt_type != NM_SUPPL_OPT_TYPE_INVALID) type = opt_type; else { type = nm_supplicant_settings_verify_setting(key, value, len); if (type == NM_SUPPL_OPT_TYPE_INVALID) { gs_free char *str_free = NULL; const char *str; str = nm_utils_buf_utf8safe_escape(value, len, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, &str_free); str = nm_strquote_a(255, str); g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "key '%s' and/or value %s invalid", key, display_value ?: str); return FALSE; } } old_opt = (ConfigOption *) g_hash_table_lookup(priv->config, key); if (old_opt) { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "key '%s' already configured", key); return FALSE; } opt = g_slice_new0(ConfigOption); opt->value = g_malloc(len + 1); memcpy(opt->value, value, len); opt->value[len] = '\0'; opt->len = len; opt->type = type; { char buf[255]; memset(&buf[0], 0, sizeof(buf)); memcpy(&buf[0], opt->value, opt->len > 254 ? 254 : opt->len); nm_log_info(LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, display_value ?: &buf[0]); } g_hash_table_insert(priv->config, g_strdup(key), opt); return TRUE; } static gboolean nm_supplicant_config_add_option(NMSupplicantConfig *self, const char *key, const char *value, gint32 len, const char *display_value, GError **error) { return nm_supplicant_config_add_option_with_type(self, key, value, len, NM_SUPPL_OPT_TYPE_INVALID, display_value, error); } static gboolean nm_supplicant_config_add_blob(NMSupplicantConfig *self, const char *key, GBytes *value, const char *blobid, GError **error) { NMSupplicantConfigPrivate *priv; ConfigOption *old_opt; ConfigOption *opt; NMSupplOptType type; const guint8 *data; gsize data_len; g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE); g_return_val_if_fail(key != NULL, FALSE); g_return_val_if_fail(value != NULL, FALSE); g_return_val_if_fail(blobid != NULL, FALSE); data = g_bytes_get_data(value, &data_len); g_return_val_if_fail(data_len > 0, FALSE); priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self); type = nm_supplicant_settings_verify_setting(key, (const char *) data, data_len); if (type == NM_SUPPL_OPT_TYPE_INVALID) { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "key '%s' and/or its contained value is invalid", key); return FALSE; } old_opt = (ConfigOption *) g_hash_table_lookup(priv->config, key); if (old_opt) { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "key '%s' already configured", key); return FALSE; } opt = g_slice_new0(ConfigOption); opt->value = g_strdup_printf("blob://%s", blobid); opt->len = strlen(opt->value); opt->type = type; nm_log_info(LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, opt->value); g_hash_table_insert(priv->config, g_strdup(key), opt); if (!priv->blobs) { priv->blobs = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_bytes_unref); } g_hash_table_insert(priv->blobs, g_strdup(blobid), g_bytes_ref(value)); return TRUE; } static gboolean nm_supplicant_config_add_blob_for_connection(NMSupplicantConfig *self, GBytes *field, const char *name, const char *con_uid, GError **error) { if (field && g_bytes_get_size(field)) { gs_free char *uid = NULL; char *p; uid = g_strdup_printf("%s-%s", con_uid, name); for (p = uid; *p; p++) { if (*p == '/') *p = '-'; } if (!nm_supplicant_config_add_blob(self, name, field, uid, error)) return FALSE; } return TRUE; } static void nm_supplicant_config_finalize(GObject *object) { NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(object); g_hash_table_destroy(priv->config); nm_clear_pointer(&priv->blobs, g_hash_table_destroy); G_OBJECT_CLASS(nm_supplicant_config_parent_class)->finalize(object); } static void nm_supplicant_config_class_init(NMSupplicantConfigClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = nm_supplicant_config_finalize; } guint32 nm_supplicant_config_get_ap_scan(NMSupplicantConfig *self) { g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), 1); return NM_SUPPLICANT_CONFIG_GET_PRIVATE(self)->ap_scan; } gboolean nm_supplicant_config_fast_required(NMSupplicantConfig *self) { g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE); return NM_SUPPLICANT_CONFIG_GET_PRIVATE(self)->fast_required; } GVariant * nm_supplicant_config_to_variant(NMSupplicantConfig *self) { NMSupplicantConfigPrivate *priv; GVariantBuilder builder; GHashTableIter iter; ConfigOption *option; const char *key; g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), NULL); priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self); g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); g_hash_table_iter_init(&iter, priv->config); while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &option)) { switch (option->type) { case NM_SUPPL_OPT_TYPE_INT: g_variant_builder_add(&builder, "{sv}", key, g_variant_new_int32(atoi(option->value))); break; case NM_SUPPL_OPT_TYPE_BYTES: case NM_SUPPL_OPT_TYPE_UTF8: g_variant_builder_add(&builder, "{sv}", key, nm_g_variant_new_ay((const guint8 *) option->value, option->len)); break; case NM_SUPPL_OPT_TYPE_KEYWORD: case NM_SUPPL_OPT_TYPE_STRING: g_variant_builder_add(&builder, "{sv}", key, g_variant_new_string(option->value)); break; default: break; } } return g_variant_builder_end(&builder); } GHashTable * nm_supplicant_config_get_blobs(NMSupplicantConfig *self) { g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), NULL); return NM_SUPPLICANT_CONFIG_GET_PRIVATE(self)->blobs; } static const char * wifi_freqs_to_string(gboolean bg_band) { static const char *str_2ghz = NULL; static const char *str_5ghz = NULL; const char **f_p; const char *f; f_p = bg_band ? &str_2ghz : &str_5ghz; again: f = g_atomic_pointer_get(f_p); if (G_UNLIKELY(!f)) { nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(400, FALSE); const guint *freqs; int i; freqs = bg_band ? nm_utils_wifi_2ghz_freqs() : nm_utils_wifi_5ghz_freqs(); for (i = 0; freqs[i]; i++) { if (i > 0) nm_str_buf_append_c(&strbuf, ' '); nm_str_buf_append_printf(&strbuf, "%u", freqs[i]); } f = g_strdup(nm_str_buf_get_str(&strbuf)); if (!g_atomic_pointer_compare_and_exchange(f_p, NULL, f)) { g_free((char *) f); goto again; } } return f; } gboolean nm_supplicant_config_add_setting_macsec(NMSupplicantConfig *self, NMSettingMacsec *setting, GError **error) { const char *value; char buf[32]; int port; g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE); g_return_val_if_fail(setting != NULL, FALSE); g_return_val_if_fail(!error || !*error, FALSE); if (!nm_supplicant_config_add_option(self, "macsec_policy", "1", -1, NULL, error)) return FALSE; value = nm_setting_macsec_get_encrypt(setting) ? "0" : "1"; if (!nm_supplicant_config_add_option(self, "macsec_integ_only", value, -1, NULL, error)) return FALSE; port = nm_setting_macsec_get_port(setting); if (port > 0 && port < 65534) { g_snprintf(buf, sizeof(buf), "%d", port); if (!nm_supplicant_config_add_option(self, "macsec_port", buf, -1, NULL, error)) return FALSE; } if (nm_setting_macsec_get_mode(setting) == NM_SETTING_MACSEC_MODE_PSK) { guint8 buffer_cak[NM_SETTING_MACSEC_MKA_CAK_LENGTH / 2]; guint8 buffer_ckn[NM_SETTING_MACSEC_MKA_CKN_LENGTH / 2]; if (!nm_supplicant_config_add_option(self, "key_mgmt", "NONE", -1, NULL, error)) return FALSE; value = nm_setting_macsec_get_mka_cak(setting); if (!value || !nm_utils_hexstr2bin_buf(value, FALSE, FALSE, NULL, buffer_cak)) { g_set_error_literal(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, value ? "invalid MKA CAK" : "missing MKA CAK"); return FALSE; } if (!nm_supplicant_config_add_option(self, "mka_cak", (char *) buffer_cak, sizeof(buffer_cak), "", error)) return FALSE; value = nm_setting_macsec_get_mka_ckn(setting); if (!value || !nm_utils_hexstr2bin_buf(value, FALSE, FALSE, NULL, buffer_ckn)) { g_set_error_literal(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, value ? "invalid MKA CKN" : "missing MKA CKN"); return FALSE; } if (!nm_supplicant_config_add_option(self, "mka_ckn", (char *) buffer_ckn, sizeof(buffer_ckn), value, error)) return FALSE; } return TRUE; } gboolean nm_supplicant_config_add_setting_wireless(NMSupplicantConfig *self, NMSettingWireless *setting, guint32 fixed_freq, GError **error) { NMSupplicantConfigPrivate *priv; gboolean is_adhoc, is_ap, is_mesh; const char *mode, *band; guint32 channel; GBytes *ssid; const char *bssid; g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE); g_return_val_if_fail(setting != NULL, FALSE); g_return_val_if_fail(!error || !*error, FALSE); priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self); mode = nm_setting_wireless_get_mode(setting); is_adhoc = nm_streq0(mode, "adhoc"); is_ap = nm_streq0(mode, "ap"); is_mesh = nm_streq0(mode, "mesh"); if (is_adhoc || is_ap) priv->ap_scan = 2; else priv->ap_scan = 1; ssid = nm_setting_wireless_get_ssid(setting); if (!nm_supplicant_config_add_option(self, "ssid", (char *) g_bytes_get_data(ssid, NULL), g_bytes_get_size(ssid), NULL, error)) return FALSE; if (is_adhoc) { if (!nm_supplicant_config_add_option(self, "mode", "1", -1, NULL, error)) return FALSE; } if (is_ap) { if (!nm_supplicant_config_add_option(self, "mode", "2", -1, NULL, error)) return FALSE; if (nm_setting_wireless_get_hidden(setting) && !nm_supplicant_config_add_option(self, "ignore_broadcast_ssid", "1", -1, NULL, error)) return FALSE; } if (is_mesh) { if (!nm_supplicant_config_add_option(self, "mode", "5", -1, NULL, error)) return FALSE; } if ((is_adhoc || is_ap || is_mesh) && fixed_freq) { gs_free char *str_freq = NULL; str_freq = g_strdup_printf("%u", fixed_freq); if (!nm_supplicant_config_add_option(self, "frequency", str_freq, -1, NULL, error)) return FALSE; } /* Except for Ad-Hoc, Hotspot and Mesh, request that the driver probe for the * specific SSID we want to associate with. */ if (!(is_adhoc || is_ap || is_mesh)) { if (!nm_supplicant_config_add_option(self, "scan_ssid", "1", -1, NULL, error)) return FALSE; } bssid = nm_setting_wireless_get_bssid(setting); if (bssid) { if (!nm_supplicant_config_add_option(self, "bssid", bssid, strlen(bssid), NULL, error)) return FALSE; } band = nm_setting_wireless_get_band(setting); channel = nm_setting_wireless_get_channel(setting); if (band) { if (channel) { guint32 freq; gs_free char *str_freq = NULL; freq = nm_utils_wifi_channel_to_freq(channel, band); str_freq = g_strdup_printf("%u", freq); if (!nm_supplicant_config_add_option(self, "freq_list", str_freq, -1, NULL, error)) return FALSE; } else { const char *freqs = NULL; if (nm_streq(band, "a")) freqs = wifi_freqs_to_string(FALSE); else if (nm_streq(band, "bg")) freqs = wifi_freqs_to_string(TRUE); if (freqs && !nm_supplicant_config_add_option(self, "freq_list", freqs, strlen(freqs), NULL, error)) return FALSE; } } return TRUE; } gboolean nm_supplicant_config_add_bgscan(NMSupplicantConfig *self, NMConnection *connection, GError **error) { NMSettingWireless *s_wifi; NMSettingWirelessSecurity *s_wsec; const char *bgscan; s_wifi = nm_connection_get_setting_wireless(connection); g_assert(s_wifi); /* Don't scan when a shared connection (either AP or Ad-Hoc) is active; * it will disrupt connected clients. */ if (NM_IN_STRSET(nm_setting_wireless_get_mode(s_wifi), NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) return TRUE; /* Don't scan when the connection is locked to a specific AP, since * intra-ESS roaming (which requires periodic scanning) isn't being * used due to the specific AP lock. (bgo #513820) */ if (nm_setting_wireless_get_bssid(s_wifi)) return TRUE; /* Default to a very long bgscan interval when signal is OK on the assumption * that either (a) there aren't multiple APs and we don't need roaming, or * (b) since EAP/802.1x isn't used and thus there are fewer steps to fail * during a roam, we can wait longer before scanning for roam candidates. */ bgscan = "simple:30:-70:86400"; /* If using WPA Enterprise, Dynamic WEP or we have seen more than one AP use * a shorter bgscan interval on the assumption that this is a multi-AP ESS * in which we want more reliable roaming between APs. Thus trigger scans * when the signal is still somewhat OK so we have an up-to-date roam * candidate list when the signal gets bad. */ if (nm_setting_wireless_get_num_seen_bssids(s_wifi) > 1 || ((s_wsec = nm_connection_get_setting_wireless_security(connection)) && NM_IN_STRSET(nm_setting_wireless_security_get_key_mgmt(s_wsec), "ieee8021x", "wpa-eap", "wpa-eap-suite-b-192"))) bgscan = "simple:30:-65:300"; return nm_supplicant_config_add_option(self, "bgscan", bgscan, -1, FALSE, error); } static gboolean add_string_val(NMSupplicantConfig *self, const char *field, const char *name, gboolean ucase, const char *display_value, GError **error) { if (field) { gs_free char *value = NULL; if (ucase) { value = g_ascii_strup(field, -1); field = value; } return nm_supplicant_config_add_option(self, name, field, strlen(field), display_value, error); } return TRUE; } #define ADD_STRING_LIST_VAL(self, \ setting, \ setting_name, \ field, \ field_plural, \ name, \ separator, \ ucase, \ display_value, \ error) \ ({ \ typeof(setting) _setting = (setting); \ gboolean _success = TRUE; \ \ if (nm_setting_##setting_name##_get_num_##field_plural(_setting)) { \ const char _separator = (separator); \ GString *_str = g_string_new(NULL); \ guint _k, _n; \ \ _n = nm_setting_##setting_name##_get_num_##field_plural(_setting); \ for (_k = 0; _k < _n; _k++) { \ const char *item = nm_setting_##setting_name##_get_##field(_setting, _k); \ \ if (!_str->len) { \ g_string_append(_str, item); \ } else { \ g_string_append_c(_str, _separator); \ g_string_append(_str, item); \ } \ } \ if ((ucase)) \ g_string_ascii_up(_str); \ if (_str->len) { \ if (!nm_supplicant_config_add_option((self), \ (name), \ _str->str, \ -1, \ (display_value), \ (error))) \ _success = FALSE; \ } \ g_string_free(_str, TRUE); \ } \ _success; \ }) static void wep128_passphrase_hash(const char *input, gsize input_len, guint8 *digest /* 13 bytes */) { nm_auto_free_checksum GChecksum *sum = NULL; guint8 md5[NM_UTILS_CHECKSUM_LENGTH_MD5]; guint8 data[64]; int i; nm_assert(input); nm_assert(input_len); nm_assert(digest); /* Get at least 64 bytes by repeating the passphrase into the buffer */ for (i = 0; i < sizeof(data); i++) data[i] = input[i % input_len]; sum = g_checksum_new(G_CHECKSUM_MD5); g_checksum_update(sum, data, sizeof(data)); nm_utils_checksum_get_digest(sum, md5); /* WEP104 keys are 13 bytes in length (26 hex characters) */ memcpy(digest, md5, 13); } static gboolean add_wep_key(NMSupplicantConfig *self, const char *key, const char *name, NMWepKeyType wep_type, GError **error) { gsize key_len; if (!key || (key_len = strlen(key)) == 0) return TRUE; if (wep_type == NM_WEP_KEY_TYPE_UNKNOWN) { if (nm_utils_wep_key_valid(key, NM_WEP_KEY_TYPE_KEY)) wep_type = NM_WEP_KEY_TYPE_KEY; else if (nm_utils_wep_key_valid(key, NM_WEP_KEY_TYPE_PASSPHRASE)) wep_type = NM_WEP_KEY_TYPE_PASSPHRASE; } if ((wep_type == NM_WEP_KEY_TYPE_UNKNOWN) || (wep_type == NM_WEP_KEY_TYPE_KEY)) { if ((key_len == 10) || (key_len == 26)) { guint8 buffer[26 / 2]; if (!nm_utils_hexstr2bin_full(key, FALSE, FALSE, FALSE, NULL, key_len / 2, buffer, sizeof(buffer), NULL)) { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "cannot add wep-key %s to supplicant config because key is not hex", name); return FALSE; } if (!nm_supplicant_config_add_option(self, name, (char *) buffer, key_len / 2, "", error)) return FALSE; } else if ((key_len == 5) || (key_len == 13)) { if (!nm_supplicant_config_add_option(self, name, key, key_len, "", error)) return FALSE; } else { g_set_error( error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Cannot add wep-key %s to supplicant config because key-length %u is invalid", name, (guint) key_len); return FALSE; } } else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) { guint8 digest[13]; wep128_passphrase_hash(key, key_len, digest); if (!nm_supplicant_config_add_option(self, name, (const char *) digest, sizeof(digest), "", error)) return FALSE; } return TRUE; } gboolean nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig *self, NMSettingWirelessSecurity *setting, NMSetting8021x *setting_8021x, const char *con_uuid, const char *mode, guint32 mtu, NMSettingWirelessSecurityPmf pmf, NMSettingWirelessSecurityFils fils, GError **error) { NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self); nm_auto_free_gstring GString *key_mgmt_conf = NULL; const char *key_mgmt, *auth_alg; const char *psk; gboolean set_pmf, wps_disabled; gboolean is_ap; g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE); g_return_val_if_fail(setting != NULL, FALSE); g_return_val_if_fail(con_uuid != NULL, FALSE); g_return_val_if_fail(!error || !*error, FALSE); /* Currently wpa_supplicant doesn't support FT in AP mode. Even * if it did, it would require additional parameters as the nas * identifier and the mobility domain. Therefore we disable all * FT key-mgmts in AP mode. */ is_ap = nm_streq0(mode, NM_SETTING_WIRELESS_MODE_AP); /* Check if we actually support FILS */ if (!_get_capability(priv, NM_SUPPL_CAP_TYPE_FILS)) { if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED) { g_set_error_literal(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Supplicant does not support FILS"); return FALSE; } else if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL) fils = NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE; } key_mgmt = nm_setting_wireless_security_get_key_mgmt(setting); key_mgmt_conf = g_string_new(""); if (nm_streq(key_mgmt, "none")) { g_string_append(key_mgmt_conf, "NONE"); } else if (nm_streq(key_mgmt, "ieee8021x")) { g_string_append(key_mgmt_conf, "IEEE8021X"); } else if (nm_streq(key_mgmt, "owe")) { pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED; g_string_append(key_mgmt_conf, "OWE"); } else if (nm_streq(key_mgmt, "wpa-psk")) { if (pmf != NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) g_string_append(key_mgmt_conf, "WPA-PSK"); if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) g_string_append(key_mgmt_conf, " WPA-PSK-SHA256"); if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) g_string_append(key_mgmt_conf, " FT-PSK"); /* For NM "key-mgmt=wpa-psk" doesn't strictly mean WPA1/wPA2 only, * but also allows WPA3 (SAE), so that existing connections can * benefit from the improved security when the AP gets upgraded. * * According to WPA3_Specification_v3.0 section 2.3, when operating * in WPA3-Personal transition mode a STA: * * - should allow AKM suite selector: 00-0F-AC:6 (WPA-PSK-SHA256) to * be selected for an association; * - shall negotiate PMF when associating to an AP using SAE. * * Those conditions are met when the interface has capabilities * SAE, PMF, BIP. * * According to WPA3_Specification_v3.0 section 2.3, when operating * in WPA3-Personal transition mode an AP: * * - shall set MFPC to 1, MFPR to 0. * * Therefore, do not operate in WPA3-Personal transition mode when PMF * is set to disabled. This also provides a way to be compatible with * some devices that are not fully compatible with WPA3-Personal * transition mode. */ if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SAE) && _get_capability(priv, NM_SUPPL_CAP_TYPE_PMF) && _get_capability(priv, NM_SUPPL_CAP_TYPE_BIP) && (!is_ap || pmf != NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE)) { g_string_append(key_mgmt_conf, " SAE"); if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) g_string_append(key_mgmt_conf, " FT-SAE"); } } else if (nm_streq(key_mgmt, "sae")) { pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED; g_string_append(key_mgmt_conf, "SAE"); if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) g_string_append(key_mgmt_conf, " FT-SAE"); } else if (nm_streq(key_mgmt, "wpa-eap")) { if (pmf != NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) g_string_append(key_mgmt_conf, "WPA-EAP"); if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) { g_string_append(key_mgmt_conf, " FT-EAP"); if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384)) g_string_append(key_mgmt_conf, " FT-EAP-SHA384"); } if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) { g_string_append(key_mgmt_conf, " WPA-EAP-SHA256"); if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SUITEB192) && pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) g_string_append(key_mgmt_conf, " WPA-EAP-SUITE-B-192"); } switch (fils) { case NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED: g_string_truncate(key_mgmt_conf, 0); if (!_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) g_string_assign(key_mgmt_conf, "FILS-SHA256 FILS-SHA384"); /* fall-through */ case NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL: if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) { g_string_append(key_mgmt_conf, " FILS-SHA256 FILS-SHA384"); if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) { g_string_append(key_mgmt_conf, " FT-FILS-SHA256"); if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384)) g_string_append(key_mgmt_conf, " FT-FILS-SHA384"); } } break; default: break; } } else if (nm_streq(key_mgmt, "wpa-eap-suite-b-192")) { pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED; g_string_append(key_mgmt_conf, "WPA-EAP-SUITE-B-192"); if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT) && _get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384)) g_string_append(key_mgmt_conf, " FT-EAP-SHA384"); } if (!add_string_val(self, key_mgmt_conf->str, "key_mgmt", TRUE, NULL, error)) return FALSE; auth_alg = nm_setting_wireless_security_get_auth_alg(setting); if (!add_string_val(self, auth_alg, "auth_alg", TRUE, NULL, error)) return FALSE; psk = nm_setting_wireless_security_get_psk(setting); if (psk) { size_t psk_len = strlen(psk); if (psk_len >= 8 && psk_len <= 63) { /* Use NM_SUPPL_OPT_TYPE_STRING here so that it gets pushed to the * supplicant as a string, and therefore gets quoted, * and therefore the supplicant will interpret it as a * passphrase and not a hex key. */ if (!nm_supplicant_config_add_option_with_type(self, "psk", psk, -1, NM_SUPPL_OPT_TYPE_STRING, "", error)) return FALSE; } else if (nm_streq(key_mgmt, "sae")) { /* If the SAE password doesn't comply with WPA-PSK limitation, * we need to call it "sae_password" instead of "psk". */ if (!nm_supplicant_config_add_option_with_type(self, "sae_password", psk, -1, NM_SUPPL_OPT_TYPE_STRING, "", error)) return FALSE; } else if (psk_len == 64) { guint8 buffer[32]; /* Hex PSK */ if (!nm_utils_hexstr2bin_buf(psk, FALSE, FALSE, NULL, buffer)) { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Cannot add psk to supplicant config due to invalid hex"); return FALSE; } if (!nm_supplicant_config_add_option(self, "psk", (char *) buffer, sizeof(buffer), "", error)) return FALSE; } else { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Cannot add psk to supplicant config due to invalid PSK length %u (not " "between 8 and 63 characters)", (guint) psk_len); return FALSE; } } /* Check if we actually support PMF */ set_pmf = TRUE; if (!_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) { if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) { g_set_error_literal(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Supplicant does not support PMF"); return FALSE; } set_pmf = FALSE; } /* Only WPA-specific things when using WPA */ if (NM_IN_STRSET(key_mgmt, "owe", "wpa-psk", "sae", "wpa-eap", "wpa-eap-suite-b-192")) { if (!ADD_STRING_LIST_VAL(self, setting, wireless_security, proto, protos, "proto", ' ', TRUE, NULL, error)) return FALSE; if (nm_streq(key_mgmt, "wpa-eap-suite-b-192")) { if (!nm_supplicant_config_add_option(self, "pairwise", "GCMP-256", -1, NULL, error)) return FALSE; if (!nm_supplicant_config_add_option(self, "group", "GCMP-256", -1, NULL, error)) return FALSE; } else { if (!ADD_STRING_LIST_VAL(self, setting, wireless_security, pairwise, pairwise, "pairwise", ' ', TRUE, NULL, error)) return FALSE; if (!ADD_STRING_LIST_VAL(self, setting, wireless_security, group, groups, "group", ' ', TRUE, NULL, error)) return FALSE; } /* We set the supplicants global "pmf" config value to "1" (optional), * so no need to set it network-specific again if PMF_OPTIONAL is set. */ if (set_pmf && NM_IN_SET(pmf, NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE, NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)) { if (!nm_supplicant_config_add_option( self, "ieee80211w", pmf == NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE ? "0" : "2", -1, NULL, error)) return FALSE; } /* In case the connection is saved as OWE / Enhanced Open, prevent * unencrypted downgrade */ if (nm_streq(key_mgmt, "owe")) { if (!nm_supplicant_config_add_option(self, "owe_only", "1", -1, NULL, error)) return FALSE; } } /* WEP keys if required */ if (nm_streq(key_mgmt, "none")) { NMWepKeyType wep_type = nm_setting_wireless_security_get_wep_key_type(setting); const char *wep0 = nm_setting_wireless_security_get_wep_key(setting, 0); const char *wep1 = nm_setting_wireless_security_get_wep_key(setting, 1); const char *wep2 = nm_setting_wireless_security_get_wep_key(setting, 2); const char *wep3 = nm_setting_wireless_security_get_wep_key(setting, 3); if (!add_wep_key(self, wep0, "wep_key0", wep_type, error)) return FALSE; if (!add_wep_key(self, wep1, "wep_key1", wep_type, error)) return FALSE; if (!add_wep_key(self, wep2, "wep_key2", wep_type, error)) return FALSE; if (!add_wep_key(self, wep3, "wep_key3", wep_type, error)) return FALSE; if (wep0 || wep1 || wep2 || wep3) { gs_free char *value = NULL; value = g_strdup_printf("%d", nm_setting_wireless_security_get_wep_tx_keyidx(setting)); if (!nm_supplicant_config_add_option(self, "wep_tx_keyidx", value, -1, NULL, error)) return FALSE; } } if (nm_streq0(auth_alg, "leap")) { /* LEAP */ if (nm_streq(key_mgmt, "ieee8021x")) { const char *tmp; tmp = nm_setting_wireless_security_get_leap_username(setting); if (!add_string_val(self, tmp, "identity", FALSE, NULL, error)) return FALSE; tmp = nm_setting_wireless_security_get_leap_password(setting); if (!add_string_val(self, tmp, "password", FALSE, "", error)) return FALSE; if (!add_string_val(self, "leap", "eap", TRUE, NULL, error)) return FALSE; } else { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Invalid key-mgmt \"%s\" for leap", key_mgmt); return FALSE; } } else { /* 802.1x for Dynamic WEP and WPA-Enterprise */ if (NM_IN_STRSET(key_mgmt, "ieee8021x", "wpa-eap", "wpa-eap-suite-b-192")) { if (!setting_8021x) { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Cannot set key-mgmt %s with missing 8021x setting", key_mgmt); return FALSE; } if (!nm_supplicant_config_add_setting_8021x(self, setting_8021x, con_uuid, mtu, FALSE, error)) return FALSE; } if (NM_IN_STRSET(key_mgmt, "wpa-eap", "wpa-eap-suite-b-192")) { /* When using WPA-Enterprise, we want to use Proactive Key Caching (also * called Opportunistic Key Caching) to avoid full EAP exchanges when * roaming between access points in the same mobility group. */ if (!nm_supplicant_config_add_option(self, "proactive_key_caching", "1", -1, NULL, error)) return FALSE; } } wps_disabled = (nm_setting_wireless_security_get_wps_method(setting) == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED); if (wps_disabled) { if (!nm_supplicant_config_add_option(self, "wps_disabled", "1", 1, NULL, error)) return FALSE; } return TRUE; } static gboolean add_pkcs11_uri_with_pin(NMSupplicantConfig *self, const char *name, const char *uri, const char *pin, const NMSettingSecretFlags pin_flags, GError **error) { gs_strfreev char **split = NULL; gs_free char *tmp = NULL; gs_free char *tmp_log = NULL; gs_free char *pin_qattr = NULL; char *escaped = NULL; if (uri == NULL) return TRUE; /* We ignore the attributes -- RFC 7512 suggests that some of them * might be unsafe and we want to be on the safe side. Also, we're * installing our attributes, so this makes things a bit easier for us. */ split = g_strsplit(uri, "&", 2); if (split[1]) nm_log_info(LOGD_SUPPLICANT, "URI attributes ignored"); /* Fill in the PIN if required. */ if (pin) { escaped = g_uri_escape_string(pin, NULL, TRUE); pin_qattr = g_strdup_printf("pin-value=%s", escaped); g_free(escaped); } else if (!(pin_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { /* Include an empty PIN to indicate the login is still needed. * Probably a token that has a PIN path and the actual PIN will * be entered using a protected path. */ pin_qattr = g_strdup("pin-value="); } tmp = g_strdup_printf("%s%s%s", split[0], (pin_qattr ? "?" : ""), (pin_qattr ?: "")); tmp_log = g_strdup_printf("%s%s%s", split[0], (pin_qattr ? "?" : ""), (pin_qattr ? "pin-value=" : "")); return add_string_val(self, tmp, name, FALSE, tmp_log, error); } gboolean nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self, NMSetting8021x *setting, const char *con_uuid, guint32 mtu, gboolean wired, GError **error) { NMSupplicantConfigPrivate *priv; char *tmp; const char *peapver, *value, *path; gboolean added; GString *phase1, *phase2; GBytes *bytes; gboolean fast = FALSE; guint32 i, num_eap; gboolean fast_provisoning_allowed = FALSE; const char *ca_path_override = NULL, *ca_cert_override = NULL; guint32 frag, hdrs; gs_free char *frag_str = NULL; NMSetting8021xAuthFlags phase1_auth_flags; nm_auto_free_gstring GString *eap_str = NULL; g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE); g_return_val_if_fail(setting != NULL, FALSE); g_return_val_if_fail(con_uuid != NULL, FALSE); priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self); value = nm_setting_802_1x_get_password(setting); if (value) { if (!add_string_val(self, value, "password", FALSE, "", error)) return FALSE; } else { bytes = nm_setting_802_1x_get_password_raw(setting); if (bytes) { if (!nm_supplicant_config_add_option(self, "password", (const char *) g_bytes_get_data(bytes, NULL), g_bytes_get_size(bytes), "", error)) return FALSE; } } value = nm_setting_802_1x_get_pin(setting); if (!add_string_val(self, value, "pin", FALSE, "", error)) return FALSE; if (wired) { if (!add_string_val(self, "IEEE8021X", "key_mgmt", FALSE, NULL, error)) return FALSE; /* Wired 802.1x must always use eapol_flags=0 */ if (!add_string_val(self, "0", "eapol_flags", FALSE, NULL, error)) return FALSE; priv->ap_scan = 0; } /* Build the "eap" option string while we check for EAP methods needing * special handling: PEAP + GTC, FAST, external */ eap_str = g_string_new(NULL); num_eap = nm_setting_802_1x_get_num_eap_methods(setting); for (i = 0; i < num_eap; i++) { const char *method = nm_setting_802_1x_get_eap_method(setting, i); if (nm_streq(method, "fast")) { fast = TRUE; priv->fast_required = TRUE; } if (nm_streq(method, "external")) { if (num_eap == 1) { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Connection settings managed externally to NM, connection" " cannot be used with wpa_supplicant"); return FALSE; } continue; } if (eap_str->len) g_string_append_c(eap_str, ' '); g_string_append(eap_str, method); } g_string_ascii_up(eap_str); if (eap_str->len && !nm_supplicant_config_add_option(self, "eap", eap_str->str, -1, NULL, error)) return FALSE; /* Adjust the fragment size according to MTU, but do not set it higher than 1280-14 * for better compatibility */ hdrs = 14; /* EAPOL + EAP-TLS */ frag = 1280 - hdrs; if (mtu > hdrs) frag = CLAMP(mtu - hdrs, 100, frag); frag_str = g_strdup_printf("%u", frag); if (!nm_supplicant_config_add_option(self, "fragment_size", frag_str, -1, NULL, error)) return FALSE; phase1 = g_string_new(NULL); peapver = nm_setting_802_1x_get_phase1_peapver(setting); if (peapver) { if (nm_streq(peapver, "0")) g_string_append(phase1, "peapver=0"); else if (nm_streq(peapver, "1")) g_string_append(phase1, "peapver=1"); } if (nm_setting_802_1x_get_phase1_peaplabel(setting)) { if (phase1->len) g_string_append_c(phase1, ' '); g_string_append_printf(phase1, "peaplabel=%s", nm_setting_802_1x_get_phase1_peaplabel(setting)); } value = nm_setting_802_1x_get_phase1_fast_provisioning(setting); if (value) { if (phase1->len) g_string_append_c(phase1, ' '); g_string_append_printf(phase1, "fast_provisioning=%s", value); if (!nm_streq(value, "0")) fast_provisoning_allowed = TRUE; } phase1_auth_flags = nm_setting_802_1x_get_phase1_auth_flags(setting); if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE)) g_string_append_printf(phase1, "%stls_disable_tlsv1_0=1", (phase1->len ? " " : "")); if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE)) g_string_append_printf(phase1, "%stls_disable_tlsv1_1=1", (phase1->len ? " " : "")); if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE)) g_string_append_printf(phase1, "%stls_disable_tlsv1_2=1", (phase1->len ? " " : "")); if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_DISABLE_TIME_CHECKS)) g_string_append_printf(phase1, "%stls_disable_time_checks=1", (phase1->len ? " " : "")); if (phase1->len) { if (!add_string_val(self, phase1->str, "phase1", FALSE, NULL, error)) { g_string_free(phase1, TRUE); return FALSE; } } g_string_free(phase1, TRUE); phase2 = g_string_new(NULL); if (nm_setting_802_1x_get_phase2_auth(setting) && !fast_provisoning_allowed) { tmp = g_ascii_strup(nm_setting_802_1x_get_phase2_auth(setting), -1); g_string_append_printf(phase2, "auth=%s", tmp); g_free(tmp); } if (nm_setting_802_1x_get_phase2_autheap(setting)) { if (phase2->len) g_string_append_c(phase2, ' '); tmp = g_ascii_strup(nm_setting_802_1x_get_phase2_autheap(setting), -1); g_string_append_printf(phase2, "autheap=%s", tmp); g_free(tmp); } if (phase2->len) { if (!add_string_val(self, phase2->str, "phase2", FALSE, NULL, error)) { g_string_free(phase2, TRUE); return FALSE; } } g_string_free(phase2, TRUE); /* PAC file */ path = nm_setting_802_1x_get_pac_file(setting); if (path) { if (!add_string_val(self, path, "pac_file", FALSE, NULL, error)) return FALSE; } else { /* PAC file is not specified. * If provisioning is allowed, use an blob format. */ if (fast_provisoning_allowed) { gs_free char *blob_name = NULL; blob_name = g_strdup_printf("blob://pac-blob-%s", con_uuid); if (!add_string_val(self, blob_name, "pac_file", FALSE, NULL, error)) return FALSE; } else { /* This is only error for EAP-FAST; don't disturb other methods. */ if (fast) { g_set_error(error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "EAP-FAST error: no PAC file provided and " "automatic PAC provisioning is disabled"); return FALSE; } } } /* If user wants to use system CA certs, either populate ca_path (if the path * is a directory) or ca_cert (the path is a file name) */ if (nm_setting_802_1x_get_system_ca_certs(setting)) { if (g_file_test(SYSTEM_CA_PATH, G_FILE_TEST_IS_DIR)) ca_path_override = SYSTEM_CA_PATH; else ca_cert_override = SYSTEM_CA_PATH; } /* CA path */ path = nm_setting_802_1x_get_ca_path(setting); path = ca_path_override ?: path; if (path) { if (!add_string_val(self, path, "ca_path", FALSE, NULL, error)) return FALSE; } /* Phase2 CA path */ path = nm_setting_802_1x_get_phase2_ca_path(setting); path = ca_path_override ?: path; if (path) { if (!add_string_val(self, path, "ca_path2", FALSE, NULL, error)) return FALSE; } /* CA certificate */ if (ca_cert_override) { if (!add_string_val(self, ca_cert_override, "ca_cert", FALSE, NULL, error)) return FALSE; } else { switch (nm_setting_802_1x_get_ca_cert_scheme(setting)) { case NM_SETTING_802_1X_CK_SCHEME_BLOB: bytes = nm_setting_802_1x_get_ca_cert_blob(setting); if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "ca_cert", con_uuid, error)) return FALSE; break; case NM_SETTING_802_1X_CK_SCHEME_PATH: path = nm_setting_802_1x_get_ca_cert_path(setting); if (!add_string_val(self, path, "ca_cert", FALSE, NULL, error)) return FALSE; break; case NM_SETTING_802_1X_CK_SCHEME_PKCS11: if (!add_pkcs11_uri_with_pin(self, "ca_cert", nm_setting_802_1x_get_ca_cert_uri(setting), nm_setting_802_1x_get_ca_cert_password(setting), nm_setting_802_1x_get_ca_cert_password_flags(setting), error)) { return FALSE; } break; default: break; } } /* Phase 2 CA certificate */ if (ca_cert_override) { if (!add_string_val(self, ca_cert_override, "ca_cert2", FALSE, NULL, error)) return FALSE; } else { switch (nm_setting_802_1x_get_phase2_ca_cert_scheme(setting)) { case NM_SETTING_802_1X_CK_SCHEME_BLOB: bytes = nm_setting_802_1x_get_phase2_ca_cert_blob(setting); if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "ca_cert2", con_uuid, error)) return FALSE; break; case NM_SETTING_802_1X_CK_SCHEME_PATH: path = nm_setting_802_1x_get_phase2_ca_cert_path(setting); if (!add_string_val(self, path, "ca_cert2", FALSE, NULL, error)) return FALSE; break; case NM_SETTING_802_1X_CK_SCHEME_PKCS11: if (!add_pkcs11_uri_with_pin( self, "ca_cert2", nm_setting_802_1x_get_phase2_ca_cert_uri(setting), nm_setting_802_1x_get_phase2_ca_cert_password(setting), nm_setting_802_1x_get_phase2_ca_cert_password_flags(setting), error)) { return FALSE; } break; default: break; } } /* Subject match */ value = nm_setting_802_1x_get_subject_match(setting); if (!add_string_val(self, value, "subject_match", FALSE, NULL, error)) return FALSE; value = nm_setting_802_1x_get_phase2_subject_match(setting); if (!add_string_val(self, value, "subject_match2", FALSE, NULL, error)) return FALSE; /* altSubjectName match */ if (!ADD_STRING_LIST_VAL(self, setting, 802_1x, altsubject_match, altsubject_matches, "altsubject_match", ';', FALSE, NULL, error)) return FALSE; if (!ADD_STRING_LIST_VAL(self, setting, 802_1x, phase2_altsubject_match, phase2_altsubject_matches, "altsubject_match2", ';', FALSE, NULL, error)) return FALSE; /* Domain suffix match */ value = nm_setting_802_1x_get_domain_suffix_match(setting); if (!add_string_val(self, value, "domain_suffix_match", FALSE, NULL, error)) return FALSE; value = nm_setting_802_1x_get_phase2_domain_suffix_match(setting); if (!add_string_val(self, value, "domain_suffix_match2", FALSE, NULL, error)) return FALSE; /* domain match */ value = nm_setting_802_1x_get_domain_match(setting); if (!add_string_val(self, value, "domain_match", FALSE, NULL, error)) return FALSE; value = nm_setting_802_1x_get_phase2_domain_match(setting); if (!add_string_val(self, value, "domain_match2", FALSE, NULL, error)) return FALSE; /* Private key */ added = FALSE; switch (nm_setting_802_1x_get_private_key_scheme(setting)) { case NM_SETTING_802_1X_CK_SCHEME_BLOB: bytes = nm_setting_802_1x_get_private_key_blob(setting); if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "private_key", con_uuid, error)) return FALSE; added = TRUE; break; case NM_SETTING_802_1X_CK_SCHEME_PATH: path = nm_setting_802_1x_get_private_key_path(setting); if (!add_string_val(self, path, "private_key", FALSE, NULL, error)) return FALSE; added = TRUE; break; case NM_SETTING_802_1X_CK_SCHEME_PKCS11: if (!add_pkcs11_uri_with_pin(self, "private_key", nm_setting_802_1x_get_private_key_uri(setting), nm_setting_802_1x_get_private_key_password(setting), nm_setting_802_1x_get_private_key_password_flags(setting), error)) { return FALSE; } added = TRUE; break; default: break; } if (added) { NMSetting8021xCKFormat format; NMSetting8021xCKScheme scheme; format = nm_setting_802_1x_get_private_key_format(setting); scheme = nm_setting_802_1x_get_private_key_scheme(setting); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { /* Only add the private key password for PKCS#12 blobs and * all path schemes, since in both of these cases the private key * isn't decrypted at all. */ value = nm_setting_802_1x_get_private_key_password(setting); if (!add_string_val(self, value, "private_key_passwd", FALSE, "", error)) return FALSE; } if (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) { /* Only add the client cert if the private key is not PKCS#12, as * wpa_supplicant configuration directs us to do. */ switch (nm_setting_802_1x_get_client_cert_scheme(setting)) { case NM_SETTING_802_1X_CK_SCHEME_BLOB: bytes = nm_setting_802_1x_get_client_cert_blob(setting); if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "client_cert", con_uuid, error)) return FALSE; break; case NM_SETTING_802_1X_CK_SCHEME_PATH: path = nm_setting_802_1x_get_client_cert_path(setting); if (!add_string_val(self, path, "client_cert", FALSE, NULL, error)) return FALSE; break; case NM_SETTING_802_1X_CK_SCHEME_PKCS11: if (!add_pkcs11_uri_with_pin( self, "client_cert", nm_setting_802_1x_get_client_cert_uri(setting), nm_setting_802_1x_get_client_cert_password(setting), nm_setting_802_1x_get_client_cert_password_flags(setting), error)) { return FALSE; } break; default: break; } } } /* Phase 2 private key */ added = FALSE; switch (nm_setting_802_1x_get_phase2_private_key_scheme(setting)) { case NM_SETTING_802_1X_CK_SCHEME_BLOB: bytes = nm_setting_802_1x_get_phase2_private_key_blob(setting); if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "private_key2", con_uuid, error)) return FALSE; added = TRUE; break; case NM_SETTING_802_1X_CK_SCHEME_PATH: path = nm_setting_802_1x_get_phase2_private_key_path(setting); if (!add_string_val(self, path, "private_key2", FALSE, NULL, error)) return FALSE; added = TRUE; break; case NM_SETTING_802_1X_CK_SCHEME_PKCS11: if (!add_pkcs11_uri_with_pin( self, "private_key2", nm_setting_802_1x_get_phase2_private_key_uri(setting), nm_setting_802_1x_get_phase2_private_key_password(setting), nm_setting_802_1x_get_phase2_private_key_password_flags(setting), error)) { return FALSE; } added = TRUE; break; default: break; } if (added) { NMSetting8021xCKFormat format; NMSetting8021xCKScheme scheme; format = nm_setting_802_1x_get_phase2_private_key_format(setting); scheme = nm_setting_802_1x_get_phase2_private_key_scheme(setting); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { /* Only add the private key password for PKCS#12 blobs and * all path schemes, since in both of these cases the private key * isn't decrypted at all. */ value = nm_setting_802_1x_get_phase2_private_key_password(setting); if (!add_string_val(self, value, "private_key2_passwd", FALSE, "", error)) return FALSE; } if (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) { /* Only add the client cert if the private key is not PKCS#12, as * wpa_supplicant configuration directs us to do. */ switch (nm_setting_802_1x_get_phase2_client_cert_scheme(setting)) { case NM_SETTING_802_1X_CK_SCHEME_BLOB: bytes = nm_setting_802_1x_get_phase2_client_cert_blob(setting); if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "client_cert2", con_uuid, error)) return FALSE; break; case NM_SETTING_802_1X_CK_SCHEME_PATH: path = nm_setting_802_1x_get_phase2_client_cert_path(setting); if (!add_string_val(self, path, "client_cert2", FALSE, NULL, error)) return FALSE; break; case NM_SETTING_802_1X_CK_SCHEME_PKCS11: if (!add_pkcs11_uri_with_pin( self, "client_cert2", nm_setting_802_1x_get_phase2_client_cert_uri(setting), nm_setting_802_1x_get_phase2_client_cert_password(setting), nm_setting_802_1x_get_phase2_client_cert_password_flags(setting), error)) { return FALSE; } break; default: break; } } } value = nm_setting_802_1x_get_identity(setting); if (!add_string_val(self, value, "identity", FALSE, NULL, error)) return FALSE; value = nm_setting_802_1x_get_anonymous_identity(setting); if (!add_string_val(self, value, "anonymous_identity", FALSE, NULL, error)) return FALSE; return TRUE; } gboolean nm_supplicant_config_add_no_security(NMSupplicantConfig *self, GError **error) { return nm_supplicant_config_add_option(self, "key_mgmt", "NONE", -1, NULL, error); } gboolean nm_supplicant_config_get_ap_isolation(NMSupplicantConfig *self) { return self->_priv.ap_isolation; } void nm_supplicant_config_set_ap_isolation(NMSupplicantConfig *self, gboolean ap_isolation) { self->_priv.ap_isolation = ap_isolation; }