1    	/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2    	/* NetworkManager -- Network link manager
3    	 *
4    	 * This program is free software; you can redistribute it and/or modify
5    	 * it under the terms of the GNU General Public License as published by
6    	 * the Free Software Foundation; either version 2 of the License, or
7    	 * (at your option) any later version.
8    	 *
9    	 * This program is distributed in the hope that it will be useful,
10   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   	 * GNU General Public License for more details.
13   	 *
14   	 * You should have received a copy of the GNU General Public License along
15   	 * with this program; if not, write to the Free Software Foundation, Inc.,
16   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17   	 *
18   	 * Copyright (C) 2006 - 2012 Red Hat, Inc.
19   	 * Copyright (C) 2007 - 2008 Novell, Inc.
20   	 */
21   	
22   	#ifdef HAVE_CONFIG_H
23   	#include <config.h>
24   	#endif
25   	
26   	#include <string.h>
27   	#include <stdlib.h>
28   	#include <glib.h>
29   	#include <netinet/ether.h>
30   	#include <dbus/dbus-glib.h>
31   	
32   	#include "nm-supplicant-config.h"
33   	#include "nm-supplicant-settings-verify.h"
34   	#include "nm-logging.h"
35   	#include "nm-setting.h"
36   	#include "NetworkManagerUtils.h"
37   	#include "nm-utils.h"
38   	
39   	#define NM_SUPPLICANT_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
40   	                                             NM_TYPE_SUPPLICANT_CONFIG, \
41   	                                             NMSupplicantConfigPrivate))
42   	
43   	G_DEFINE_TYPE (NMSupplicantConfig, nm_supplicant_config, G_TYPE_OBJECT)
44   	
45   	typedef struct {
46   		char *value;
47   		guint32 len;	
48   		OptType type;
49   	} ConfigOption;
50   	
51   	typedef struct
52   	{
53   		GHashTable *config;
54   		GHashTable *blobs;
55   		guint32    ap_scan;
56   		gboolean   fast_required;
57   		gboolean   dispose_has_run;
58   	} NMSupplicantConfigPrivate;
59   	
60   	NMSupplicantConfig *
61   	nm_supplicant_config_new (void)
62   	{
63   		return g_object_new (NM_TYPE_SUPPLICANT_CONFIG, NULL);
64   	}
65   	
66   	static void
67   	config_option_free (ConfigOption *opt)
68   	{
69   		g_free (opt->value);
70   		g_slice_free (ConfigOption, opt);
71   	}
72   	
73   	static void
74   	blob_free (GByteArray *array)
75   	{
76   		g_byte_array_free (array, TRUE);
77   	}
78   	
79   	static void
80   	nm_supplicant_config_init (NMSupplicantConfig * self)
81   	{
82   		NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
83   	
84   		priv->config = g_hash_table_new_full (g_str_hash, g_str_equal,
85   		                                      (GDestroyNotify) g_free,
86   		                                      (GDestroyNotify) config_option_free);
87   	
88   		priv->blobs = g_hash_table_new_full (g_str_hash, g_str_equal,
89   		                                     (GDestroyNotify) g_free,
90   		                                     (GDestroyNotify) blob_free);
91   	
92   		priv->ap_scan = 1;
93   		priv->dispose_has_run = FALSE;
94   	}
95   	
96   	static gboolean
97   	nm_supplicant_config_add_option_with_type (NMSupplicantConfig *self,
98   	                                           const char *key,
99   	                                           const char *value,
100  	                                           gint32 len,
101  	                                           OptType opt_type,
102  	                                           gboolean secret)
103  	{
104  		NMSupplicantConfigPrivate *priv;
105  		ConfigOption *old_opt;
106  		ConfigOption *opt;
107  		OptType type;
108  	
109  		g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
110  		g_return_val_if_fail (key != NULL, FALSE);
111  		g_return_val_if_fail (value != NULL, FALSE);
112  	
113  		priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
114  	
115  		if (len < 0)
116  			len = strlen (value);
117  	
118  		if (opt_type != TYPE_INVALID)
119  			type = opt_type;
120  		else {
121  			type = nm_supplicant_settings_verify_setting (key, value, len);
122  			if (type == TYPE_INVALID) {
123  				char buf[255];
124  				memset (&buf[0], 0, sizeof (buf));
125  				memcpy (&buf[0], value, len > 254 ? 254 : len);
126  				nm_log_warn (LOGD_SUPPLICANT, "Key '%s' and/or value '%s' invalid.", key, secret ? "<omitted>" : buf);
127  				return FALSE;
128  			}
129  		}
130  	
131  		old_opt = (ConfigOption *) g_hash_table_lookup (priv->config, key);
132  		if (old_opt) {
133  			nm_log_warn (LOGD_SUPPLICANT, "Key '%s' already in table.", key);
134  			return FALSE;
135  		}
136  	
137  		opt = g_slice_new0 (ConfigOption);
138  		opt->value = g_malloc0 ((sizeof (char) * len) + 1);
139  		memcpy (opt->value, value, len);
140  	
141  		opt->len = len;
142  		opt->type = type;	
143  	
144  		{
145  			char buf[255];
146  			memset (&buf[0], 0, sizeof (buf));
147  			memcpy (&buf[0], opt->value, opt->len > 254 ? 254 : opt->len);
148  			nm_log_info (LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, secret ? "<omitted>" : &buf[0]);
149  		}
150  	
151  		g_hash_table_insert (priv->config, g_strdup (key), opt);
152  	
153  		return TRUE;
154  	}
155  	
156  	static gboolean
157  	nm_supplicant_config_add_option (NMSupplicantConfig *self,
158  	                                 const char *key,
159  	                                 const char *value,
160  	                                 gint32 len,
161  	                                 gboolean secret)
162  	{
163  		return nm_supplicant_config_add_option_with_type (self, key, value, len, TYPE_INVALID, secret);
164  	}
165  	
166  	static gboolean
167  	nm_supplicant_config_add_blob (NMSupplicantConfig *self,
168  	                               const char *key,
169  	                               const GByteArray *value,
170  	                               const char *blobid)
171  	{
172  		NMSupplicantConfigPrivate *priv;
173  		ConfigOption *old_opt;
174  		ConfigOption *opt;
175  		OptType type;
176  		GByteArray *blob;
177  	
178  		g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
179  		g_return_val_if_fail (key != NULL, FALSE);
180  		g_return_val_if_fail (value != NULL, FALSE);
181  		g_return_val_if_fail (value->len > 0, FALSE);
182  		g_return_val_if_fail (blobid != NULL, FALSE);
183  	
184  		priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
185  	
186  		type = nm_supplicant_settings_verify_setting (key, (const char *) value->data, value->len);
187  		if (type == TYPE_INVALID) {
188  			nm_log_warn (LOGD_SUPPLICANT, "Key '%s' and/or it's contained value is invalid.", key);
189  			return FALSE;
190  		}
191  	
192  		old_opt = (ConfigOption *) g_hash_table_lookup (priv->config, key);
193  		if (old_opt) {
194  			nm_log_warn (LOGD_SUPPLICANT, "Key '%s' already in table.", key);
195  			return FALSE;
196  		}
197  	
198  		blob = g_byte_array_sized_new (value->len);
199  		g_byte_array_append (blob, value->data, value->len);
200  	
201  		opt = g_slice_new0 (ConfigOption);
202  		opt->value = g_strdup_printf ("blob://%s", blobid);
203  		opt->len = strlen (opt->value);
204  		opt->type = type;	
205  	
206  		nm_log_info (LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, opt->value);
207  	
208  		g_hash_table_insert (priv->config, g_strdup (key), opt);
209  		g_hash_table_insert (priv->blobs, g_strdup (blobid), blob);
210  	
211  		return TRUE;
212  	}
213  	
214  	static void
215  	nm_supplicant_config_finalize (GObject *object)
216  	{
217  		/* Complete object destruction */
218  		g_hash_table_destroy (NM_SUPPLICANT_CONFIG_GET_PRIVATE (object)->config);
219  		g_hash_table_destroy (NM_SUPPLICANT_CONFIG_GET_PRIVATE (object)->blobs);
220  	
221  		/* Chain up to the parent class */
222  		G_OBJECT_CLASS (nm_supplicant_config_parent_class)->finalize (object);
223  	}
224  	
225  	
226  	static void
227  	nm_supplicant_config_class_init (NMSupplicantConfigClass *klass)
228  	{
229  		GObjectClass *object_class = G_OBJECT_CLASS (klass);
230  	
231  		object_class->finalize = nm_supplicant_config_finalize;
232  	
233  		g_type_class_add_private (object_class, sizeof (NMSupplicantConfigPrivate));
234  	}
235  	
236  	guint32
237  	nm_supplicant_config_get_ap_scan (NMSupplicantConfig * self)
238  	{
239  		g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), 1);
240  	
241  		return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan;
242  	}
243  	
244  	void
245  	nm_supplicant_config_set_ap_scan (NMSupplicantConfig * self,
246  	                                  guint32 ap_scan)
247  	{
248  		g_return_if_fail (NM_IS_SUPPLICANT_CONFIG (self));
(1) Event unsigned_compare: This greater-than-or-equal-to-zero comparison of an unsigned value is always true. "ap_scan >= 0U".
249  		g_return_if_fail (ap_scan >= 0 && ap_scan <= 2);
250  	
251  		NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan = ap_scan;
252  	}
253  	
254  	gboolean
255  	nm_supplicant_config_fast_required (NMSupplicantConfig *self)
256  	{
257  		g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
258  	
259  		return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->fast_required;
260  	}
261  	
262  	static void
263  	get_hash_cb (gpointer key, gpointer value, gpointer user_data)
264  	{
265  		ConfigOption *opt = (ConfigOption *) value;
266  		GValue *variant;
267  		GByteArray *array;
268  	
269  		variant = g_slice_new0 (GValue);
270  	
271  		switch (opt->type) {
272  		case TYPE_INT:
273  			g_value_init (variant, G_TYPE_INT);
274  			g_value_set_int (variant, atoi (opt->value));
275  			break;
276  		case TYPE_BYTES:
277  		case TYPE_UTF8:
278  			array = g_byte_array_sized_new (opt->len);
279  			g_byte_array_append (array, (const guint8 *) opt->value, opt->len);
280  			g_value_init (variant, DBUS_TYPE_G_UCHAR_ARRAY);
281  			g_value_set_boxed (variant, array);
282  			g_byte_array_free (array, TRUE);
283  			break;
284  		case TYPE_KEYWORD:
285  		case TYPE_STRING:
286  			g_value_init (variant, G_TYPE_STRING);
287  			g_value_set_string (variant, opt->value);
288  			break;
289  		default:
290  			g_slice_free (GValue, variant);
291  			return;
292  		}
293  	
294  		g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), variant);
295  	}
296  	
297  	static void
298  	destroy_hash_value (gpointer data)
299  	{
300  		GValue *value = (GValue *) data;
301  	
302  		g_value_unset (value);
303  		g_slice_free (GValue, value);
304  	}
305  	
306  	GHashTable *
307  	nm_supplicant_config_get_hash (NMSupplicantConfig * self)
308  	{
309  		NMSupplicantConfigPrivate *priv;
310  		GHashTable *hash;
311  	
312  		g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), NULL);
313  	
314  		hash = g_hash_table_new_full (g_str_hash, g_str_equal,
315  		                              (GDestroyNotify) g_free,
316  		                              destroy_hash_value);
317  	
318  		priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
319  		g_hash_table_foreach (priv->config, get_hash_cb, hash);
320  		return hash;
321  	}
322  	
323  	GHashTable *
324  	nm_supplicant_config_get_blobs (NMSupplicantConfig * self)
325  	{
326  		g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), NULL);
327  	
328  		return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->blobs;
329  	}
330  	
331  	#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
332  	#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5]
333  	
334  	gboolean
335  	nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
336  	                                           NMSettingWireless * setting,
337  	                                           gboolean is_broadcast,
338  	                                           guint32 fixed_freq,
339  	                                           gboolean has_scan_capa_ssid)
340  	{
341  		NMSupplicantConfigPrivate *priv;
342  		gboolean is_adhoc, is_ap;
343  		const char *mode;
344  		const GByteArray *id;
345  	
346  		g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
347  		g_return_val_if_fail (setting != NULL, FALSE);
348  	
349  		priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
350  	
351  		mode = nm_setting_wireless_get_mode (setting);
352  		is_adhoc = (mode && !strcmp (mode, "adhoc")) ? TRUE : FALSE;
353  		is_ap = (mode && !strcmp (mode, "ap")) ? TRUE : FALSE;
354  		if (is_adhoc || is_ap)
355  			priv->ap_scan = 2;
356  		else if (is_broadcast == FALSE) {
357  			/* drivers that support scanning specific SSIDs should use
358  			 * ap_scan=1, while those that do not should use ap_scan=2.
359  			 */
360  			priv->ap_scan = has_scan_capa_ssid ? 1 : 2;
361  		}
362  	
363  		id = nm_setting_wireless_get_ssid (setting);
364  		if (!nm_supplicant_config_add_option (self, "ssid", (char *) id->data, id->len, FALSE)) {
365  			nm_log_warn (LOGD_SUPPLICANT, "Error adding SSID to supplicant config.");
366  			return FALSE;
367  		}
368  	
369  		if (is_adhoc) {
370  			if (!nm_supplicant_config_add_option (self, "mode", "1", -1, FALSE)) {
371  				nm_log_warn (LOGD_SUPPLICANT, "Error adding mode=1 (adhoc) to supplicant config.");
372  				return FALSE;
373  			}
374  		}
375  	
376  		if (is_ap) {
377  			if (!nm_supplicant_config_add_option (self, "mode", "2", -1, FALSE)) {
378  				nm_log_warn (LOGD_SUPPLICANT, "Error adding mode=2 (ap) to supplicant config.");
379  				return FALSE;
380  			}
381  		}
382  	
383  		if ((is_adhoc || is_ap) && fixed_freq) {
384  			char *str_freq;
385  	
386  			str_freq = g_strdup_printf ("%u", fixed_freq);
387  			if (!nm_supplicant_config_add_option (self, "frequency", str_freq, -1, FALSE)) {
388  				g_free (str_freq);
389  				nm_log_warn (LOGD_SUPPLICANT, "Error adding Ad-Hoc/AP frequency to supplicant config.");
390  				return FALSE;
391  			}
392  			g_free (str_freq);
393  		}
394  	
395  		/* Except for Ad-Hoc and Hotspot, request that the driver probe for the
396  		 * specific SSID we want to associate with.
397  		 */
398  		if (!(is_adhoc || is_ap)) {
399  			if (!nm_supplicant_config_add_option (self, "scan_ssid", "1", -1, FALSE))
400  				return FALSE;
401  		}
402  	
403  		id = nm_setting_wireless_get_bssid (setting);
404  		if (id && id->len) {
405  			char *str_bssid;
406  	
407  			str_bssid = g_strdup_printf (MAC_FMT, MAC_ARG (id->data));
408  			if (!nm_supplicant_config_add_option (self, "bssid",
409  			                                      str_bssid, strlen (str_bssid),
410  			                                      FALSE)) {
411  				g_free (str_bssid);
412  				nm_log_warn (LOGD_SUPPLICANT, "Error adding BSSID to supplicant config.");
413  				return FALSE;
414  			}
415  			g_free (str_bssid);
416  		}
417  	
418  		// FIXME: band & channel config items
419  		
420  		return TRUE;
421  	}
422  	
423  	static gboolean
424  	add_string_val (NMSupplicantConfig *self,
425  	                const char *field,
426  	                const char *name,
427  	                gboolean ucase,
428  	                gboolean secret)
429  	{
430  		gboolean success;
431  		char *value;
432  	
433  		if (!field)
434  			return TRUE;
435  	
436  		value = ucase ? g_ascii_strup (field, -1) : g_strdup (field);
437  		success = nm_supplicant_config_add_option (self, name, value, strlen (field), secret);
438  		if (!success)
439  			nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
440  		g_free (value);
441  		return success;
442  	}
443  	
444  	#define ADD_STRING_LIST_VAL(setting, setting_name, field, field_plural, name, separator, ucase, secret) \
445  		if (nm_setting_##setting_name##_get_num_##field_plural (setting)) { \
446  			guint32 k; \
447  			GString *str = g_string_new (NULL); \
448  			for (k = 0; k < nm_setting_##setting_name##_get_num_##field_plural (setting); k++) { \
449  				const char *item = nm_setting_##setting_name##_get_##field (setting, k); \
450  				if (!str->len) { \
451  					g_string_append (str, item); \
452  				} else { \
453  					g_string_append_c (str, separator); \
454  					g_string_append (str, item); \
455  				} \
456  			} \
457  			if (ucase) \
458  				g_string_ascii_up (str); \
459  			if (str->len) \
460  				success = nm_supplicant_config_add_option (self, name, str->str, -1, secret); \
461  			else \
462  				success = TRUE; \
463  			g_string_free (str, TRUE); \
464  			if (!success) { \
465  				nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); \
466  				return FALSE; \
467  			} \
468  		}
469  	
470  	static char *
471  	get_blob_id (const char *name, const char *seed_uid)
472  	{
473  		char *uid = g_strdup_printf ("%s-%s", seed_uid, name);
474  		char *p = uid;
475  		while (*p) {
476  			if (*p == '/') *p = '-';
477  			p++;
478  		}
479  		return uid;
480  	}
481  	
482  	#define ADD_BLOB_VAL(field, name, con_uid) \
483  		if (field && field->len) { \
484  			char *uid = get_blob_id (name, con_uid); \
485  			success = nm_supplicant_config_add_blob (self, name, field, uid); \
486  			g_free (uid); \
487  			if (!success) { \
488  				nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); \
489  				return FALSE; \
490  			} \
491  		}
492  	
493  	
494  	static gboolean
495  	wep128_passphrase_hash (const char *input,
496  	                        size_t input_len,
497  	                        guint8 *out_digest,
498  	                        size_t *out_digest_len)
499  	{
500  		GChecksum *sum;
501  		guint8 data[64];
502  		int i;
503  	
504  		g_return_val_if_fail (out_digest != NULL, FALSE);
505  		g_return_val_if_fail (out_digest_len != NULL, FALSE);
506  		g_return_val_if_fail (*out_digest_len >= 16, FALSE);
507  	
508  		/* Get at least 64 bytes by repeating the passphrase into the buffer */
509  		for (i = 0; i < sizeof (data); i++)
510  			data[i] = input[i % input_len];
511  	
512  		sum = g_checksum_new (G_CHECKSUM_MD5);
513  		g_assert (sum);
514  		g_checksum_update (sum, data, sizeof (data));
515  		g_checksum_get_digest (sum, out_digest, out_digest_len);
516  		g_checksum_free (sum);
517  	
518  		g_assert (*out_digest_len == 16);
519  		/* WEP104 keys are 13 bytes in length (26 hex characters) */
520  		*out_digest_len = 13;
521  		return TRUE;
522  	}
523  	
524  	static gboolean
525  	add_wep_key (NMSupplicantConfig *self,
526  	             const char *key,
527  	             const char *name,
528  	             NMWepKeyType wep_type)
529  	{
530  		char *value;
531  		gboolean success;
532  		size_t key_len = key ? strlen (key) : 0;
533  	
534  		if (!key || !key_len)
535  			return TRUE;
536  	
537  		if (   (wep_type == NM_WEP_KEY_TYPE_UNKNOWN)
538  		    || (wep_type == NM_WEP_KEY_TYPE_KEY)) {
539  			if ((key_len == 10) || (key_len == 26)) {
540  				value = nm_utils_hexstr2bin (key, strlen (key));
541  				success = nm_supplicant_config_add_option (self, name, value, key_len / 2, TRUE);
542  				g_free (value);
543  				if (!success) {
544  					nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
545  					return FALSE;
546  				}
547  			} else if ((key_len == 5) || (key_len == 13)) {
548  				if (!nm_supplicant_config_add_option (self, name, key, key_len, TRUE)) {
549  					nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
550  					return FALSE;
551  				}
552  			} else {
553  				nm_log_warn (LOGD_SUPPLICANT, "Invalid WEP key '%s'", name);
554  				return FALSE;
555  			}
556  		} else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) {
557  			guint8 digest[16];
558  			size_t digest_len = sizeof (digest);
559  	
560  			success = wep128_passphrase_hash (key, key_len, digest, &digest_len);
561  			if (success)
562  				success = nm_supplicant_config_add_option (self, name, (const char *) digest, digest_len, TRUE);
563  			if (!success) {
564  				nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
565  				return FALSE;
566  			}
567  		}
568  	
569  		return TRUE;
570  	}
571  	
572  	gboolean
573  	nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
574  	                                                    NMSettingWirelessSecurity *setting,
575  	                                                    NMSetting8021x *setting_8021x,
576  	                                                    const char *con_uuid)
577  	{
578  		char *value;
579  		gboolean success;
580  		const char *key_mgmt, *auth_alg;
581  		const char *psk;
582  	
583  		g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
584  		g_return_val_if_fail (setting != NULL, FALSE);
585  		g_return_val_if_fail (con_uuid != NULL, FALSE);
586  	
587  		key_mgmt = nm_setting_wireless_security_get_key_mgmt (setting);
588  		if (!add_string_val (self, key_mgmt, "key_mgmt", TRUE, FALSE))
589  			return FALSE;
590  	
591  		auth_alg = nm_setting_wireless_security_get_auth_alg (setting);
592  		if (!add_string_val (self, auth_alg, "auth_alg", TRUE, FALSE))
593  			return FALSE;
594  	
595  		psk = nm_setting_wireless_security_get_psk (setting);
596  		if (psk) {
597  			size_t psk_len = strlen (psk);
598  	
599  			if (psk_len == 64) {
600  				/* Hex PSK */
601  				value = nm_utils_hexstr2bin (psk, psk_len);
602  				success = nm_supplicant_config_add_option (self, "psk", value, psk_len / 2, TRUE);
603  				g_free (value);
604  				if (!success) {
605  					nm_log_warn (LOGD_SUPPLICANT, "Error adding 'psk' to supplicant config.");
606  					return FALSE;
607  				}
608  			} else if (psk_len >= 8 && psk_len <= 63) {
609  				/* Use TYPE_STRING here so that it gets pushed to the
610  				 * supplicant as a string, and therefore gets quoted,
611  				 * and therefore the supplicant will interpret it as a
612  				 * passphrase and not a hex key.
613  				 */
614  				if (!nm_supplicant_config_add_option_with_type (self, "psk", psk, -1, TYPE_STRING, TRUE)) {
615  					nm_log_warn (LOGD_SUPPLICANT, "Error adding 'psk' to supplicant config.");
616  					return FALSE;
617  				}
618  			} else {
619  				/* Invalid PSK */
620  				nm_log_warn (LOGD_SUPPLICANT, "Invalid PSK length %u: not between 8 and 63 characters inclusive.", (guint32) psk_len);
621  				return FALSE;
622  			}
623  		}
624  	
625  		/* Only WPA-specific things when using WPA */
626  		if (   !strcmp (key_mgmt, "wpa-none")
627  		    || !strcmp (key_mgmt, "wpa-psk")
628  		    || !strcmp (key_mgmt, "wpa-eap")) {
629  			ADD_STRING_LIST_VAL (setting, wireless_security, proto, protos, "proto", ' ', TRUE, FALSE);
630  			ADD_STRING_LIST_VAL (setting, wireless_security, pairwise, pairwise, "pairwise", ' ', TRUE, FALSE);
631  			ADD_STRING_LIST_VAL (setting, wireless_security, group, groups, "group", ' ', TRUE, FALSE);
632  		}
633  	
634  		/* WEP keys if required */
635  		if (!strcmp (key_mgmt, "none")) {
636  			NMWepKeyType wep_type = nm_setting_wireless_security_get_wep_key_type (setting);
637  			const char *wep0 = nm_setting_wireless_security_get_wep_key (setting, 0);
638  			const char *wep1 = nm_setting_wireless_security_get_wep_key (setting, 1);
639  			const char *wep2 = nm_setting_wireless_security_get_wep_key (setting, 2);
640  			const char *wep3 = nm_setting_wireless_security_get_wep_key (setting, 3);
641  	
642  			if (!add_wep_key (self, wep0, "wep_key0", wep_type))
643  				return FALSE;
644  			if (!add_wep_key (self, wep1, "wep_key1", wep_type))
645  				return FALSE;
646  			if (!add_wep_key (self, wep2, "wep_key2", wep_type))
647  				return FALSE;
648  			if (!add_wep_key (self, wep3, "wep_key3", wep_type))
649  				return FALSE;
650  	
651  			if (wep0 || wep1 || wep2 || wep3) {
652  				value = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (setting));
653  				success = nm_supplicant_config_add_option (self, "wep_tx_keyidx", value, -1, FALSE);
654  				g_free (value);
655  				if (!success) {
656  					nm_log_warn (LOGD_SUPPLICANT, "Error adding wep_tx_keyidx to supplicant config.");
657  					return FALSE;
658  				}
659  			}
660  		}
661  	
662  		if (auth_alg && !strcmp (auth_alg, "leap")) {
663  			/* LEAP */
664  			if (!strcmp (key_mgmt, "ieee8021x")) {
665  				const char *tmp;
666  	
667  				tmp = nm_setting_wireless_security_get_leap_username (setting);
668  				if (!add_string_val (self, tmp, "identity", FALSE, FALSE))
669  					return FALSE;
670  	
671  				tmp = nm_setting_wireless_security_get_leap_password (setting);
672  				if (!add_string_val (self, tmp, "password", FALSE, TRUE))
673  					return FALSE;
674  	
675  				if (!add_string_val (self, "leap", "eap", TRUE, FALSE))
676  					return FALSE;
677  			} else {
678  				return FALSE;
679  			}
680  		} else {
681  			/* 802.1x for Dynamic WEP and WPA-Enterprise */
682  			if (!strcmp (key_mgmt, "ieee8021x") || !strcmp (key_mgmt, "wpa-eap")) {
683  			    if (!setting_8021x)
684  			    	return FALSE;
685  				if (!nm_supplicant_config_add_setting_8021x (self, setting_8021x, con_uuid, FALSE))
686  					return FALSE;
687  			}
688  	
689  			if (!strcmp (key_mgmt, "wpa-eap")) {
690  				/* If using WPA Enterprise, enable optimized background scanning
691  				 * to ensure roaming within an ESS works well.
692  				 */
693  				if (!nm_supplicant_config_add_option (self, "bgscan", "simple:30:-65:300", -1, FALSE))
694  					nm_log_warn (LOGD_SUPPLICANT, "Error enabling background scanning for ESS roaming");
695  	
696  				/* When using WPA-Enterprise, we want to use Proactive Key Caching (also
697  				 * called Opportunistic Key Caching) to avoid full EAP exchanges when
698  				 * roaming between access points in the same mobility group.
699  				 */
700  				if (!nm_supplicant_config_add_option (self, "proactive_key_caching", "1", -1, FALSE))
701  					return FALSE;
702  			}
703  		}
704  	
705  		return TRUE;
706  	}
707  	
708  	gboolean
709  	nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
710  	                                        NMSetting8021x *setting,
711  	                                        const char *con_uuid,
712  	                                        gboolean wired)
713  	{
714  		NMSupplicantConfigPrivate *priv;
715  		char *tmp;
716  		const char *peapver, *value, *path;
717  		gboolean success, added;
718  		GString *phase1, *phase2;
719  		const GByteArray *array;
720  		gboolean fast = FALSE;
721  		guint32 i, num_eap;
722  		gboolean fast_provisoning_allowed = FALSE;
723  	
724  		g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
725  		g_return_val_if_fail (setting != NULL, FALSE);
726  		g_return_val_if_fail (con_uuid != NULL, FALSE);
727  	
728  		priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
729  	
730  		value = nm_setting_802_1x_get_password (setting);
731  		if (value) {
732  			if (!add_string_val (self, value, "password", FALSE, TRUE))
733  				return FALSE;
734  		} else {
735  			array = nm_setting_802_1x_get_password_raw (setting);
736  			if (array) {
737  				success = nm_supplicant_config_add_option (self,
738  				                                           "password",
739  				                                           (const char *)array->data,
740  				                                           array->len,
741  				                                           TRUE);
742  				if (!success) {
743  					nm_log_warn (LOGD_SUPPLICANT, "Error adding password-raw to supplicant config.");
744  					return FALSE;
745  				}
746  			}
747  		}
748  		value = nm_setting_802_1x_get_pin (setting);
749  		if (!add_string_val (self, value, "pin", FALSE, TRUE))
750  			return FALSE;
751  	
752  		if (wired) {
753  			if (!add_string_val (self, "IEEE8021X", "key_mgmt", FALSE, FALSE))
754  				return FALSE;
755  			/* Wired 802.1x must always use eapol_flags=0 */
756  			if (!add_string_val (self, "0", "eapol_flags", FALSE, FALSE))
757  				return FALSE;
758  			nm_supplicant_config_set_ap_scan (self, 0);
759  		}
760  	
761  		ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", ' ', TRUE, FALSE);
762  	
763  		/* Check EAP method for special handling: PEAP + GTC, FAST */
764  		num_eap = nm_setting_802_1x_get_num_eap_methods (setting);
765  		for (i = 0; i < num_eap; i++) {
766  			const char *method = nm_setting_802_1x_get_eap_method (setting, i);
767  	
768  			if (method && (strcasecmp (method, "fast") == 0)) {
769  				fast = TRUE;
770  				priv->fast_required = TRUE;
771  			}
772  		}
773  	
774  		/* Drop the fragment size a bit for better compatibility */
775  		if (!nm_supplicant_config_add_option (self, "fragment_size", "1300", -1, FALSE))
776  			return FALSE;
777  	
778  		phase1 = g_string_new (NULL);
779  		peapver = nm_setting_802_1x_get_phase1_peapver (setting);
780  		if (peapver) {
781  			if (!strcmp (peapver, "0"))
782  				g_string_append (phase1, "peapver=0");
783  			else if (!strcmp (peapver, "1"))
784  				g_string_append (phase1, "peapver=1");
785  		}
786  	
787  		if (nm_setting_802_1x_get_phase1_peaplabel (setting)) {
788  			if (phase1->len)
789  				g_string_append_c (phase1, ' ');
790  			g_string_append_printf (phase1, "peaplabel=%s", nm_setting_802_1x_get_phase1_peaplabel (setting));
791  		}
792  	
793  		value = nm_setting_802_1x_get_phase1_fast_provisioning (setting);
794  		if (value) {
795  			if (phase1->len)
796  				g_string_append_c (phase1, ' ');
797  			g_string_append_printf (phase1, "fast_provisioning=%s", value);
798  			
799  			if (strcmp (value, "0") != 0)
800  				fast_provisoning_allowed = TRUE;
801  		}
802  	
803  		if (phase1->len) {
804  			if (!add_string_val (self, phase1->str, "phase1", FALSE, FALSE)) {
805  				g_string_free (phase1, TRUE);
806  				return FALSE;
807  			}
808  		}
809  		g_string_free (phase1, TRUE);
810  	
811  		phase2 = g_string_new (NULL);
812  		if (nm_setting_802_1x_get_phase2_auth (setting) && !fast_provisoning_allowed) {
813  			tmp = g_ascii_strup (nm_setting_802_1x_get_phase2_auth (setting), -1);
814  			g_string_append_printf (phase2, "auth=%s", tmp);
815  			g_free (tmp);
816  		}
817  	
818  		if (nm_setting_802_1x_get_phase2_autheap (setting)) {
819  			if (phase2->len)
820  				g_string_append_c (phase2, ' ');
821  			tmp = g_ascii_strup (nm_setting_802_1x_get_phase2_autheap (setting), -1);
822  			g_string_append_printf (phase2, "autheap=%s", tmp);
823  			g_free (tmp);
824  		}
825  	
826  		if (phase2->len) {
827  			if (!add_string_val (self, phase2->str, "phase2", FALSE, FALSE)) {
828  				g_string_free (phase2, TRUE);
829  				return FALSE;
830  			}
831  		}
832  		g_string_free (phase2, TRUE);
833  	
834  		/* PAC file */
835  		path = nm_setting_802_1x_get_pac_file (setting);
836  		if (path) {
837  			if (!add_string_val (self, path, "pac_file", FALSE, FALSE))
838  				return FALSE;
839  		} else {
840  			/* PAC file is not specified.
841  			 * If provisioning is allowed, use an blob format.
842  			 */
843  			if (fast_provisoning_allowed) {
844  				char *blob_name = g_strdup_printf ("blob://pac-blob-%s", con_uuid);
845  				if (!add_string_val (self, blob_name, "pac_file", FALSE, FALSE)) {
846  					g_free (blob_name);
847  					return FALSE;
848  				}
849  				g_free (blob_name);
850  			} else {
851  				/* This is only error for EAP-FAST; don't disturb other methods. */
852  				if (fast) {
853  					nm_log_err (LOGD_SUPPLICANT, "EAP-FAST error: no PAC file provided and "
854  					                              "automatic PAC provisioning is disabled.");
855  					return FALSE;
856  				}
857  			}
858  		}
859  	
860  		/* CA path */
861  		path = nm_setting_802_1x_get_ca_path (setting);
862  		if (nm_setting_802_1x_get_system_ca_certs (setting))
863  			path = SYSTEM_CA_PATH;
864  		if (path) {
865  			if (!add_string_val (self, path, "ca_path", FALSE, FALSE))
866  				return FALSE;
867  		}
868  	
869  		/* Phase2 CA path */
870  		path = nm_setting_802_1x_get_phase2_ca_path (setting);
871  		if (nm_setting_802_1x_get_system_ca_certs (setting))
872  			path = SYSTEM_CA_PATH;
873  		if (path) {
874  			if (!add_string_val (self, path, "ca_path2", FALSE, FALSE))
875  				return FALSE;
876  		}
877  	
878  		/* CA certificate */
879  		switch (nm_setting_802_1x_get_ca_cert_scheme (setting)) {
880  		case NM_SETTING_802_1X_CK_SCHEME_BLOB:
881  			array = nm_setting_802_1x_get_ca_cert_blob (setting);
882  			ADD_BLOB_VAL (array, "ca_cert", con_uuid);
883  			break;
884  		case NM_SETTING_802_1X_CK_SCHEME_PATH:
885  			path = nm_setting_802_1x_get_ca_cert_path (setting);
886  			if (!add_string_val (self, path, "ca_cert", FALSE, FALSE))
887  				return FALSE;
888  			break;
889  		default:
890  			break;
891  		}
892  	
893  		/* Phase 2 CA certificate */
894  		switch (nm_setting_802_1x_get_phase2_ca_cert_scheme (setting)) {
895  		case NM_SETTING_802_1X_CK_SCHEME_BLOB:
896  			array = nm_setting_802_1x_get_phase2_ca_cert_blob (setting);
897  			ADD_BLOB_VAL (array, "ca_cert2", con_uuid);
898  			break;
899  		case NM_SETTING_802_1X_CK_SCHEME_PATH:
900  			path = nm_setting_802_1x_get_phase2_ca_cert_path (setting);
901  			if (!add_string_val (self, path, "ca_cert2", FALSE, FALSE))
902  				return FALSE;
903  			break;
904  		default:
905  			break;
906  		}
907  	
908  		/* Subject match */
909  		value = nm_setting_802_1x_get_subject_match (setting);
910  		if (!add_string_val (self, value, "subject_match", FALSE, FALSE))
911  			return FALSE;
912  		value = nm_setting_802_1x_get_phase2_subject_match (setting);
913  		if (!add_string_val (self, value, "subject_match2", FALSE, FALSE))
914  			return FALSE;
915  	
916  		/* altSubjectName match */
917  		ADD_STRING_LIST_VAL (setting, 802_1x, altsubject_match, altsubject_matches, "altsubject_match", ';', FALSE, FALSE);
918  		ADD_STRING_LIST_VAL (setting, 802_1x, phase2_altsubject_match, phase2_altsubject_matches, "altsubject_match2", ';', FALSE, FALSE);
919  	
920  		/* Private key */
921  		added = FALSE;
922  		switch (nm_setting_802_1x_get_private_key_scheme (setting)) {
923  		case NM_SETTING_802_1X_CK_SCHEME_BLOB:
924  			array = nm_setting_802_1x_get_private_key_blob (setting);
925  			ADD_BLOB_VAL (array, "private_key", con_uuid);
926  			added = TRUE;
927  			break;
928  		case NM_SETTING_802_1X_CK_SCHEME_PATH:
929  			path = nm_setting_802_1x_get_private_key_path (setting);
930  			if (!add_string_val (self, path, "private_key", FALSE, FALSE))
931  				return FALSE;
932  			added = TRUE;
933  			break;
934  		default:
935  			break;
936  		}
937  	
938  		if (added) {
939  			NMSetting8021xCKFormat format;
940  			NMSetting8021xCKScheme scheme;
941  	
942  			format = nm_setting_802_1x_get_private_key_format (setting);
943  			scheme = nm_setting_802_1x_get_private_key_scheme (setting);
944  	
945  			if (   scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
946  			    || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
947  				/* Only add the private key password for PKCS#12 blobs and
948  				 * all path schemes, since in both of these cases the private key
949  				 * isn't decrypted at all.
950  				 */
951  				value = nm_setting_802_1x_get_private_key_password (setting);
952  				if (!add_string_val (self, value, "private_key_passwd", FALSE, TRUE))
953  					return FALSE;
954  			}
955  	
956  			if (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
957  				/* Only add the client cert if the private key is not PKCS#12, as
958  				 * wpa_supplicant configuration directs us to do.
959  				 */
960  				switch (nm_setting_802_1x_get_client_cert_scheme (setting)) {
961  				case NM_SETTING_802_1X_CK_SCHEME_BLOB:
962  					array = nm_setting_802_1x_get_client_cert_blob (setting);
963  					ADD_BLOB_VAL (array, "client_cert", con_uuid);
964  					break;
965  				case NM_SETTING_802_1X_CK_SCHEME_PATH:
966  					path = nm_setting_802_1x_get_client_cert_path (setting);
967  					if (!add_string_val (self, path, "client_cert", FALSE, FALSE))
968  						return FALSE;
969  					break;
970  				default:
971  					break;
972  				}
973  			}
974  		}
975  	
976  		/* Phase 2 private key */
977  		added = FALSE;
978  		switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) {
979  		case NM_SETTING_802_1X_CK_SCHEME_BLOB:
980  			array = nm_setting_802_1x_get_phase2_private_key_blob (setting);
981  			ADD_BLOB_VAL (array, "private_key2", con_uuid);
982  			added = TRUE;
983  			break;
984  		case NM_SETTING_802_1X_CK_SCHEME_PATH:
985  			path = nm_setting_802_1x_get_phase2_private_key_path (setting);
986  			if (!add_string_val (self, path, "private_key2", FALSE, FALSE))
987  				return FALSE;
988  			added = TRUE;
989  			break;
990  		default:
991  			break;
992  		}
993  	
994  		if (added) {
995  			NMSetting8021xCKFormat format;
996  			NMSetting8021xCKScheme scheme;
997  	
998  			format = nm_setting_802_1x_get_phase2_private_key_format (setting);
999  			scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting);
1000 	
1001 			if (   scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
1002 			    || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
1003 				/* Only add the private key password for PKCS#12 blobs and
1004 				 * all path schemes, since in both of these cases the private key
1005 				 * isn't decrypted at all.
1006 				 */
1007 				value = nm_setting_802_1x_get_phase2_private_key_password (setting);
1008 				if (!add_string_val (self, value, "private_key_passwd2", FALSE, TRUE))
1009 					return FALSE;
1010 			}
1011 	
1012 			if (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
1013 				/* Only add the client cert if the private key is not PKCS#12, as
1014 				 * wpa_supplicant configuration directs us to do.
1015 				 */
1016 				switch (nm_setting_802_1x_get_phase2_client_cert_scheme (setting)) {
1017 				case NM_SETTING_802_1X_CK_SCHEME_BLOB:
1018 					array = nm_setting_802_1x_get_phase2_client_cert_blob (setting);
1019 					ADD_BLOB_VAL (array, "client_cert2", con_uuid);
1020 					break;
1021 				case NM_SETTING_802_1X_CK_SCHEME_PATH:
1022 					path = nm_setting_802_1x_get_phase2_client_cert_path (setting);
1023 					if (!add_string_val (self, path, "client_cert2", FALSE, FALSE))
1024 						return FALSE;
1025 					break;
1026 				default:
1027 					break;
1028 				}
1029 			}
1030 		}
1031 	
1032 		value = nm_setting_802_1x_get_identity (setting);
1033 		if (!add_string_val (self, value, "identity", FALSE, FALSE))
1034 			return FALSE;
1035 		value = nm_setting_802_1x_get_anonymous_identity (setting);
1036 		if (!add_string_val (self, value, "anonymous_identity", FALSE, FALSE))
1037 			return FALSE;
1038 	
1039 		return TRUE;
1040 	}
1041 	
1042 	gboolean
1043 	nm_supplicant_config_add_no_security (NMSupplicantConfig *self)
1044 	{
1045 		return nm_supplicant_config_add_option (self, "key_mgmt", "NONE", -1, FALSE);
1046 	}
1047 	
1048