Interworking: Fix SIM/USIM NAI construction
[mech_eap.git] / wpa_supplicant / interworking.c
1 /*
2  * Interworking (IEEE 802.11u)
3  * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #include "includes.h"
10
11 #include "common.h"
12 #include "common/ieee802_11_defs.h"
13 #include "common/gas.h"
14 #include "common/wpa_ctrl.h"
15 #include "drivers/driver.h"
16 #include "eap_common/eap_defs.h"
17 #include "eap_peer/eap_methods.h"
18 #include "wpa_supplicant_i.h"
19 #include "config.h"
20 #include "config_ssid.h"
21 #include "bss.h"
22 #include "scan.h"
23 #include "notify.h"
24 #include "gas_query.h"
25 #include "interworking.h"
26
27
28 #if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
29 #define INTERWORKING_3GPP
30 #else
31 #if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
32 #define INTERWORKING_3GPP
33 #else
34 #if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
35 #define INTERWORKING_3GPP
36 #endif
37 #endif
38 #endif
39
40 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
41
42
43 static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
44                                       struct wpabuf *extra)
45 {
46         struct wpabuf *buf;
47         size_t i;
48         u8 *len_pos;
49
50         buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
51                                          (extra ? wpabuf_len(extra) : 0));
52         if (buf == NULL)
53                 return NULL;
54
55         len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
56         for (i = 0; i < num_ids; i++)
57                 wpabuf_put_le16(buf, info_ids[i]);
58         gas_anqp_set_element_len(buf, len_pos);
59         if (extra)
60                 wpabuf_put_buf(buf, extra);
61
62         gas_anqp_set_len(buf);
63
64         return buf;
65 }
66
67
68 static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
69                                       u8 dialog_token,
70                                       enum gas_query_result result,
71                                       const struct wpabuf *adv_proto,
72                                       const struct wpabuf *resp,
73                                       u16 status_code)
74 {
75         struct wpa_supplicant *wpa_s = ctx;
76
77         anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
78                      status_code);
79         interworking_next_anqp_fetch(wpa_s);
80 }
81
82
83 static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
84                                       struct wpa_bss *bss)
85 {
86         struct wpabuf *buf;
87         int ret = 0;
88         int res;
89         u16 info_ids[] = {
90                 ANQP_CAPABILITY_LIST,
91                 ANQP_VENUE_NAME,
92                 ANQP_NETWORK_AUTH_TYPE,
93                 ANQP_ROAMING_CONSORTIUM,
94                 ANQP_IP_ADDR_TYPE_AVAILABILITY,
95                 ANQP_NAI_REALM,
96                 ANQP_3GPP_CELLULAR_NETWORK,
97                 ANQP_DOMAIN_NAME
98         };
99         struct wpabuf *extra = NULL;
100
101         wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
102                    MAC2STR(bss->bssid));
103
104         buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
105                              extra);
106         wpabuf_free(extra);
107         if (buf == NULL)
108                 return -1;
109
110         res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
111                             interworking_anqp_resp_cb, wpa_s);
112         if (res < 0) {
113                 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
114                 ret = -1;
115         } else
116                 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
117                            "%u", res);
118
119         wpabuf_free(buf);
120         return ret;
121 }
122
123
124 struct nai_realm_eap {
125         u8 method;
126         u8 inner_method;
127         enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
128         u8 cred_type;
129         u8 tunneled_cred_type;
130 };
131
132 struct nai_realm {
133         u8 encoding;
134         char *realm;
135         u8 eap_count;
136         struct nai_realm_eap *eap;
137 };
138
139
140 static void nai_realm_free(struct nai_realm *realms, u16 count)
141 {
142         u16 i;
143
144         if (realms == NULL)
145                 return;
146         for (i = 0; i < count; i++) {
147                 os_free(realms[i].eap);
148                 os_free(realms[i].realm);
149         }
150         os_free(realms);
151 }
152
153
154 static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
155                                       const u8 *end)
156 {
157         u8 elen, auth_count, a;
158         const u8 *e_end;
159
160         if (pos + 3 > end) {
161                 wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
162                 return NULL;
163         }
164
165         elen = *pos++;
166         if (pos + elen > end || elen < 2) {
167                 wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
168                 return NULL;
169         }
170         e_end = pos + elen;
171         e->method = *pos++;
172         auth_count = *pos++;
173         wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
174                    elen, e->method, auth_count);
175
176         for (a = 0; a < auth_count; a++) {
177                 u8 id, len;
178
179                 if (pos + 2 > end || pos + 2 + pos[1] > end) {
180                         wpa_printf(MSG_DEBUG, "No room for Authentication "
181                                    "Parameter subfield");
182                         return NULL;
183                 }
184
185                 id = *pos++;
186                 len = *pos++;
187
188                 switch (id) {
189                 case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
190                         if (len < 1)
191                                 break;
192                         e->inner_non_eap = *pos;
193                         if (e->method != EAP_TYPE_TTLS)
194                                 break;
195                         switch (*pos) {
196                         case NAI_REALM_INNER_NON_EAP_PAP:
197                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
198                                 break;
199                         case NAI_REALM_INNER_NON_EAP_CHAP:
200                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
201                                 break;
202                         case NAI_REALM_INNER_NON_EAP_MSCHAP:
203                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
204                                 break;
205                         case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
206                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
207                                 break;
208                         }
209                         break;
210                 case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
211                         if (len < 1)
212                                 break;
213                         e->inner_method = *pos;
214                         wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
215                                    e->inner_method);
216                         break;
217                 case NAI_REALM_EAP_AUTH_CRED_TYPE:
218                         if (len < 1)
219                                 break;
220                         e->cred_type = *pos;
221                         wpa_printf(MSG_DEBUG, "Credential Type: %u",
222                                    e->cred_type);
223                         break;
224                 case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
225                         if (len < 1)
226                                 break;
227                         e->tunneled_cred_type = *pos;
228                         wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
229                                    "Type: %u", e->tunneled_cred_type);
230                         break;
231                 default:
232                         wpa_printf(MSG_DEBUG, "Unsupported Authentication "
233                                    "Parameter: id=%u len=%u", id, len);
234                         wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
235                                     "Value", pos, len);
236                         break;
237                 }
238
239                 pos += len;
240         }
241
242         return e_end;
243 }
244
245
246 static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
247                                         const u8 *end)
248 {
249         u16 len;
250         const u8 *f_end;
251         u8 realm_len, e;
252
253         if (end - pos < 4) {
254                 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
255                            "fixed fields");
256                 return NULL;
257         }
258
259         len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
260         pos += 2;
261         if (pos + len > end || len < 3) {
262                 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
263                            "(len=%u; left=%u)",
264                            len, (unsigned int) (end - pos));
265                 return NULL;
266         }
267         f_end = pos + len;
268
269         r->encoding = *pos++;
270         realm_len = *pos++;
271         if (pos + realm_len > f_end) {
272                 wpa_printf(MSG_DEBUG, "No room for NAI Realm "
273                            "(len=%u; left=%u)",
274                            realm_len, (unsigned int) (f_end - pos));
275                 return NULL;
276         }
277         wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
278         r->realm = os_malloc(realm_len + 1);
279         if (r->realm == NULL)
280                 return NULL;
281         os_memcpy(r->realm, pos, realm_len);
282         r->realm[realm_len] = '\0';
283         pos += realm_len;
284
285         if (pos + 1 > f_end) {
286                 wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
287                 return NULL;
288         }
289         r->eap_count = *pos++;
290         wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
291         if (pos + r->eap_count * 3 > f_end) {
292                 wpa_printf(MSG_DEBUG, "No room for EAP Methods");
293                 return NULL;
294         }
295         r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap));
296         if (r->eap == NULL)
297                 return NULL;
298
299         for (e = 0; e < r->eap_count; e++) {
300                 pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
301                 if (pos == NULL)
302                         return NULL;
303         }
304
305         return f_end;
306 }
307
308
309 static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
310 {
311         struct nai_realm *realm;
312         const u8 *pos, *end;
313         u16 i, num;
314
315         if (anqp == NULL || wpabuf_len(anqp) < 2)
316                 return NULL;
317
318         pos = wpabuf_head_u8(anqp);
319         end = pos + wpabuf_len(anqp);
320         num = WPA_GET_LE16(pos);
321         wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
322         pos += 2;
323
324         if (num * 5 > end - pos) {
325                 wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
326                            "enough data (%u octets) for that many realms",
327                            num, (unsigned int) (end - pos));
328                 return NULL;
329         }
330
331         realm = os_zalloc(num * sizeof(struct nai_realm));
332         if (realm == NULL)
333                 return NULL;
334
335         for (i = 0; i < num; i++) {
336                 pos = nai_realm_parse_realm(&realm[i], pos, end);
337                 if (pos == NULL) {
338                         nai_realm_free(realm, num);
339                         return NULL;
340                 }
341         }
342
343         *count = num;
344         return realm;
345 }
346
347
348 static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
349 {
350         char *tmp, *pos, *end;
351         int match = 0;
352
353         if (realm->realm == NULL || home_realm == NULL)
354                 return 0;
355
356         if (os_strchr(realm->realm, ';') == NULL)
357                 return os_strcasecmp(realm->realm, home_realm) == 0;
358
359         tmp = os_strdup(realm->realm);
360         if (tmp == NULL)
361                 return 0;
362
363         pos = tmp;
364         while (*pos) {
365                 end = os_strchr(pos, ';');
366                 if (end)
367                         *end = '\0';
368                 if (os_strcasecmp(pos, home_realm) == 0) {
369                         match = 1;
370                         break;
371                 }
372                 if (end == NULL)
373                         break;
374                 pos = end + 1;
375         }
376
377         os_free(tmp);
378
379         return match;
380 }
381
382
383 static int nai_realm_cred_username(struct nai_realm_eap *eap)
384 {
385         if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
386                 return 0; /* method not supported */
387
388         if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
389                 /* Only tunneled methods with username/password supported */
390                 return 0;
391         }
392
393         if (eap->method == EAP_TYPE_PEAP &&
394             eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
395                 return 0;
396
397         if (eap->method == EAP_TYPE_TTLS) {
398                 if (eap->inner_method == 0 && eap->inner_non_eap == 0)
399                         return 0;
400                 if (eap->inner_method &&
401                     eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
402                         return 0;
403                 if (eap->inner_non_eap &&
404                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
405                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
406                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
407                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
408                         return 0;
409         }
410
411         if (eap->inner_method &&
412             eap->inner_method != EAP_TYPE_GTC &&
413             eap->inner_method != EAP_TYPE_MSCHAPV2)
414                 return 0;
415
416         return 1;
417 }
418
419
420 static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
421                                                  struct nai_realm *realm)
422 {
423         u8 e;
424
425         if (cred == NULL ||
426             cred->username == NULL ||
427             cred->username[0] == '\0' ||
428             cred->password == NULL ||
429             cred->password[0] == '\0')
430                 return NULL;
431
432         for (e = 0; e < realm->eap_count; e++) {
433                 struct nai_realm_eap *eap = &realm->eap[e];
434                 if (nai_realm_cred_username(eap))
435                         return eap;
436         }
437
438         return NULL;
439 }
440
441
442 #ifdef INTERWORKING_3GPP
443
444 static int plmn_id_match(struct wpabuf *anqp, const char *imsi)
445 {
446         const char *sep;
447         u8 plmn[3];
448         const u8 *pos, *end;
449         u8 udhl;
450
451         sep = os_strchr(imsi, '-');
452         if (sep == NULL || (sep - imsi != 5 && sep - imsi != 6))
453                 return 0;
454
455         /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
456         plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
457         plmn[1] = imsi[2] - '0';
458         if (sep - imsi == 6)
459                 plmn[1] |= (imsi[5] - '0') << 4;
460         else
461                 plmn[1] |= 0xf0;
462         plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
463
464         if (anqp == NULL)
465                 return 0;
466         pos = wpabuf_head_u8(anqp);
467         end = pos + wpabuf_len(anqp);
468         if (pos + 2 > end)
469                 return 0;
470         if (*pos != 0) {
471                 wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
472                 return 0;
473         }
474         pos++;
475         udhl = *pos++;
476         if (pos + udhl > end) {
477                 wpa_printf(MSG_DEBUG, "Invalid UDHL");
478                 return 0;
479         }
480         end = pos + udhl;
481
482         while (pos + 2 <= end) {
483                 u8 iei, len;
484                 const u8 *l_end;
485                 iei = *pos++;
486                 len = *pos++ & 0x7f;
487                 if (pos + len > end)
488                         break;
489                 l_end = pos + len;
490
491                 if (iei == 0 && len > 0) {
492                         /* PLMN List */
493                         u8 num, i;
494                         num = *pos++;
495                         for (i = 0; i < num; i++) {
496                                 if (pos + 3 > end)
497                                         break;
498                                 if (os_memcmp(pos, plmn, 3) == 0)
499                                         return 1; /* Found matching PLMN */
500                         }
501                 }
502
503                 pos = l_end;
504         }
505
506         return 0;
507 }
508
509
510 static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
511                           char prefix)
512 {
513         const char *sep, *msin;
514         char *end, *pos;
515         size_t msin_len, plmn_len;
516
517         /*
518          * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
519          * Root NAI:
520          * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
521          * <MNC> is zero-padded to three digits in case two-digit MNC is used
522          */
523
524         if (imsi == NULL || os_strlen(imsi) > 16) {
525                 wpa_printf(MSG_DEBUG, "No valid IMSI available");
526                 return -1;
527         }
528         sep = os_strchr(imsi, '-');
529         if (sep == NULL)
530                 return -1;
531         plmn_len = sep - imsi;
532         if (plmn_len != 5 && plmn_len != 6)
533                 return -1;
534         msin = sep + 1;
535         msin_len = os_strlen(msin);
536
537         pos = nai;
538         end = nai + nai_len;
539         if (prefix)
540                 *pos++ = prefix;
541         os_memcpy(pos, imsi, plmn_len);
542         pos += plmn_len;
543         os_memcpy(pos, msin, msin_len);
544         pos += msin_len;
545         pos += os_snprintf(pos, end - pos, "@wlan.mnc");
546         if (plmn_len == 5) {
547                 *pos++ = '0';
548                 *pos++ = imsi[3];
549                 *pos++ = imsi[4];
550         } else {
551                 *pos++ = imsi[3];
552                 *pos++ = imsi[4];
553                 *pos++ = imsi[5];
554         }
555         pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
556                            imsi[0], imsi[1], imsi[2]);
557
558         return 0;
559 }
560
561
562 static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
563 {
564         char nai[100];
565         if (build_root_nai(nai, sizeof(nai), imsi, prefix) < 0)
566                 return -1;
567         return wpa_config_set_quoted(ssid, "identity", nai);
568 }
569
570 #endif /* INTERWORKING_3GPP */
571
572
573 static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
574                                      struct wpa_bss *bss)
575 {
576 #ifdef INTERWORKING_3GPP
577         struct wpa_cred *cred;
578         struct wpa_ssid *ssid;
579         const u8 *ie;
580
581         if (bss->anqp_3gpp == NULL)
582                 return -1;
583
584         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
585                 if (cred->imsi == NULL || !cred->imsi[0] ||
586                     cred->milenage == NULL || !cred->milenage[0])
587                         continue;
588                 if (plmn_id_match(bss->anqp_3gpp, cred->imsi))
589                         break;
590         }
591         if (cred == NULL)
592                 return -1;
593
594         ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
595         if (ie == NULL)
596                 return -1;
597         wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
598                    MAC2STR(bss->bssid));
599
600         ssid = wpa_config_add_network(wpa_s->conf);
601         if (ssid == NULL)
602                 return -1;
603
604         wpas_notify_network_added(wpa_s, ssid);
605         wpa_config_set_network_defaults(ssid);
606         ssid->priority = cred->priority;
607         ssid->temporary = 1;
608         ssid->ssid = os_zalloc(ie[1] + 1);
609         if (ssid->ssid == NULL)
610                 goto fail;
611         os_memcpy(ssid->ssid, ie + 2, ie[1]);
612         ssid->ssid_len = ie[1];
613
614         /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
615         if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) {
616                 wpa_printf(MSG_DEBUG, "EAP-SIM not supported");
617                 goto fail;
618         }
619         if (set_root_nai(ssid, cred->imsi, '1') < 0) {
620                 wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
621                 goto fail;
622         }
623
624         if (cred->milenage && cred->milenage[0]) {
625                 if (wpa_config_set_quoted(ssid, "password",
626                                           cred->milenage) < 0)
627                         goto fail;
628         } else {
629                 /* TODO: PIN */
630                 if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
631                         goto fail;
632         }
633
634         if (cred->password && cred->password[0] &&
635             wpa_config_set_quoted(ssid, "password", cred->password) < 0)
636                 goto fail;
637
638         wpa_config_update_prio_list(wpa_s->conf);
639         wpa_s->disconnected = 0;
640         wpa_s->reassociate = 1;
641         wpa_supplicant_req_scan(wpa_s, 0, 0);
642
643         return 0;
644
645 fail:
646         wpas_notify_network_removed(wpa_s, ssid);
647         wpa_config_remove_network(wpa_s->conf, ssid->id);
648 #endif /* INTERWORKING_3GPP */
649         return -1;
650 }
651
652
653 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
654 {
655         struct wpa_cred *cred;
656         struct wpa_ssid *ssid;
657         struct nai_realm *realm;
658         struct nai_realm_eap *eap = NULL;
659         u16 count, i;
660         char buf[100];
661         const u8 *ie;
662
663         if (wpa_s->conf->cred == NULL || bss == NULL)
664                 return -1;
665         ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
666         if (ie == NULL || ie[1] == 0) {
667                 wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
668                            MACSTR, MAC2STR(bss->bssid));
669                 return -1;
670         }
671
672         realm = nai_realm_parse(bss->anqp_nai_realm, &count);
673         if (realm == NULL) {
674                 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
675                            "Realm list from " MACSTR, MAC2STR(bss->bssid));
676                 count = 0;
677         }
678
679         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
680                 for (i = 0; i < count; i++) {
681                         if (!nai_realm_match(&realm[i], cred->realm))
682                                 continue;
683                         eap = nai_realm_find_eap(cred, &realm[i]);
684                         if (eap)
685                                 break;
686                 }
687                 if (eap)
688                         break;
689         }
690
691         if (!eap) {
692                 if (interworking_connect_3gpp(wpa_s, bss) == 0) {
693                         if (realm)
694                                 nai_realm_free(realm, count);
695                         return 0;
696                 }
697
698                 wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
699                            "and EAP method found for " MACSTR,
700                            MAC2STR(bss->bssid));
701                 nai_realm_free(realm, count);
702                 return -1;
703         }
704
705         wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
706                    MAC2STR(bss->bssid));
707
708         ssid = wpa_config_add_network(wpa_s->conf);
709         if (ssid == NULL) {
710                 nai_realm_free(realm, count);
711                 return -1;
712         }
713         wpas_notify_network_added(wpa_s, ssid);
714         wpa_config_set_network_defaults(ssid);
715         ssid->priority = cred->priority;
716         ssid->temporary = 1;
717         ssid->ssid = os_zalloc(ie[1] + 1);
718         if (ssid->ssid == NULL)
719                 goto fail;
720         os_memcpy(ssid->ssid, ie + 2, ie[1]);
721         ssid->ssid_len = ie[1];
722
723         if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
724                                                      eap->method), 0) < 0)
725                 goto fail;
726
727         if (eap->method == EAP_TYPE_TTLS &&
728             cred->username && cred->username[0]) {
729                 const char *pos;
730                 char *anon;
731                 /* Use anonymous NAI in Phase 1 */
732                 pos = os_strchr(cred->username, '@');
733                 if (pos) {
734                         size_t buflen = 9 + os_strlen(pos) + 1;
735                         anon = os_malloc(buflen);
736                         if (anon == NULL)
737                                 goto fail;
738                         os_snprintf(anon, buflen, "anonymous%s", pos);
739                 } else {
740                         anon = os_strdup("anonymous");
741                         if (anon == NULL)
742                                 goto fail;
743                 }
744                 if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
745                     0) {
746                         os_free(anon);
747                         goto fail;
748                 }
749                 os_free(anon);
750         }
751
752         if (cred->username && cred->username[0] &&
753             wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
754                 goto fail;
755
756         if (cred->password && cred->password[0] &&
757             wpa_config_set_quoted(ssid, "password", cred->password) < 0)
758                 goto fail;
759
760         switch (eap->method) {
761         case EAP_TYPE_TTLS:
762                 if (eap->inner_method) {
763                         os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
764                                     eap_get_name(EAP_VENDOR_IETF,
765                                                  eap->inner_method));
766                         if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
767                                 goto fail;
768                         break;
769                 }
770                 switch (eap->inner_non_eap) {
771                 case NAI_REALM_INNER_NON_EAP_PAP:
772                         if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
773                             0)
774                                 goto fail;
775                         break;
776                 case NAI_REALM_INNER_NON_EAP_CHAP:
777                         if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
778                             < 0)
779                                 goto fail;
780                         break;
781                 case NAI_REALM_INNER_NON_EAP_MSCHAP:
782                         if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
783                                            0) < 0)
784                                 goto fail;
785                         break;
786                 case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
787                         if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
788                                            0) < 0)
789                                 goto fail;
790                         break;
791                 }
792                 break;
793         case EAP_TYPE_PEAP:
794                 os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
795                             eap_get_name(EAP_VENDOR_IETF, eap->inner_method));
796                 if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
797                         goto fail;
798                 break;
799         }
800
801         if (cred->ca_cert && cred->ca_cert[0] &&
802             wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
803                 goto fail;
804
805         nai_realm_free(realm, count);
806
807         wpa_config_update_prio_list(wpa_s->conf);
808         wpa_s->disconnected = 0;
809         wpa_s->reassociate = 1;
810         wpa_supplicant_req_scan(wpa_s, 0, 0);
811
812         return 0;
813
814 fail:
815         wpas_notify_network_removed(wpa_s, ssid);
816         wpa_config_remove_network(wpa_s->conf, ssid->id);
817         nai_realm_free(realm, count);
818         return -1;
819 }
820
821
822 static struct wpa_cred * interworking_credentials_available_3gpp(
823         struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
824 {
825         struct wpa_cred *cred, *selected = NULL;
826         int ret;
827
828 #ifdef INTERWORKING_3GPP
829         if (bss->anqp_3gpp == NULL)
830                 return NULL;
831
832         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
833                 if (cred->imsi == NULL || !cred->imsi[0] ||
834                     cred->milenage == NULL || !cred->milenage[0])
835                         continue;
836
837                 wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
838                            MACSTR, MAC2STR(bss->bssid));
839                 ret = plmn_id_match(bss->anqp_3gpp, cred->imsi);
840                 wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
841                 if (ret) {
842                         if (selected == NULL ||
843                             selected->priority < cred->priority)
844                                 selected = cred;
845                 }
846         }
847 #endif /* INTERWORKING_3GPP */
848         return selected;
849 }
850
851
852 static struct wpa_cred * interworking_credentials_available_realm(
853         struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
854 {
855         struct wpa_cred *cred, *selected = NULL;
856         struct nai_realm *realm;
857         u16 count, i;
858
859         if (bss->anqp_nai_realm == NULL)
860                 return NULL;
861
862         if (wpa_s->conf->cred == NULL)
863                 return NULL;
864
865         wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
866                    MACSTR, MAC2STR(bss->bssid));
867         realm = nai_realm_parse(bss->anqp_nai_realm, &count);
868         if (realm == NULL) {
869                 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
870                            "Realm list from " MACSTR, MAC2STR(bss->bssid));
871                 return NULL;
872         }
873
874         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
875                 if (cred->realm == NULL)
876                         continue;
877
878                 for (i = 0; i < count; i++) {
879                         if (!nai_realm_match(&realm[i], cred->realm))
880                                 continue;
881                         if (nai_realm_find_eap(cred, &realm[i])) {
882                                 if (selected == NULL ||
883                                     selected->priority < cred->priority)
884                                         selected = cred;
885                                 break;
886                         }
887                 }
888         }
889
890         nai_realm_free(realm, count);
891
892         return selected;
893 }
894
895
896 static struct wpa_cred * interworking_credentials_available(
897         struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
898 {
899         struct wpa_cred *cred, *cred2;
900
901         cred = interworking_credentials_available_realm(wpa_s, bss);
902         cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
903         if (cred && cred2 && cred2->priority >= cred->priority)
904                 cred = cred2;
905         if (!cred)
906                 cred = cred2;
907
908         return cred;
909 }
910
911
912 static int domain_name_list_contains(struct wpabuf *domain_names,
913                                      const char *domain)
914 {
915         const u8 *pos, *end;
916         size_t len;
917
918         len = os_strlen(domain);
919         pos = wpabuf_head(domain_names);
920         end = pos + wpabuf_len(domain_names);
921
922         while (pos + 1 < end) {
923                 if (pos + 1 + pos[0] > end)
924                         break;
925
926                 wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
927                                   pos + 1, pos[0]);
928                 if (pos[0] == len &&
929                     os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
930                         return 1;
931
932                 pos += 1 + pos[0];
933         }
934
935         return 0;
936 }
937
938
939 static int interworking_home_sp(struct wpa_supplicant *wpa_s,
940                                 struct wpabuf *domain_names)
941 {
942         struct wpa_cred *cred;
943 #ifdef INTERWORKING_3GPP
944         char nai[100], *realm;
945 #endif /* INTERWORKING_3GPP */
946
947         if (domain_names == NULL || wpa_s->conf->cred == NULL)
948                 return -1;
949
950         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
951 #ifdef INTERWORKING_3GPP
952                 if (cred->imsi &&
953                     build_root_nai(nai, sizeof(nai), cred->imsi, 0) == 0) {
954                         realm = os_strchr(nai, '@');
955                         if (realm)
956                                 realm++;
957                         wpa_printf(MSG_DEBUG, "Interworking: Search for match "
958                                    "with SIM/USIM domain %s", realm);
959                         if (realm &&
960                             domain_name_list_contains(domain_names, realm))
961                                 return 1;
962                 }
963 #endif /* INTERWORKING_3GPP */
964
965                 if (cred->domain == NULL)
966                         continue;
967
968                 wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
969                            "home SP FQDN %s", cred->domain);
970                 if (domain_name_list_contains(domain_names, cred->domain))
971                         return 1;
972         }
973
974         return 0;
975 }
976
977
978 static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
979 {
980         struct wpa_bss *bss;
981         struct wpa_ssid *ssid;
982
983         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
984                 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
985                         if (ssid->disabled || ssid->mode != WPAS_MODE_INFRA)
986                                 continue;
987                         if (ssid->ssid_len != bss->ssid_len ||
988                             os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
989                             0)
990                                 continue;
991                         /*
992                          * TODO: Consider more accurate matching of security
993                          * configuration similarly to what is done in events.c
994                          */
995                         return 1;
996                 }
997         }
998
999         return 0;
1000 }
1001
1002
1003 static void interworking_select_network(struct wpa_supplicant *wpa_s)
1004 {
1005         struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
1006         int selected_prio = -999999, selected_home_prio = -999999;
1007         unsigned int count = 0;
1008         const char *type;
1009         int res;
1010         struct wpa_cred *cred;
1011
1012         wpa_s->network_select = 0;
1013
1014         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1015                 cred = interworking_credentials_available(wpa_s, bss);
1016                 if (!cred)
1017                         continue;
1018                 count++;
1019                 res = interworking_home_sp(wpa_s, bss->anqp_domain_name);
1020                 if (res > 0)
1021                         type = "home";
1022                 else if (res == 0)
1023                         type = "roaming";
1024                 else
1025                         type = "unknown";
1026                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
1027                         MAC2STR(bss->bssid), type);
1028                 if (wpa_s->auto_select) {
1029                         if (selected == NULL ||
1030                             cred->priority > selected_prio) {
1031                                 selected = bss;
1032                                 selected_prio = cred->priority;
1033                         }
1034                         if (res > 0 &&
1035                             (selected_home == NULL ||
1036                              cred->priority > selected_home_prio)) {
1037                                 selected_home = bss;
1038                                 selected_home_prio = cred->priority;
1039                         }
1040                 }
1041         }
1042
1043         if (selected_home && selected_home != selected &&
1044             selected_home_prio >= selected_prio) {
1045                 /* Prefer network operated by the Home SP */
1046                 selected = selected_home;
1047         }
1048
1049         if (count == 0) {
1050                 /*
1051                  * No matching network was found based on configured
1052                  * credentials. Check whether any of the enabled network blocks
1053                  * have matching APs.
1054                  */
1055                 if (interworking_find_network_match(wpa_s)) {
1056                         wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
1057                                    "match for enabled network configurations");
1058                         wpa_s->disconnected = 0;
1059                         wpa_s->reassociate = 1;
1060                         wpa_supplicant_req_scan(wpa_s, 0, 0);
1061                         return;
1062                 }
1063
1064                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
1065                         "with matching credentials found");
1066         }
1067
1068         if (selected)
1069                 interworking_connect(wpa_s, selected);
1070 }
1071
1072
1073 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
1074 {
1075         struct wpa_bss *bss;
1076         int found = 0;
1077         const u8 *ie;
1078
1079         if (!wpa_s->fetch_anqp_in_progress)
1080                 return;
1081
1082         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1083                 if (!(bss->caps & IEEE80211_CAP_ESS))
1084                         continue;
1085                 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
1086                 if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
1087                         continue; /* AP does not support Interworking */
1088
1089                 if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
1090                         found++;
1091                         bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
1092                         wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
1093                                 MACSTR, MAC2STR(bss->bssid));
1094                         interworking_anqp_send_req(wpa_s, bss);
1095                         break;
1096                 }
1097         }
1098
1099         if (found == 0) {
1100                 wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
1101                 wpa_s->fetch_anqp_in_progress = 0;
1102                 if (wpa_s->network_select)
1103                         interworking_select_network(wpa_s);
1104         }
1105 }
1106
1107
1108 static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
1109 {
1110         struct wpa_bss *bss;
1111
1112         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
1113                 bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
1114
1115         wpa_s->fetch_anqp_in_progress = 1;
1116         interworking_next_anqp_fetch(wpa_s);
1117 }
1118
1119
1120 int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
1121 {
1122         if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
1123                 return 0;
1124
1125         wpa_s->network_select = 0;
1126
1127         interworking_start_fetch_anqp(wpa_s);
1128
1129         return 0;
1130 }
1131
1132
1133 void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
1134 {
1135         if (!wpa_s->fetch_anqp_in_progress)
1136                 return;
1137
1138         wpa_s->fetch_anqp_in_progress = 0;
1139 }
1140
1141
1142 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
1143                   u16 info_ids[], size_t num_ids)
1144 {
1145         struct wpabuf *buf;
1146         int ret = 0;
1147         int freq;
1148         struct wpa_bss *bss;
1149         int res;
1150
1151         freq = wpa_s->assoc_freq;
1152         bss = wpa_bss_get_bssid(wpa_s, dst);
1153         if (bss)
1154                 freq = bss->freq;
1155         if (freq <= 0)
1156                 return -1;
1157
1158         wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
1159                    MAC2STR(dst), (unsigned int) num_ids);
1160
1161         buf = anqp_build_req(info_ids, num_ids, NULL);
1162         if (buf == NULL)
1163                 return -1;
1164
1165         res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
1166         if (res < 0) {
1167                 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
1168                 ret = -1;
1169         } else
1170                 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
1171                            "%u", res);
1172
1173         wpabuf_free(buf);
1174         return ret;
1175 }
1176
1177
1178 static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
1179                                             const u8 *sa, u16 info_id,
1180                                             const u8 *data, size_t slen)
1181 {
1182         const u8 *pos = data;
1183         struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
1184
1185         switch (info_id) {
1186         case ANQP_CAPABILITY_LIST:
1187                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1188                         " ANQP Capability list", MAC2STR(sa));
1189                 break;
1190         case ANQP_VENUE_NAME:
1191                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1192                         " Venue Name", MAC2STR(sa));
1193                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
1194                 if (bss) {
1195                         wpabuf_free(bss->anqp_venue_name);
1196                         bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
1197                 }
1198                 break;
1199         case ANQP_NETWORK_AUTH_TYPE:
1200                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1201                         " Network Authentication Type information",
1202                         MAC2STR(sa));
1203                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
1204                                   "Type", pos, slen);
1205                 if (bss) {
1206                         wpabuf_free(bss->anqp_network_auth_type);
1207                         bss->anqp_network_auth_type =
1208                                 wpabuf_alloc_copy(pos, slen);
1209                 }
1210                 break;
1211         case ANQP_ROAMING_CONSORTIUM:
1212                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1213                         " Roaming Consortium list", MAC2STR(sa));
1214                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
1215                                   pos, slen);
1216                 if (bss) {
1217                         wpabuf_free(bss->anqp_roaming_consortium);
1218                         bss->anqp_roaming_consortium =
1219                                 wpabuf_alloc_copy(pos, slen);
1220                 }
1221                 break;
1222         case ANQP_IP_ADDR_TYPE_AVAILABILITY:
1223                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1224                         " IP Address Type Availability information",
1225                         MAC2STR(sa));
1226                 wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
1227                             pos, slen);
1228                 if (bss) {
1229                         wpabuf_free(bss->anqp_ip_addr_type_availability);
1230                         bss->anqp_ip_addr_type_availability =
1231                                 wpabuf_alloc_copy(pos, slen);
1232                 }
1233                 break;
1234         case ANQP_NAI_REALM:
1235                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1236                         " NAI Realm list", MAC2STR(sa));
1237                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
1238                 if (bss) {
1239                         wpabuf_free(bss->anqp_nai_realm);
1240                         bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
1241                 }
1242                 break;
1243         case ANQP_3GPP_CELLULAR_NETWORK:
1244                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1245                         " 3GPP Cellular Network information", MAC2STR(sa));
1246                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
1247                                   pos, slen);
1248                 if (bss) {
1249                         wpabuf_free(bss->anqp_3gpp);
1250                         bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
1251                 }
1252                 break;
1253         case ANQP_DOMAIN_NAME:
1254                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1255                         " Domain Name list", MAC2STR(sa));
1256                 wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
1257                 if (bss) {
1258                         wpabuf_free(bss->anqp_domain_name);
1259                         bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
1260                 }
1261                 break;
1262         case ANQP_VENDOR_SPECIFIC:
1263                 if (slen < 3)
1264                         return;
1265
1266                 switch (WPA_GET_BE24(pos)) {
1267                 default:
1268                         wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
1269                                    "vendor-specific ANQP OUI %06x",
1270                                    WPA_GET_BE24(pos));
1271                         return;
1272                 }
1273                 break;
1274         default:
1275                 wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
1276                            "%u", info_id);
1277                 break;
1278         }
1279 }
1280
1281
1282 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
1283                   enum gas_query_result result,
1284                   const struct wpabuf *adv_proto,
1285                   const struct wpabuf *resp, u16 status_code)
1286 {
1287         struct wpa_supplicant *wpa_s = ctx;
1288         const u8 *pos;
1289         const u8 *end;
1290         u16 info_id;
1291         u16 slen;
1292
1293         if (result != GAS_QUERY_SUCCESS)
1294                 return;
1295
1296         pos = wpabuf_head(adv_proto);
1297         if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
1298             pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
1299                 wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
1300                            "Protocol in response");
1301                 return;
1302         }
1303
1304         pos = wpabuf_head(resp);
1305         end = pos + wpabuf_len(resp);
1306
1307         while (pos < end) {
1308                 if (pos + 4 > end) {
1309                         wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
1310                         break;
1311                 }
1312                 info_id = WPA_GET_LE16(pos);
1313                 pos += 2;
1314                 slen = WPA_GET_LE16(pos);
1315                 pos += 2;
1316                 if (pos + slen > end) {
1317                         wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
1318                                    "for Info ID %u", info_id);
1319                         break;
1320                 }
1321                 interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
1322                                                 slen);
1323                 pos += slen;
1324         }
1325 }
1326
1327
1328 static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
1329                                           struct wpa_scan_results *scan_res)
1330 {
1331         wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
1332                    "ANQP fetch");
1333         interworking_start_fetch_anqp(wpa_s);
1334 }
1335
1336
1337 int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
1338 {
1339         interworking_stop_fetch_anqp(wpa_s);
1340         wpa_s->network_select = 1;
1341         wpa_s->auto_select = !!auto_select;
1342         wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
1343                    "selection");
1344         wpa_s->scan_res_handler = interworking_scan_res_handler;
1345         wpa_s->scan_req = 2;
1346         wpa_supplicant_req_scan(wpa_s, 0, 0);
1347
1348         return 0;
1349 }