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