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;
}
static gboolean
verify_no_wpa (NMSettingWirelessSecurity *s_wsec,
               const char *tag,
               GError **error)
{
	const char *key_mgmt;
	int n, i;

	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
	if (key_mgmt && !strncmp (key_mgmt, "wpa", 3)) {
		g_set_error (error,
		             NM_SETTING_WIRELESS_SECURITY_ERROR,
		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
		             "%s incompatible with any WPA key management", tag);
		return FALSE;
	}

	if (nm_setting_wireless_security_get_num_protos (s_wsec)) {
		g_set_error (error,
		             NM_SETTING_WIRELESS_SECURITY_ERROR,
		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
		             "%s incompatible with any 'proto' setting", tag);
		return FALSE;
	}

	n = nm_setting_wireless_security_get_num_pairwise (s_wsec);
	for (i = 0; i < n; i++) {
		const char *pw;

		pw = nm_setting_wireless_security_get_pairwise (s_wsec, i);
		if (strcmp (pw, "wep40") && strcmp (pw, "wep104")) {
			g_set_error (error,
			             NM_SETTING_WIRELESS_SECURITY_ERROR,
			             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
			             "%s is incompatible with WPA pairwise ciphers", tag);
			return FALSE;
		}
	}

	n = nm_setting_wireless_security_get_num_groups (s_wsec);
	for (i = 0; i < n; i++) {
		const char *gr;

		gr = nm_setting_wireless_security_get_group (s_wsec, i);
		if (strcmp (gr, "wep40") && strcmp (gr, "wep104")) {
			g_set_error (error,
			             NM_SETTING_WIRELESS_SECURITY_ERROR,
			             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
			             "%s is incompatible with WPA group ciphers", tag);
			return FALSE;
		}
	}

	if (nm_setting_wireless_security_get_psk (s_wsec)) {
		g_set_error (error,
		             NM_SETTING_WIRELESS_SECURITY_ERROR,
		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
		             "%s is incompatible with a WPA Pre-Shared Key", tag);
		return FALSE;
	}

	return TRUE;
}