diff options
Diffstat (limited to 'src/core/supplicant/nm-supplicant-config.c')
-rw-r--r-- | src/core/supplicant/nm-supplicant-config.c | 1698 |
1 files changed, 1698 insertions, 0 deletions
diff --git a/src/core/supplicant/nm-supplicant-config.c b/src/core/supplicant/nm-supplicant-config.c new file mode 100644 index 0000000000..1754bfff59 --- /dev/null +++ b/src/core/supplicant/nm-supplicant-config.c @@ -0,0 +1,1698 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2006 - 2012 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#include "nm-default.h" + +#include "nm-supplicant-config.h" + +#include <stdlib.h> + +#include "nm-glib-aux/nm-str-buf.h" +#include "nm-core-internal.h" +#include "nm-supplicant-settings-verify.h" +#include "nm-setting.h" +#include "nm-libnm-core-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, + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, option->value, option->len, 1)); + 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) { + 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), + "<hidden>", + 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, + "<hidden>", + error)) + return FALSE; + } else if ((key_len == 5) || (key_len == 13)) { + if (!nm_supplicant_config_add_option(self, name, key, key_len, "<hidden>", 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), + "<hidden>", + error)) + return FALSE; + } + + return TRUE; +} + +gboolean +nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig * self, + NMSettingWirelessSecurity * setting, + NMSetting8021x * setting_8021x, + const char * con_uuid, + 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; + + 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); + + /* 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(key_mgmt); + if (nm_streq(key_mgmt, "wpa-psk")) { + if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) + g_string_append(key_mgmt_conf, " wpa-psk-sha256"); + if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) + g_string_append(key_mgmt_conf, " ft-psk"); + } else if (nm_streq(key_mgmt, "wpa-eap")) { + 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"); + } + if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) + g_string_append(key_mgmt_conf, " ft-eap"); + if (_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"); + 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 (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF) + && _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_PMF) + && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT) + && _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, "sae")) { + if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) + g_string_append(key_mgmt_conf, " ft-sae"); + } else if (nm_streq(key_mgmt, "wpa-eap-suite-b-192")) { + pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED; + if (!nm_supplicant_config_add_option(self, "pairwise", "GCMP-256", -1, NULL, error) + || !nm_supplicant_config_add_option(self, "group", "GCMP-256", -1, NULL, error)) + return FALSE; + } + + 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, + "<hidden>", + 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, + "<hidden>", + 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), + "<hidden>", + 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; + } + } + + /* Don't try to enable PMF on non-WPA/SAE/OWE networks */ + if (!NM_IN_STRSET(key_mgmt, "wpa-eap", "wpa-eap-suite-b-192", "wpa-psk", "sae", "owe")) + pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; + + /* 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, "wpa-psk", "wpa-eap", "sae", "owe")) { + if (!ADD_STRING_LIST_VAL(self, + setting, + wireless_security, + proto, + protos, + "proto", + ' ', + TRUE, + NULL, + error)) + return FALSE; + 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; + + 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; + } + } + + /* 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, "<hidden>", 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; + } + } + + 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=<hidden>" : "")); + + 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, "<hidden>", 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), + "<hidden>", + error)) + return FALSE; + } + } + value = nm_setting_802_1x_get_pin(setting); + if (!add_string_val(self, value, "pin", FALSE, "<hidden>", 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 (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, "<hidden>", 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, "<hidden>", 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; +} |