summaryrefslogtreecommitdiff
path: root/src/core/supplicant/nm-supplicant-config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/supplicant/nm-supplicant-config.c')
-rw-r--r--src/core/supplicant/nm-supplicant-config.c1698
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;
+}