static gboolean
add_wep_key (NMSupplicantConfig *self,
             const char *key,
             const char *name,
             NMWepKeyType wep_type)
{
    GBytes *bytes;
    gboolean success = FALSE;
    size_t key_len = key ? strlen (key) : 0;

    if (!key || !key_len)
        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)) {
            bytes = nm_utils_hexstr2bin (key);
            if (bytes) {
                success = nm_supplicant_config_add_option (self,
                          name,
                          g_bytes_get_data (bytes, NULL),
                          g_bytes_get_size (bytes),
                          TRUE);
                g_bytes_unref (bytes);
            }
            if (!success) {
                nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
                return FALSE;
            }
        } else if ((key_len == 5) || (key_len == 13)) {
            if (!nm_supplicant_config_add_option (self, name, key, key_len, TRUE)) {
                nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
                return FALSE;
            }
        } else {
            nm_log_warn (LOGD_SUPPLICANT, "Invalid WEP key '%s'", name);
            return FALSE;
        }
    } else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) {
        guint8 digest[16];
        size_t digest_len = sizeof (digest);

        success = wep128_passphrase_hash (key, key_len, digest, &digest_len);
        if (success)
            success = nm_supplicant_config_add_option (self, name, (const char *) digest, digest_len, TRUE);
        if (!success) {
            nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
            return FALSE;
        }
    }

    return TRUE;
}
static gboolean
add_wep_key (NMSupplicantConfig *self,
             const char *key,
             const char *name,
             NMWepKeyType wep_type,
             GError **error)
{
	size_t key_len = key ? strlen (key) : 0;

	if (!key || !key_len)
		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)) {
			gs_unref_bytes GBytes *bytes = NULL;

			bytes = nm_utils_hexstr2bin (key);
			if (!bytes) {
				g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
				             "cannot add wep-key %s to suplicant config because key is not hex",
				             name);
				return FALSE;
			}
			if (!nm_supplicant_config_add_option (self,
			                                      name,
			                                      g_bytes_get_data (bytes, NULL),
			                                      g_bytes_get_size (bytes),
			                                      TRUE,
			                                      error))
				return FALSE;
		} else if ((key_len == 5) || (key_len == 13)) {
			if (!nm_supplicant_config_add_option (self, name, key, key_len, TRUE, error))
				return FALSE;
		} else {
			g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
			             "Cannot add wep-key %s to suplicant config because key-length %u is invalid",
			             name, (guint) key_len);
			return FALSE;
		}
	} else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) {
		guint8 digest[16];
		size_t digest_len = sizeof (digest);

		wep128_passphrase_hash (key, key_len, digest, &digest_len);
		if (!nm_supplicant_config_add_option (self, name, (const char *) digest, digest_len, TRUE, error))
			return FALSE;
	}

	return TRUE;
}
static gboolean
add_string_val (NMSupplicantConfig *self,
                const char *field,
                const char *name,
                gboolean ucase,
                gboolean secret,
                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), secret, error);
	}
	return TRUE;
}
static gboolean
add_string_val (NMSupplicantConfig *self,
                const char *field,
                const char *name,
                gboolean ucase,
                gboolean secret)
{
	gboolean success;
	char *value;

	if (!field)
		return TRUE;

	value = ucase ? g_ascii_strup (field, -1) : g_strdup (field);
	success = nm_supplicant_config_add_option (self, name, value, strlen (field), secret);
	if (!success)
		nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
	g_free (value);
	return success;
}
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;

	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, TRUE, 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),
			                                      TRUE,
			                                      error))
				return FALSE;
		}
	}
	value = nm_setting_802_1x_get_pin (setting);
	if (!add_string_val (self, value, "pin", FALSE, TRUE, error))
		return FALSE;

	if (wired) {
		if (!add_string_val (self, "IEEE8021X", "key_mgmt", FALSE, FALSE, error))
			return FALSE;
		/* Wired 802.1x must always use eapol_flags=0 */
		if (!add_string_val (self, "0", "eapol_flags", FALSE, FALSE, error))
			return FALSE;
		priv->ap_scan = 0;
	}

	if (!ADD_STRING_LIST_VAL (self, setting, 802_1x, eap_method, eap_methods, "eap", ' ', TRUE, FALSE, error))
		return FALSE;

	/* Check EAP method for special handling: PEAP + GTC, FAST */
	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 (method && (strcasecmp (method, "fast") == 0)) {
			fast = TRUE;
			priv->fast_required = TRUE;
		}
	}

	/* 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, FALSE, error))
		return FALSE;

	phase1 = g_string_new (NULL);
	peapver = nm_setting_802_1x_get_phase1_peapver (setting);
	if (peapver) {
		if (!strcmp (peapver, "0"))
			g_string_append (phase1, "peapver=0");
		else if (!strcmp (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 (strcmp (value, "0") != 0)
			fast_provisoning_allowed = TRUE;
	}

	if (phase1->len) {
		if (!add_string_val (self, phase1->str, "phase1", FALSE, FALSE, 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, FALSE, 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, FALSE, 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, FALSE, 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 ? ca_path_override : path;
	if (path) {
		if (!add_string_val (self, path, "ca_path", FALSE, FALSE, error))
			return FALSE;
	}

	/* Phase2 CA path */
	path = nm_setting_802_1x_get_phase2_ca_path (setting);
	path = ca_path_override ? ca_path_override : path;
	if (path) {
		if (!add_string_val (self, path, "ca_path2", FALSE, FALSE, error))
			return FALSE;
	}

	/* CA certificate */
	if (ca_cert_override) {
		if (!add_string_val (self, ca_cert_override, "ca_cert", FALSE, FALSE, 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, FALSE, 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, FALSE, 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, FALSE, 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, FALSE, error))
		return FALSE;
	value = nm_setting_802_1x_get_phase2_subject_match (setting);
	if (!add_string_val (self, value, "subject_match2", FALSE, FALSE, error))
		return FALSE;

	/* altSubjectName match */
	if (!ADD_STRING_LIST_VAL (self, setting, 802_1x, altsubject_match, altsubject_matches, "altsubject_match", ';', FALSE, FALSE, error))
		return FALSE;
	if (!ADD_STRING_LIST_VAL (self, setting, 802_1x, phase2_altsubject_match, phase2_altsubject_matches, "altsubject_match2", ';', FALSE, FALSE, 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, FALSE, error))
		return FALSE;
	value = nm_setting_802_1x_get_phase2_domain_suffix_match (setting);
	if (!add_string_val (self, value, "domain_suffix_match2", FALSE, FALSE, 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, FALSE, 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, TRUE, 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, FALSE, 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, FALSE, 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, TRUE, 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, FALSE, error))
					return FALSE;
				break;
			default:
				break;
			}
		}
	}

	value = nm_setting_802_1x_get_identity (setting);
	if (!add_string_val (self, value, "identity", FALSE, FALSE, error))
		return FALSE;
	value = nm_setting_802_1x_get_anonymous_identity (setting);
	if (!add_string_val (self, value, "anonymous_identity", FALSE, FALSE, 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,
                                                    GError **error)
{
	const char *key_mgmt, *auth_alg;
	const char *psk;

	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);

	key_mgmt = nm_setting_wireless_security_get_key_mgmt (setting);
	if (!add_string_val (self, key_mgmt, "key_mgmt", TRUE, FALSE, error))
		return FALSE;

	auth_alg = nm_setting_wireless_security_get_auth_alg (setting);
	if (!add_string_val (self, auth_alg, "auth_alg", TRUE, FALSE, error))
		return FALSE;

	psk = nm_setting_wireless_security_get_psk (setting);
	if (psk) {
		size_t psk_len = strlen (psk);

		if (psk_len == 64) {
			gs_unref_bytes GBytes *bytes = NULL;

			/* Hex PSK */
			bytes = nm_utils_hexstr2bin (psk);
			if (!bytes) {
				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",
			                                      g_bytes_get_data (bytes, NULL),
			                                      g_bytes_get_size (bytes),
			                                      TRUE,
			                                      error))
				return FALSE;
		} else if (psk_len >= 8 && psk_len <= 63) {
			/* Use 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, TYPE_STRING, TRUE, 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;
		}
	}

	/* Only WPA-specific things when using WPA */
	if (   !strcmp (key_mgmt, "wpa-none")
	    || !strcmp (key_mgmt, "wpa-psk")
	    || !strcmp (key_mgmt, "wpa-eap")) {
		if (!ADD_STRING_LIST_VAL (self, setting, wireless_security, proto, protos, "proto", ' ', TRUE, FALSE, error))
			return FALSE;
		if (!ADD_STRING_LIST_VAL (self, setting, wireless_security, pairwise, pairwise, "pairwise", ' ', TRUE, FALSE, error))
			return FALSE;
		if (!ADD_STRING_LIST_VAL (self, setting, wireless_security, group, groups, "group", ' ', TRUE, FALSE, error))
			return FALSE;
	}

	/* WEP keys if required */
	if (!strcmp (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, FALSE, error))
				return FALSE;
		}
	}

	if (auth_alg && !strcmp (auth_alg, "leap")) {
		/* LEAP */
		if (!strcmp (key_mgmt, "ieee8021x")) {
			const char *tmp;

			tmp = nm_setting_wireless_security_get_leap_username (setting);
			if (!add_string_val (self, tmp, "identity", FALSE, FALSE, error))
				return FALSE;

			tmp = nm_setting_wireless_security_get_leap_password (setting);
			if (!add_string_val (self, tmp, "password", FALSE, TRUE, error))
				return FALSE;

			if (!add_string_val (self, "leap", "eap", TRUE, FALSE, 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 (!strcmp (key_mgmt, "ieee8021x") || !strcmp (key_mgmt, "wpa-eap")) {
			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 (!strcmp (key_mgmt, "wpa-eap")) {
			/* If using WPA Enterprise, enable optimized background scanning
			 * to ensure roaming within an ESS works well.
			 */
			if (!nm_supplicant_config_add_option (self, "bgscan", "simple:30:-65:300", -1, FALSE, error))
				return FALSE;

			/* 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, FALSE, 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;
	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 = (mode && !strcmp (mode, "adhoc")) ? TRUE : FALSE;
	is_ap = (mode && !strcmp (mode, "ap")) ? TRUE : FALSE;
	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),
	                                      FALSE,
	                                      error))
		return FALSE;

	if (is_adhoc) {
		if (!nm_supplicant_config_add_option (self, "mode", "1", -1, FALSE, error))
			return FALSE;
	}

	if (is_ap) {
		if (!nm_supplicant_config_add_option (self, "mode", "2", -1, FALSE, error))
			return FALSE;
	}

	if ((is_adhoc || is_ap) && 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, FALSE, error))
			return FALSE;
	}

	/* Except for Ad-Hoc and Hotspot, request that the driver probe for the
	 * specific SSID we want to associate with.
	 */
	if (!(is_adhoc || is_ap)) {
		if (!nm_supplicant_config_add_option (self, "scan_ssid", "1", -1, FALSE, error))
			return FALSE;
	}

	bssid = nm_setting_wireless_get_bssid (setting);
	if (bssid) {
		if (!nm_supplicant_config_add_option (self, "bssid",
		                                      bssid, strlen (bssid),
		                                      FALSE,
		                                      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, FALSE, error))
				return FALSE;
		} else {
			const char *freqs = NULL;

			if (!strcmp (band, "a"))
				freqs = wifi_freqs_to_string (FALSE);
			else if (!strcmp (band, "bg"))
				freqs = wifi_freqs_to_string (TRUE);

			if (freqs && !nm_supplicant_config_add_option (self, "freq_list", freqs, strlen (freqs), FALSE, 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, FALSE, error);
}
gboolean
nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
                                        NMSetting8021x *setting,
                                        const char *con_uuid,
                                        gboolean wired)
{
	NMSupplicantConfigPrivate *priv;
	char *tmp;
	const char *peapver, *value, *path;
	gboolean success, added;
	GString *phase1, *phase2;
	GBytes *bytes;
	gboolean fast = FALSE;
	guint32 i, num_eap;
	gboolean fast_provisoning_allowed = FALSE;

	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, TRUE))
			return FALSE;
	} else {
		bytes = nm_setting_802_1x_get_password_raw (setting);
		if (bytes) {
			success = nm_supplicant_config_add_option (self,
			                                           "password",
			                                           (const char *) g_bytes_get_data (bytes, NULL),
			                                           g_bytes_get_size (bytes),
			                                           TRUE);
			if (!success) {
				nm_log_warn (LOGD_SUPPLICANT, "Error adding password-raw to supplicant config.");
				return FALSE;
			}
		}
	}
	value = nm_setting_802_1x_get_pin (setting);
	if (!add_string_val (self, value, "pin", FALSE, TRUE))
		return FALSE;

	if (wired) {
		if (!add_string_val (self, "IEEE8021X", "key_mgmt", FALSE, FALSE))
			return FALSE;
		/* Wired 802.1x must always use eapol_flags=0 */
		if (!add_string_val (self, "0", "eapol_flags", FALSE, FALSE))
			return FALSE;
		nm_supplicant_config_set_ap_scan (self, 0);
	}

	ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", ' ', TRUE, FALSE);

	/* Check EAP method for special handling: PEAP + GTC, FAST */
	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 (method && (strcasecmp (method, "fast") == 0)) {
			fast = TRUE;
			priv->fast_required = TRUE;
		}
	}

	/* Drop the fragment size a bit for better compatibility */
	if (!nm_supplicant_config_add_option (self, "fragment_size", "1300", -1, FALSE))
		return FALSE;

	phase1 = g_string_new (NULL);
	peapver = nm_setting_802_1x_get_phase1_peapver (setting);
	if (peapver) {
		if (!strcmp (peapver, "0"))
			g_string_append (phase1, "peapver=0");
		else if (!strcmp (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 (strcmp (value, "0") != 0)
			fast_provisoning_allowed = TRUE;
	}

	if (phase1->len) {
		if (!add_string_val (self, phase1->str, "phase1", FALSE, FALSE)) {
			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, FALSE)) {
			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, FALSE))
			return FALSE;
	} else {
		/* PAC file is not specified.
		 * If provisioning is allowed, use an blob format.
		 */
		if (fast_provisoning_allowed) {
			char *blob_name = g_strdup_printf ("blob://pac-blob-%s", con_uuid);
			if (!add_string_val (self, blob_name, "pac_file", FALSE, FALSE)) {
				g_free (blob_name);
				return FALSE;
			}
			g_free (blob_name);
		} else {
			/* This is only error for EAP-FAST; don't disturb other methods. */
			if (fast) {
				nm_log_err (LOGD_SUPPLICANT, "EAP-FAST error: no PAC file provided and "
				                              "automatic PAC provisioning is disabled.");
				return FALSE;
			}
		}
	}

	/* CA path */
	path = nm_setting_802_1x_get_ca_path (setting);
	if (nm_setting_802_1x_get_system_ca_certs (setting))
		path = SYSTEM_CA_PATH;
	if (path) {
		if (!add_string_val (self, path, "ca_path", FALSE, FALSE))
			return FALSE;
	}

	/* Phase2 CA path */
	path = nm_setting_802_1x_get_phase2_ca_path (setting);
	if (nm_setting_802_1x_get_system_ca_certs (setting))
		path = SYSTEM_CA_PATH;
	if (path) {
		if (!add_string_val (self, path, "ca_path2", FALSE, FALSE))
			return FALSE;
	}

	/* CA certificate */
	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);
		ADD_BLOB_VAL (bytes, "ca_cert", con_uuid);
		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, FALSE))
			return FALSE;
		break;
	default:
		break;
	}

	/* Phase 2 CA certificate */
	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);
		ADD_BLOB_VAL (bytes, "ca_cert2", con_uuid);
		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, FALSE))
			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, FALSE))
		return FALSE;
	value = nm_setting_802_1x_get_phase2_subject_match (setting);
	if (!add_string_val (self, value, "subject_match2", FALSE, FALSE))
		return FALSE;

	/* altSubjectName match */
	ADD_STRING_LIST_VAL (setting, 802_1x, altsubject_match, altsubject_matches, "altsubject_match", ';', FALSE, FALSE);
	ADD_STRING_LIST_VAL (setting, 802_1x, phase2_altsubject_match, phase2_altsubject_matches, "altsubject_match2", ';', FALSE, 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);
		ADD_BLOB_VAL (bytes, "private_key", con_uuid);
		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, FALSE))
			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, TRUE))
				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);
				ADD_BLOB_VAL (bytes, "client_cert", con_uuid);
				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, FALSE))
					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);
		ADD_BLOB_VAL (bytes, "private_key2", con_uuid);
		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, FALSE))
			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, TRUE))
				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);
				ADD_BLOB_VAL (bytes, "client_cert2", con_uuid);
				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, FALSE))
					return FALSE;
				break;
			default:
				break;
			}
		}
	}

	value = nm_setting_802_1x_get_identity (setting);
	if (!add_string_val (self, value, "identity", FALSE, FALSE))
		return FALSE;
	value = nm_setting_802_1x_get_anonymous_identity (setting);
	if (!add_string_val (self, value, "anonymous_identity", FALSE, FALSE))
		return FALSE;

	return TRUE;
}
gboolean
nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
                                           NMSettingWireless * setting,
                                           guint32 fixed_freq)
{
	NMSupplicantConfigPrivate *priv;
	gboolean is_adhoc, is_ap;
	const char *mode, *band;
	GBytes *ssid;
	const char *bssid;

	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
	g_return_val_if_fail (setting != NULL, FALSE);

	priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);

	mode = nm_setting_wireless_get_mode (setting);
	is_adhoc = (mode && !strcmp (mode, "adhoc")) ? TRUE : FALSE;
	is_ap = (mode && !strcmp (mode, "ap")) ? TRUE : FALSE;
	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),
	                                      FALSE)) {
		nm_log_warn (LOGD_SUPPLICANT, "Error adding SSID to supplicant config.");
		return FALSE;
	}

	if (is_adhoc) {
		if (!nm_supplicant_config_add_option (self, "mode", "1", -1, FALSE)) {
			nm_log_warn (LOGD_SUPPLICANT, "Error adding mode=1 (adhoc) to supplicant config.");
			return FALSE;
		}
	}

	if (is_ap) {
		if (!nm_supplicant_config_add_option (self, "mode", "2", -1, FALSE)) {
			nm_log_warn (LOGD_SUPPLICANT, "Error adding mode=2 (ap) to supplicant config.");
			return FALSE;
		}
	}

	if ((is_adhoc || is_ap) && fixed_freq) {
		char *str_freq;

		str_freq = g_strdup_printf ("%u", fixed_freq);
		if (!nm_supplicant_config_add_option (self, "frequency", str_freq, -1, FALSE)) {
			g_free (str_freq);
			nm_log_warn (LOGD_SUPPLICANT, "Error adding Ad-Hoc/AP frequency to supplicant config.");
			return FALSE;
		}
		g_free (str_freq);
	}

	/* Except for Ad-Hoc and Hotspot, request that the driver probe for the
	 * specific SSID we want to associate with.
	 */
	if (!(is_adhoc || is_ap)) {
		if (!nm_supplicant_config_add_option (self, "scan_ssid", "1", -1, FALSE))
			return FALSE;
	}

	bssid = nm_setting_wireless_get_bssid (setting);
	if (bssid) {
		if (!nm_supplicant_config_add_option (self, "bssid",
		                                      bssid, strlen (bssid),
		                                      FALSE)) {
			nm_log_warn (LOGD_SUPPLICANT, "Error adding BSSID to supplicant config.");
			return FALSE;
		}
	}

	band = nm_setting_wireless_get_band (setting);
	if (band) {
		const char *freqs = NULL;

		if (!strcmp (band, "a"))
			freqs = FIVE_GHZ_FREQS;
		else if (!strcmp (band, "bg"))
			freqs = TWO_GHZ_FREQS;

		if (freqs && !nm_supplicant_config_add_option (self, "freq_list", freqs, strlen (freqs), FALSE)) {
			nm_log_warn (LOGD_SUPPLICANT, "Error adding frequency list/band to supplicant config.");
			return FALSE;
		}
	}

	// FIXME: channel config item
	
	return TRUE;
}