interworking.c revision 4b9d52f502481b258fec743c03a5e957e5605afc
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 "utils/pcsc_funcs.h" 16#include "utils/eloop.h" 17#include "drivers/driver.h" 18#include "eap_common/eap_defs.h" 19#include "eap_peer/eap.h" 20#include "eap_peer/eap_methods.h" 21#include "wpa_supplicant_i.h" 22#include "config.h" 23#include "config_ssid.h" 24#include "bss.h" 25#include "scan.h" 26#include "notify.h" 27#include "gas_query.h" 28#include "hs20_supplicant.h" 29#include "interworking.h" 30 31 32#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC) 33#define INTERWORKING_3GPP 34#else 35#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC) 36#define INTERWORKING_3GPP 37#else 38#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC) 39#define INTERWORKING_3GPP 40#endif 41#endif 42#endif 43 44static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s); 45 46 47static void interworking_reconnect(struct wpa_supplicant *wpa_s) 48{ 49 if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { 50 wpa_supplicant_cancel_sched_scan(wpa_s); 51 wpa_supplicant_deauthenticate(wpa_s, 52 WLAN_REASON_DEAUTH_LEAVING); 53 } 54 wpa_s->disconnected = 0; 55 wpa_s->reassociate = 1; 56 57 if (wpa_supplicant_fast_associate(wpa_s) >= 0) 58 return; 59 60 wpa_supplicant_req_scan(wpa_s, 0, 0); 61} 62 63 64static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids, 65 struct wpabuf *extra) 66{ 67 struct wpabuf *buf; 68 size_t i; 69 u8 *len_pos; 70 71 buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 + 72 (extra ? wpabuf_len(extra) : 0)); 73 if (buf == NULL) 74 return NULL; 75 76 len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST); 77 for (i = 0; i < num_ids; i++) 78 wpabuf_put_le16(buf, info_ids[i]); 79 gas_anqp_set_element_len(buf, len_pos); 80 if (extra) 81 wpabuf_put_buf(buf, extra); 82 83 gas_anqp_set_len(buf); 84 85 return buf; 86} 87 88 89static void interworking_anqp_resp_cb(void *ctx, const u8 *dst, 90 u8 dialog_token, 91 enum gas_query_result result, 92 const struct wpabuf *adv_proto, 93 const struct wpabuf *resp, 94 u16 status_code) 95{ 96 struct wpa_supplicant *wpa_s = ctx; 97 98 anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp, 99 status_code); 100 interworking_next_anqp_fetch(wpa_s); 101} 102 103 104static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s) 105{ 106 struct wpa_cred *cred; 107 108 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 109 if (cred->roaming_consortium_len) 110 return 1; 111 } 112 return 0; 113} 114 115 116static int cred_with_3gpp(struct wpa_supplicant *wpa_s) 117{ 118 struct wpa_cred *cred; 119 120 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 121 if (cred->pcsc || cred->imsi) 122 return 1; 123 } 124 return 0; 125} 126 127 128static int cred_with_nai_realm(struct wpa_supplicant *wpa_s) 129{ 130 struct wpa_cred *cred; 131 132 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 133 if (cred->pcsc || cred->imsi) 134 continue; 135 if (!cred->eap_method) 136 return 1; 137 if (cred->realm && cred->roaming_consortium_len == 0) 138 return 1; 139 } 140 return 0; 141} 142 143 144static int cred_with_domain(struct wpa_supplicant *wpa_s) 145{ 146 struct wpa_cred *cred; 147 148 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 149 if (cred->domain || cred->pcsc || cred->imsi) 150 return 1; 151 } 152 return 0; 153} 154 155 156static int additional_roaming_consortiums(struct wpa_bss *bss) 157{ 158 const u8 *ie; 159 ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); 160 if (ie == NULL || ie[1] == 0) 161 return 0; 162 return ie[2]; /* Number of ANQP OIs */ 163} 164 165 166static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx) 167{ 168 struct wpa_supplicant *wpa_s = eloop_ctx; 169 interworking_next_anqp_fetch(wpa_s); 170} 171 172 173static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, 174 struct wpa_bss *bss) 175{ 176 struct wpabuf *buf; 177 int ret = 0; 178 int res; 179 u16 info_ids[8]; 180 size_t num_info_ids = 0; 181 struct wpabuf *extra = NULL; 182 int all = wpa_s->fetch_all_anqp; 183 184 wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR, 185 MAC2STR(bss->bssid)); 186 187 info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST; 188 if (all) { 189 info_ids[num_info_ids++] = ANQP_VENUE_NAME; 190 info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE; 191 } 192 if (all || (cred_with_roaming_consortium(wpa_s) && 193 additional_roaming_consortiums(bss))) 194 info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM; 195 if (all) 196 info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY; 197 if (all || cred_with_nai_realm(wpa_s)) 198 info_ids[num_info_ids++] = ANQP_NAI_REALM; 199 if (all || cred_with_3gpp(wpa_s)) 200 info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK; 201 if (all || cred_with_domain(wpa_s)) 202 info_ids[num_info_ids++] = ANQP_DOMAIN_NAME; 203 wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info", 204 (u8 *) info_ids, num_info_ids * 2); 205 206#ifdef CONFIG_HS20 207 if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { 208 u8 *len_pos; 209 210 extra = wpabuf_alloc(100); 211 if (!extra) 212 return -1; 213 214 len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC); 215 wpabuf_put_be24(extra, OUI_WFA); 216 wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE); 217 wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST); 218 wpabuf_put_u8(extra, 0); /* Reserved */ 219 wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST); 220 if (all) { 221 wpabuf_put_u8(extra, 222 HS20_STYPE_OPERATOR_FRIENDLY_NAME); 223 wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS); 224 wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY); 225 wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS); 226 } 227 gas_anqp_set_element_len(extra, len_pos); 228 } 229#endif /* CONFIG_HS20 */ 230 231 buf = anqp_build_req(info_ids, num_info_ids, extra); 232 wpabuf_free(extra); 233 if (buf == NULL) 234 return -1; 235 236 res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf, 237 interworking_anqp_resp_cb, wpa_s); 238 if (res < 0) { 239 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); 240 ret = -1; 241 eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, 242 NULL); 243 } else 244 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " 245 "%u", res); 246 247 wpabuf_free(buf); 248 return ret; 249} 250 251 252struct nai_realm_eap { 253 u8 method; 254 u8 inner_method; 255 enum nai_realm_eap_auth_inner_non_eap inner_non_eap; 256 u8 cred_type; 257 u8 tunneled_cred_type; 258}; 259 260struct nai_realm { 261 u8 encoding; 262 char *realm; 263 u8 eap_count; 264 struct nai_realm_eap *eap; 265}; 266 267 268static void nai_realm_free(struct nai_realm *realms, u16 count) 269{ 270 u16 i; 271 272 if (realms == NULL) 273 return; 274 for (i = 0; i < count; i++) { 275 os_free(realms[i].eap); 276 os_free(realms[i].realm); 277 } 278 os_free(realms); 279} 280 281 282static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos, 283 const u8 *end) 284{ 285 u8 elen, auth_count, a; 286 const u8 *e_end; 287 288 if (pos + 3 > end) { 289 wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields"); 290 return NULL; 291 } 292 293 elen = *pos++; 294 if (pos + elen > end || elen < 2) { 295 wpa_printf(MSG_DEBUG, "No room for EAP Method subfield"); 296 return NULL; 297 } 298 e_end = pos + elen; 299 e->method = *pos++; 300 auth_count = *pos++; 301 wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u", 302 elen, e->method, auth_count); 303 304 for (a = 0; a < auth_count; a++) { 305 u8 id, len; 306 307 if (pos + 2 > end || pos + 2 + pos[1] > end) { 308 wpa_printf(MSG_DEBUG, "No room for Authentication " 309 "Parameter subfield"); 310 return NULL; 311 } 312 313 id = *pos++; 314 len = *pos++; 315 316 switch (id) { 317 case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH: 318 if (len < 1) 319 break; 320 e->inner_non_eap = *pos; 321 if (e->method != EAP_TYPE_TTLS) 322 break; 323 switch (*pos) { 324 case NAI_REALM_INNER_NON_EAP_PAP: 325 wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP"); 326 break; 327 case NAI_REALM_INNER_NON_EAP_CHAP: 328 wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP"); 329 break; 330 case NAI_REALM_INNER_NON_EAP_MSCHAP: 331 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP"); 332 break; 333 case NAI_REALM_INNER_NON_EAP_MSCHAPV2: 334 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2"); 335 break; 336 } 337 break; 338 case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD: 339 if (len < 1) 340 break; 341 e->inner_method = *pos; 342 wpa_printf(MSG_DEBUG, "Inner EAP method: %u", 343 e->inner_method); 344 break; 345 case NAI_REALM_EAP_AUTH_CRED_TYPE: 346 if (len < 1) 347 break; 348 e->cred_type = *pos; 349 wpa_printf(MSG_DEBUG, "Credential Type: %u", 350 e->cred_type); 351 break; 352 case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE: 353 if (len < 1) 354 break; 355 e->tunneled_cred_type = *pos; 356 wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential " 357 "Type: %u", e->tunneled_cred_type); 358 break; 359 default: 360 wpa_printf(MSG_DEBUG, "Unsupported Authentication " 361 "Parameter: id=%u len=%u", id, len); 362 wpa_hexdump(MSG_DEBUG, "Authentication Parameter " 363 "Value", pos, len); 364 break; 365 } 366 367 pos += len; 368 } 369 370 return e_end; 371} 372 373 374static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, 375 const u8 *end) 376{ 377 u16 len; 378 const u8 *f_end; 379 u8 realm_len, e; 380 381 if (end - pos < 4) { 382 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data " 383 "fixed fields"); 384 return NULL; 385 } 386 387 len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */ 388 pos += 2; 389 if (pos + len > end || len < 3) { 390 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data " 391 "(len=%u; left=%u)", 392 len, (unsigned int) (end - pos)); 393 return NULL; 394 } 395 f_end = pos + len; 396 397 r->encoding = *pos++; 398 realm_len = *pos++; 399 if (pos + realm_len > f_end) { 400 wpa_printf(MSG_DEBUG, "No room for NAI Realm " 401 "(len=%u; left=%u)", 402 realm_len, (unsigned int) (f_end - pos)); 403 return NULL; 404 } 405 wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len); 406 r->realm = os_malloc(realm_len + 1); 407 if (r->realm == NULL) 408 return NULL; 409 os_memcpy(r->realm, pos, realm_len); 410 r->realm[realm_len] = '\0'; 411 pos += realm_len; 412 413 if (pos + 1 > f_end) { 414 wpa_printf(MSG_DEBUG, "No room for EAP Method Count"); 415 return NULL; 416 } 417 r->eap_count = *pos++; 418 wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count); 419 if (pos + r->eap_count * 3 > f_end) { 420 wpa_printf(MSG_DEBUG, "No room for EAP Methods"); 421 return NULL; 422 } 423 r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap)); 424 if (r->eap == NULL) 425 return NULL; 426 427 for (e = 0; e < r->eap_count; e++) { 428 pos = nai_realm_parse_eap(&r->eap[e], pos, f_end); 429 if (pos == NULL) 430 return NULL; 431 } 432 433 return f_end; 434} 435 436 437static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count) 438{ 439 struct nai_realm *realm; 440 const u8 *pos, *end; 441 u16 i, num; 442 443 if (anqp == NULL || wpabuf_len(anqp) < 2) 444 return NULL; 445 446 pos = wpabuf_head_u8(anqp); 447 end = pos + wpabuf_len(anqp); 448 num = WPA_GET_LE16(pos); 449 wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num); 450 pos += 2; 451 452 if (num * 5 > end - pos) { 453 wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not " 454 "enough data (%u octets) for that many realms", 455 num, (unsigned int) (end - pos)); 456 return NULL; 457 } 458 459 realm = os_calloc(num, sizeof(struct nai_realm)); 460 if (realm == NULL) 461 return NULL; 462 463 for (i = 0; i < num; i++) { 464 pos = nai_realm_parse_realm(&realm[i], pos, end); 465 if (pos == NULL) { 466 nai_realm_free(realm, num); 467 return NULL; 468 } 469 } 470 471 *count = num; 472 return realm; 473} 474 475 476static int nai_realm_match(struct nai_realm *realm, const char *home_realm) 477{ 478 char *tmp, *pos, *end; 479 int match = 0; 480 481 if (realm->realm == NULL || home_realm == NULL) 482 return 0; 483 484 if (os_strchr(realm->realm, ';') == NULL) 485 return os_strcasecmp(realm->realm, home_realm) == 0; 486 487 tmp = os_strdup(realm->realm); 488 if (tmp == NULL) 489 return 0; 490 491 pos = tmp; 492 while (*pos) { 493 end = os_strchr(pos, ';'); 494 if (end) 495 *end = '\0'; 496 if (os_strcasecmp(pos, home_realm) == 0) { 497 match = 1; 498 break; 499 } 500 if (end == NULL) 501 break; 502 pos = end + 1; 503 } 504 505 os_free(tmp); 506 507 return match; 508} 509 510 511static int nai_realm_cred_username(struct nai_realm_eap *eap) 512{ 513 if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) 514 return 0; /* method not supported */ 515 516 if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) { 517 /* Only tunneled methods with username/password supported */ 518 return 0; 519 } 520 521 if (eap->method == EAP_TYPE_PEAP) { 522 if (eap->inner_method && 523 eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) 524 return 0; 525 if (!eap->inner_method && 526 eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) 527 return 0; 528 } 529 530 if (eap->method == EAP_TYPE_TTLS) { 531 if (eap->inner_method == 0 && eap->inner_non_eap == 0) 532 return 1; /* Assume TTLS/MSCHAPv2 is used */ 533 if (eap->inner_method && 534 eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) 535 return 0; 536 if (eap->inner_non_eap && 537 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP && 538 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP && 539 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP && 540 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2) 541 return 0; 542 } 543 544 if (eap->inner_method && 545 eap->inner_method != EAP_TYPE_GTC && 546 eap->inner_method != EAP_TYPE_MSCHAPV2) 547 return 0; 548 549 return 1; 550} 551 552 553static int nai_realm_cred_cert(struct nai_realm_eap *eap) 554{ 555 if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) 556 return 0; /* method not supported */ 557 558 if (eap->method != EAP_TYPE_TLS) { 559 /* Only EAP-TLS supported for credential authentication */ 560 return 0; 561 } 562 563 return 1; 564} 565 566 567static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred, 568 struct nai_realm *realm) 569{ 570 u8 e; 571 572 if (cred == NULL || 573 cred->username == NULL || 574 cred->username[0] == '\0' || 575 ((cred->password == NULL || 576 cred->password[0] == '\0') && 577 (cred->private_key == NULL || 578 cred->private_key[0] == '\0'))) 579 return NULL; 580 581 for (e = 0; e < realm->eap_count; e++) { 582 struct nai_realm_eap *eap = &realm->eap[e]; 583 if (cred->password && cred->password[0] && 584 nai_realm_cred_username(eap)) 585 return eap; 586 if (cred->private_key && cred->private_key[0] && 587 nai_realm_cred_cert(eap)) 588 return eap; 589 } 590 591 return NULL; 592} 593 594 595#ifdef INTERWORKING_3GPP 596 597static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) 598{ 599 u8 plmn[3]; 600 const u8 *pos, *end; 601 u8 udhl; 602 603 /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */ 604 plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); 605 plmn[1] = imsi[2] - '0'; 606 /* default to MNC length 3 if unknown */ 607 if (mnc_len != 2) 608 plmn[1] |= (imsi[5] - '0') << 4; 609 else 610 plmn[1] |= 0xf0; 611 plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); 612 613 if (anqp == NULL) 614 return 0; 615 pos = wpabuf_head_u8(anqp); 616 end = pos + wpabuf_len(anqp); 617 if (pos + 2 > end) 618 return 0; 619 if (*pos != 0) { 620 wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos); 621 return 0; 622 } 623 pos++; 624 udhl = *pos++; 625 if (pos + udhl > end) { 626 wpa_printf(MSG_DEBUG, "Invalid UDHL"); 627 return 0; 628 } 629 end = pos + udhl; 630 631 while (pos + 2 <= end) { 632 u8 iei, len; 633 const u8 *l_end; 634 iei = *pos++; 635 len = *pos++ & 0x7f; 636 if (pos + len > end) 637 break; 638 l_end = pos + len; 639 640 if (iei == 0 && len > 0) { 641 /* PLMN List */ 642 u8 num, i; 643 num = *pos++; 644 for (i = 0; i < num; i++) { 645 if (pos + 3 > end) 646 break; 647 if (os_memcmp(pos, plmn, 3) == 0) 648 return 1; /* Found matching PLMN */ 649 pos += 3; 650 } 651 } 652 653 pos = l_end; 654 } 655 656 return 0; 657} 658 659 660static int build_root_nai(char *nai, size_t nai_len, const char *imsi, 661 size_t mnc_len, char prefix) 662{ 663 const char *sep, *msin; 664 char *end, *pos; 665 size_t msin_len, plmn_len; 666 667 /* 668 * TS 23.003, Clause 14 (3GPP to WLAN Interworking) 669 * Root NAI: 670 * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org 671 * <MNC> is zero-padded to three digits in case two-digit MNC is used 672 */ 673 674 if (imsi == NULL || os_strlen(imsi) > 16) { 675 wpa_printf(MSG_DEBUG, "No valid IMSI available"); 676 return -1; 677 } 678 sep = os_strchr(imsi, '-'); 679 if (sep) { 680 plmn_len = sep - imsi; 681 msin = sep + 1; 682 } else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) { 683 plmn_len = 3 + mnc_len; 684 msin = imsi + plmn_len; 685 } else 686 return -1; 687 if (plmn_len != 5 && plmn_len != 6) 688 return -1; 689 msin_len = os_strlen(msin); 690 691 pos = nai; 692 end = nai + nai_len; 693 if (prefix) 694 *pos++ = prefix; 695 os_memcpy(pos, imsi, plmn_len); 696 pos += plmn_len; 697 os_memcpy(pos, msin, msin_len); 698 pos += msin_len; 699 pos += os_snprintf(pos, end - pos, "@wlan.mnc"); 700 if (plmn_len == 5) { 701 *pos++ = '0'; 702 *pos++ = imsi[3]; 703 *pos++ = imsi[4]; 704 } else { 705 *pos++ = imsi[3]; 706 *pos++ = imsi[4]; 707 *pos++ = imsi[5]; 708 } 709 pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org", 710 imsi[0], imsi[1], imsi[2]); 711 712 return 0; 713} 714 715 716static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix) 717{ 718 char nai[100]; 719 if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0) 720 return -1; 721 return wpa_config_set_quoted(ssid, "identity", nai); 722} 723 724#endif /* INTERWORKING_3GPP */ 725 726 727static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s, 728 struct wpa_ssid *ssid) 729{ 730 if (wpa_config_set(ssid, "key_mgmt", 731 wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ? 732 "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0) 733 return -1; 734 if (wpa_config_set(ssid, "proto", "RSN", 0) < 0) 735 return -1; 736 if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0) 737 return -1; 738 return 0; 739} 740 741 742static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, 743 struct wpa_bss *bss) 744{ 745#ifdef INTERWORKING_3GPP 746 struct wpa_cred *cred; 747 struct wpa_ssid *ssid; 748 const u8 *ie; 749 int eap_type; 750 int res; 751 char prefix; 752 753 if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) 754 return -1; 755 756 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 757 char *sep; 758 const char *imsi; 759 int mnc_len; 760 761#ifdef PCSC_FUNCS 762 if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard && 763 wpa_s->imsi[0]) { 764 imsi = wpa_s->imsi; 765 mnc_len = wpa_s->mnc_len; 766 goto compare; 767 } 768#endif /* PCSC_FUNCS */ 769 770 if (cred->imsi == NULL || !cred->imsi[0] || 771 cred->milenage == NULL || !cred->milenage[0]) 772 continue; 773 774 sep = os_strchr(cred->imsi, '-'); 775 if (sep == NULL || 776 (sep - cred->imsi != 5 && sep - cred->imsi != 6)) 777 continue; 778 mnc_len = sep - cred->imsi - 3; 779 imsi = cred->imsi; 780 781#ifdef PCSC_FUNCS 782 compare: 783#endif /* PCSC_FUNCS */ 784 if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len)) 785 break; 786 } 787 if (cred == NULL) 788 return -1; 789 790 ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); 791 if (ie == NULL) 792 return -1; 793 wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)", 794 MAC2STR(bss->bssid)); 795 796 ssid = wpa_config_add_network(wpa_s->conf); 797 if (ssid == NULL) 798 return -1; 799 ssid->parent_cred = cred; 800 801 wpas_notify_network_added(wpa_s, ssid); 802 wpa_config_set_network_defaults(ssid); 803 ssid->priority = cred->priority; 804 ssid->temporary = 1; 805 ssid->ssid = os_zalloc(ie[1] + 1); 806 if (ssid->ssid == NULL) 807 goto fail; 808 os_memcpy(ssid->ssid, ie + 2, ie[1]); 809 ssid->ssid_len = ie[1]; 810 811 if (interworking_set_hs20_params(wpa_s, ssid) < 0) 812 goto fail; 813 814 eap_type = EAP_TYPE_SIM; 815 if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard)) 816 eap_type = EAP_TYPE_AKA; 817 if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) { 818 if (cred->eap_method[0].method == EAP_TYPE_SIM || 819 cred->eap_method[0].method == EAP_TYPE_AKA || 820 cred->eap_method[0].method == EAP_TYPE_AKA_PRIME) 821 eap_type = cred->eap_method[0].method; 822 } 823 824 switch (eap_type) { 825 case EAP_TYPE_SIM: 826 prefix = '1'; 827 res = wpa_config_set(ssid, "eap", "SIM", 0); 828 break; 829 case EAP_TYPE_AKA: 830 prefix = '0'; 831 res = wpa_config_set(ssid, "eap", "AKA", 0); 832 break; 833 case EAP_TYPE_AKA_PRIME: 834 prefix = '6'; 835 res = wpa_config_set(ssid, "eap", "AKA'", 0); 836 break; 837 default: 838 res = -1; 839 break; 840 } 841 if (res < 0) { 842 wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported", 843 eap_type); 844 goto fail; 845 } 846 847 if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) { 848 wpa_printf(MSG_DEBUG, "Failed to set Root NAI"); 849 goto fail; 850 } 851 852 if (cred->milenage && cred->milenage[0]) { 853 if (wpa_config_set_quoted(ssid, "password", 854 cred->milenage) < 0) 855 goto fail; 856 } else if (cred->pcsc) { 857 if (wpa_config_set_quoted(ssid, "pcsc", "") < 0) 858 goto fail; 859 if (wpa_s->conf->pcsc_pin && 860 wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin) 861 < 0) 862 goto fail; 863 } 864 865 if (cred->password && cred->password[0] && 866 wpa_config_set_quoted(ssid, "password", cred->password) < 0) 867 goto fail; 868 869 wpa_config_update_prio_list(wpa_s->conf); 870 interworking_reconnect(wpa_s); 871 872 return 0; 873 874fail: 875 wpas_notify_network_removed(wpa_s, ssid); 876 wpa_config_remove_network(wpa_s->conf, ssid->id); 877#endif /* INTERWORKING_3GPP */ 878 return -1; 879} 880 881 882static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id, 883 size_t rc_len) 884{ 885 const u8 *pos, *end; 886 u8 lens; 887 888 if (ie == NULL) 889 return 0; 890 891 pos = ie + 2; 892 end = ie + 2 + ie[1]; 893 894 /* Roaming Consortium element: 895 * Number of ANQP OIs 896 * OI #1 and #2 lengths 897 * OI #1, [OI #2], [OI #3] 898 */ 899 900 if (pos + 2 > end) 901 return 0; 902 903 pos++; /* skip Number of ANQP OIs */ 904 lens = *pos++; 905 if (pos + (lens & 0x0f) + (lens >> 4) > end) 906 return 0; 907 908 if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) 909 return 1; 910 pos += lens & 0x0f; 911 912 if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) 913 return 1; 914 pos += lens >> 4; 915 916 if (pos < end && (size_t) (end - pos) == rc_len && 917 os_memcmp(pos, rc_id, rc_len) == 0) 918 return 1; 919 920 return 0; 921} 922 923 924static int roaming_consortium_anqp_match(const struct wpabuf *anqp, 925 const u8 *rc_id, size_t rc_len) 926{ 927 const u8 *pos, *end; 928 u8 len; 929 930 if (anqp == NULL) 931 return 0; 932 933 pos = wpabuf_head(anqp); 934 end = pos + wpabuf_len(anqp); 935 936 /* Set of <OI Length, OI> duples */ 937 while (pos < end) { 938 len = *pos++; 939 if (pos + len > end) 940 break; 941 if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) 942 return 1; 943 pos += len; 944 } 945 946 return 0; 947} 948 949 950static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp, 951 const u8 *rc_id, size_t rc_len) 952{ 953 return roaming_consortium_element_match(ie, rc_id, rc_len) || 954 roaming_consortium_anqp_match(anqp, rc_id, rc_len); 955} 956 957 958static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss) 959{ 960 size_t i; 961 962 if (!cred->excluded_ssid) 963 return 0; 964 965 for (i = 0; i < cred->num_excluded_ssid; i++) { 966 struct excluded_ssid *e = &cred->excluded_ssid[i]; 967 if (bss->ssid_len == e->ssid_len && 968 os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0) 969 return 1; 970 } 971 972 return 0; 973} 974 975 976static struct wpa_cred * interworking_credentials_available_roaming_consortium( 977 struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 978{ 979 struct wpa_cred *cred, *selected = NULL; 980 const u8 *ie; 981 982 ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); 983 984 if (ie == NULL && 985 (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL)) 986 return NULL; 987 988 if (wpa_s->conf->cred == NULL) 989 return NULL; 990 991 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 992 if (cred->roaming_consortium_len == 0) 993 continue; 994 995 if (!roaming_consortium_match(ie, 996 bss->anqp ? 997 bss->anqp->roaming_consortium : 998 NULL, 999 cred->roaming_consortium, 1000 cred->roaming_consortium_len)) 1001 continue; 1002 1003 if (cred_excluded_ssid(cred, bss)) 1004 continue; 1005 1006 if (selected == NULL || 1007 selected->priority < cred->priority) 1008 selected = cred; 1009 } 1010 1011 return selected; 1012} 1013 1014 1015static int interworking_set_eap_params(struct wpa_ssid *ssid, 1016 struct wpa_cred *cred, int ttls) 1017{ 1018 if (cred->eap_method) { 1019 ttls = cred->eap_method->vendor == EAP_VENDOR_IETF && 1020 cred->eap_method->method == EAP_TYPE_TTLS; 1021 1022 os_free(ssid->eap.eap_methods); 1023 ssid->eap.eap_methods = 1024 os_malloc(sizeof(struct eap_method_type) * 2); 1025 if (ssid->eap.eap_methods == NULL) 1026 return -1; 1027 os_memcpy(ssid->eap.eap_methods, cred->eap_method, 1028 sizeof(*cred->eap_method)); 1029 ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF; 1030 ssid->eap.eap_methods[1].method = EAP_TYPE_NONE; 1031 } 1032 1033 if (ttls && cred->username && cred->username[0]) { 1034 const char *pos; 1035 char *anon; 1036 /* Use anonymous NAI in Phase 1 */ 1037 pos = os_strchr(cred->username, '@'); 1038 if (pos) { 1039 size_t buflen = 9 + os_strlen(pos) + 1; 1040 anon = os_malloc(buflen); 1041 if (anon == NULL) 1042 return -1; 1043 os_snprintf(anon, buflen, "anonymous%s", pos); 1044 } else if (cred->realm) { 1045 size_t buflen = 10 + os_strlen(cred->realm) + 1; 1046 anon = os_malloc(buflen); 1047 if (anon == NULL) 1048 return -1; 1049 os_snprintf(anon, buflen, "anonymous@%s", cred->realm); 1050 } else { 1051 anon = os_strdup("anonymous"); 1052 if (anon == NULL) 1053 return -1; 1054 } 1055 if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) < 1056 0) { 1057 os_free(anon); 1058 return -1; 1059 } 1060 os_free(anon); 1061 } 1062 1063 if (cred->username && cred->username[0] && 1064 wpa_config_set_quoted(ssid, "identity", cred->username) < 0) 1065 return -1; 1066 1067 if (cred->password && cred->password[0]) { 1068 if (cred->ext_password && 1069 wpa_config_set(ssid, "password", cred->password, 0) < 0) 1070 return -1; 1071 if (!cred->ext_password && 1072 wpa_config_set_quoted(ssid, "password", cred->password) < 1073 0) 1074 return -1; 1075 } 1076 1077 if (cred->client_cert && cred->client_cert[0] && 1078 wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0) 1079 return -1; 1080 1081#ifdef ANDROID 1082 if (cred->private_key && 1083 os_strncmp(cred->private_key, "keystore://", 11) == 0) { 1084 /* Use OpenSSL engine configuration for Android keystore */ 1085 if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 || 1086 wpa_config_set_quoted(ssid, "key_id", 1087 cred->private_key + 11) < 0 || 1088 wpa_config_set(ssid, "engine", "1", 0) < 0) 1089 return -1; 1090 } else 1091#endif /* ANDROID */ 1092 if (cred->private_key && cred->private_key[0] && 1093 wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0) 1094 return -1; 1095 1096 if (cred->private_key_passwd && cred->private_key_passwd[0] && 1097 wpa_config_set_quoted(ssid, "private_key_passwd", 1098 cred->private_key_passwd) < 0) 1099 return -1; 1100 1101 if (cred->phase1) { 1102 os_free(ssid->eap.phase1); 1103 ssid->eap.phase1 = os_strdup(cred->phase1); 1104 } 1105 if (cred->phase2) { 1106 os_free(ssid->eap.phase2); 1107 ssid->eap.phase2 = os_strdup(cred->phase2); 1108 } 1109 1110 if (cred->ca_cert && cred->ca_cert[0] && 1111 wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0) 1112 return -1; 1113 1114 return 0; 1115} 1116 1117 1118static int interworking_connect_roaming_consortium( 1119 struct wpa_supplicant *wpa_s, struct wpa_cred *cred, 1120 struct wpa_bss *bss, const u8 *ssid_ie) 1121{ 1122 struct wpa_ssid *ssid; 1123 1124 wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on " 1125 "roaming consortium match", MAC2STR(bss->bssid)); 1126 1127 ssid = wpa_config_add_network(wpa_s->conf); 1128 if (ssid == NULL) 1129 return -1; 1130 ssid->parent_cred = cred; 1131 wpas_notify_network_added(wpa_s, ssid); 1132 wpa_config_set_network_defaults(ssid); 1133 ssid->priority = cred->priority; 1134 ssid->temporary = 1; 1135 ssid->ssid = os_zalloc(ssid_ie[1] + 1); 1136 if (ssid->ssid == NULL) 1137 goto fail; 1138 os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]); 1139 ssid->ssid_len = ssid_ie[1]; 1140 1141 if (interworking_set_hs20_params(wpa_s, ssid) < 0) 1142 goto fail; 1143 1144 if (cred->eap_method == NULL) { 1145 wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for " 1146 "credential using roaming consortium"); 1147 goto fail; 1148 } 1149 1150 if (interworking_set_eap_params( 1151 ssid, cred, 1152 cred->eap_method->vendor == EAP_VENDOR_IETF && 1153 cred->eap_method->method == EAP_TYPE_TTLS) < 0) 1154 goto fail; 1155 1156 wpa_config_update_prio_list(wpa_s->conf); 1157 interworking_reconnect(wpa_s); 1158 1159 return 0; 1160 1161fail: 1162 wpas_notify_network_removed(wpa_s, ssid); 1163 wpa_config_remove_network(wpa_s->conf, ssid->id); 1164 return -1; 1165} 1166 1167 1168int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 1169{ 1170 struct wpa_cred *cred; 1171 struct wpa_ssid *ssid; 1172 struct nai_realm *realm; 1173 struct nai_realm_eap *eap = NULL; 1174 u16 count, i; 1175 char buf[100]; 1176 const u8 *ie; 1177 1178 if (wpa_s->conf->cred == NULL || bss == NULL) 1179 return -1; 1180 ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); 1181 if (ie == NULL || ie[1] == 0) { 1182 wpa_printf(MSG_DEBUG, "Interworking: No SSID known for " 1183 MACSTR, MAC2STR(bss->bssid)); 1184 return -1; 1185 } 1186 1187 if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { 1188 /* 1189 * We currently support only HS 2.0 networks and those are 1190 * required to use WPA2-Enterprise. 1191 */ 1192 wpa_printf(MSG_DEBUG, "Interworking: Network does not use " 1193 "RSN"); 1194 return -1; 1195 } 1196 1197 cred = interworking_credentials_available_roaming_consortium(wpa_s, 1198 bss); 1199 if (cred) 1200 return interworking_connect_roaming_consortium(wpa_s, cred, 1201 bss, ie); 1202 1203 realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL, 1204 &count); 1205 if (realm == NULL) { 1206 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " 1207 "Realm list from " MACSTR, MAC2STR(bss->bssid)); 1208 count = 0; 1209 } 1210 1211 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 1212 for (i = 0; i < count; i++) { 1213 if (!nai_realm_match(&realm[i], cred->realm)) 1214 continue; 1215 eap = nai_realm_find_eap(cred, &realm[i]); 1216 if (eap) 1217 break; 1218 } 1219 if (eap) 1220 break; 1221 } 1222 1223 if (!eap) { 1224 if (interworking_connect_3gpp(wpa_s, bss) == 0) { 1225 if (realm) 1226 nai_realm_free(realm, count); 1227 return 0; 1228 } 1229 1230 wpa_printf(MSG_DEBUG, "Interworking: No matching credentials " 1231 "and EAP method found for " MACSTR, 1232 MAC2STR(bss->bssid)); 1233 nai_realm_free(realm, count); 1234 return -1; 1235 } 1236 1237 wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR, 1238 MAC2STR(bss->bssid)); 1239 1240 ssid = wpa_config_add_network(wpa_s->conf); 1241 if (ssid == NULL) { 1242 nai_realm_free(realm, count); 1243 return -1; 1244 } 1245 ssid->parent_cred = cred; 1246 wpas_notify_network_added(wpa_s, ssid); 1247 wpa_config_set_network_defaults(ssid); 1248 ssid->priority = cred->priority; 1249 ssid->temporary = 1; 1250 ssid->ssid = os_zalloc(ie[1] + 1); 1251 if (ssid->ssid == NULL) 1252 goto fail; 1253 os_memcpy(ssid->ssid, ie + 2, ie[1]); 1254 ssid->ssid_len = ie[1]; 1255 1256 if (interworking_set_hs20_params(wpa_s, ssid) < 0) 1257 goto fail; 1258 1259 if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF, 1260 eap->method), 0) < 0) 1261 goto fail; 1262 1263 switch (eap->method) { 1264 case EAP_TYPE_TTLS: 1265 if (eap->inner_method) { 1266 os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", 1267 eap_get_name(EAP_VENDOR_IETF, 1268 eap->inner_method)); 1269 if (wpa_config_set(ssid, "phase2", buf, 0) < 0) 1270 goto fail; 1271 break; 1272 } 1273 switch (eap->inner_non_eap) { 1274 case NAI_REALM_INNER_NON_EAP_PAP: 1275 if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) < 1276 0) 1277 goto fail; 1278 break; 1279 case NAI_REALM_INNER_NON_EAP_CHAP: 1280 if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0) 1281 < 0) 1282 goto fail; 1283 break; 1284 case NAI_REALM_INNER_NON_EAP_MSCHAP: 1285 if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"", 1286 0) < 0) 1287 goto fail; 1288 break; 1289 case NAI_REALM_INNER_NON_EAP_MSCHAPV2: 1290 if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"", 1291 0) < 0) 1292 goto fail; 1293 break; 1294 default: 1295 /* EAP params were not set - assume TTLS/MSCHAPv2 */ 1296 if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"", 1297 0) < 0) 1298 goto fail; 1299 break; 1300 } 1301 break; 1302 case EAP_TYPE_PEAP: 1303 os_snprintf(buf, sizeof(buf), "\"auth=%s\"", 1304 eap_get_name(EAP_VENDOR_IETF, 1305 eap->inner_method ? 1306 eap->inner_method : 1307 EAP_TYPE_MSCHAPV2)); 1308 if (wpa_config_set(ssid, "phase2", buf, 0) < 0) 1309 goto fail; 1310 break; 1311 case EAP_TYPE_TLS: 1312 break; 1313 } 1314 1315 if (interworking_set_eap_params(ssid, cred, 1316 eap->method == EAP_TYPE_TTLS) < 0) 1317 goto fail; 1318 1319 nai_realm_free(realm, count); 1320 1321 wpa_config_update_prio_list(wpa_s->conf); 1322 interworking_reconnect(wpa_s); 1323 1324 return 0; 1325 1326fail: 1327 wpas_notify_network_removed(wpa_s, ssid); 1328 wpa_config_remove_network(wpa_s->conf, ssid->id); 1329 nai_realm_free(realm, count); 1330 return -1; 1331} 1332 1333 1334static struct wpa_cred * interworking_credentials_available_3gpp( 1335 struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 1336{ 1337 struct wpa_cred *cred, *selected = NULL; 1338 int ret; 1339 1340#ifdef INTERWORKING_3GPP 1341 if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) 1342 return NULL; 1343 1344 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 1345 char *sep; 1346 const char *imsi; 1347 int mnc_len; 1348 1349#ifdef PCSC_FUNCS 1350 if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard && 1351 wpa_s->imsi[0]) { 1352 imsi = wpa_s->imsi; 1353 mnc_len = wpa_s->mnc_len; 1354 goto compare; 1355 } 1356#endif /* PCSC_FUNCS */ 1357 1358 if (cred->imsi == NULL || !cred->imsi[0] || 1359 cred->milenage == NULL || !cred->milenage[0]) 1360 continue; 1361 1362 sep = os_strchr(cred->imsi, '-'); 1363 if (sep == NULL || 1364 (sep - cred->imsi != 5 && sep - cred->imsi != 6)) 1365 continue; 1366 mnc_len = sep - cred->imsi - 3; 1367 imsi = cred->imsi; 1368 1369#ifdef PCSC_FUNCS 1370 compare: 1371#endif /* PCSC_FUNCS */ 1372 wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " 1373 MACSTR, MAC2STR(bss->bssid)); 1374 ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len); 1375 wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); 1376 if (ret) { 1377 if (cred_excluded_ssid(cred, bss)) 1378 continue; 1379 if (selected == NULL || 1380 selected->priority < cred->priority) 1381 selected = cred; 1382 } 1383 } 1384#endif /* INTERWORKING_3GPP */ 1385 return selected; 1386} 1387 1388 1389static struct wpa_cred * interworking_credentials_available_realm( 1390 struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 1391{ 1392 struct wpa_cred *cred, *selected = NULL; 1393 struct nai_realm *realm; 1394 u16 count, i; 1395 1396 if (bss->anqp == NULL || bss->anqp->nai_realm == NULL) 1397 return NULL; 1398 1399 if (wpa_s->conf->cred == NULL) 1400 return NULL; 1401 1402 wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from " 1403 MACSTR, MAC2STR(bss->bssid)); 1404 realm = nai_realm_parse(bss->anqp->nai_realm, &count); 1405 if (realm == NULL) { 1406 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " 1407 "Realm list from " MACSTR, MAC2STR(bss->bssid)); 1408 return NULL; 1409 } 1410 1411 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 1412 if (cred->realm == NULL) 1413 continue; 1414 1415 for (i = 0; i < count; i++) { 1416 if (!nai_realm_match(&realm[i], cred->realm)) 1417 continue; 1418 if (nai_realm_find_eap(cred, &realm[i])) { 1419 if (cred_excluded_ssid(cred, bss)) 1420 continue; 1421 if (selected == NULL || 1422 selected->priority < cred->priority) 1423 selected = cred; 1424 break; 1425 } 1426 } 1427 } 1428 1429 nai_realm_free(realm, count); 1430 1431 return selected; 1432} 1433 1434 1435static struct wpa_cred * interworking_credentials_available( 1436 struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 1437{ 1438 struct wpa_cred *cred, *cred2; 1439 1440 cred = interworking_credentials_available_realm(wpa_s, bss); 1441 cred2 = interworking_credentials_available_3gpp(wpa_s, bss); 1442 if (cred && cred2 && cred2->priority >= cred->priority) 1443 cred = cred2; 1444 if (!cred) 1445 cred = cred2; 1446 1447 cred2 = interworking_credentials_available_roaming_consortium(wpa_s, 1448 bss); 1449 if (cred && cred2 && cred2->priority >= cred->priority) 1450 cred = cred2; 1451 if (!cred) 1452 cred = cred2; 1453 1454 return cred; 1455} 1456 1457 1458static int domain_name_list_contains(struct wpabuf *domain_names, 1459 const char *domain) 1460{ 1461 const u8 *pos, *end; 1462 size_t len; 1463 1464 len = os_strlen(domain); 1465 pos = wpabuf_head(domain_names); 1466 end = pos + wpabuf_len(domain_names); 1467 1468 while (pos + 1 < end) { 1469 if (pos + 1 + pos[0] > end) 1470 break; 1471 1472 wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name", 1473 pos + 1, pos[0]); 1474 if (pos[0] == len && 1475 os_strncasecmp(domain, (const char *) (pos + 1), len) == 0) 1476 return 1; 1477 1478 pos += 1 + pos[0]; 1479 } 1480 1481 return 0; 1482} 1483 1484 1485int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, 1486 struct wpa_cred *cred, 1487 struct wpabuf *domain_names) 1488{ 1489#ifdef INTERWORKING_3GPP 1490 char nai[100], *realm; 1491 1492 char *imsi = NULL; 1493 int mnc_len = 0; 1494 if (cred->imsi) 1495 imsi = cred->imsi; 1496#ifdef CONFIG_PCSC 1497 else if (cred->pcsc && wpa_s->conf->pcsc_reader && 1498 wpa_s->scard && wpa_s->imsi[0]) { 1499 imsi = wpa_s->imsi; 1500 mnc_len = wpa_s->mnc_len; 1501 } 1502#endif /* CONFIG_PCSC */ 1503 if (domain_names && 1504 imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) { 1505 realm = os_strchr(nai, '@'); 1506 if (realm) 1507 realm++; 1508 wpa_printf(MSG_DEBUG, "Interworking: Search for match " 1509 "with SIM/USIM domain %s", realm); 1510 if (realm && 1511 domain_name_list_contains(domain_names, realm)) 1512 return 1; 1513 } 1514#endif /* INTERWORKING_3GPP */ 1515 1516 if (domain_names == NULL || cred->domain == NULL) 1517 return 0; 1518 1519 wpa_printf(MSG_DEBUG, "Interworking: Search for match with " 1520 "home SP FQDN %s", cred->domain); 1521 if (domain_name_list_contains(domain_names, cred->domain)) 1522 return 1; 1523 1524 return 0; 1525} 1526 1527 1528static int interworking_home_sp(struct wpa_supplicant *wpa_s, 1529 struct wpabuf *domain_names) 1530{ 1531 struct wpa_cred *cred; 1532 1533 if (domain_names == NULL || wpa_s->conf->cred == NULL) 1534 return -1; 1535 1536 for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 1537 int res = interworking_home_sp_cred(wpa_s, cred, domain_names); 1538 if (res) 1539 return res; 1540 } 1541 1542 return 0; 1543} 1544 1545 1546static int interworking_find_network_match(struct wpa_supplicant *wpa_s) 1547{ 1548 struct wpa_bss *bss; 1549 struct wpa_ssid *ssid; 1550 1551 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 1552 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { 1553 if (wpas_network_disabled(wpa_s, ssid) || 1554 ssid->mode != WPAS_MODE_INFRA) 1555 continue; 1556 if (ssid->ssid_len != bss->ssid_len || 1557 os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != 1558 0) 1559 continue; 1560 /* 1561 * TODO: Consider more accurate matching of security 1562 * configuration similarly to what is done in events.c 1563 */ 1564 return 1; 1565 } 1566 } 1567 1568 return 0; 1569} 1570 1571 1572static void interworking_select_network(struct wpa_supplicant *wpa_s) 1573{ 1574 struct wpa_bss *bss, *selected = NULL, *selected_home = NULL; 1575 int selected_prio = -999999, selected_home_prio = -999999; 1576 unsigned int count = 0; 1577 const char *type; 1578 int res; 1579 struct wpa_cred *cred; 1580 1581 wpa_s->network_select = 0; 1582 1583 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 1584 cred = interworking_credentials_available(wpa_s, bss); 1585 if (!cred) 1586 continue; 1587 if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { 1588 /* 1589 * We currently support only HS 2.0 networks and those 1590 * are required to use WPA2-Enterprise. 1591 */ 1592 wpa_printf(MSG_DEBUG, "Interworking: Credential match " 1593 "with " MACSTR " but network does not use " 1594 "RSN", MAC2STR(bss->bssid)); 1595 continue; 1596 } 1597 count++; 1598 res = interworking_home_sp(wpa_s, bss->anqp ? 1599 bss->anqp->domain_name : NULL); 1600 if (res > 0) 1601 type = "home"; 1602 else if (res == 0) 1603 type = "roaming"; 1604 else 1605 type = "unknown"; 1606 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s", 1607 MAC2STR(bss->bssid), type); 1608 if (wpa_s->auto_select || 1609 (wpa_s->conf->auto_interworking && 1610 wpa_s->auto_network_select)) { 1611 if (selected == NULL || 1612 cred->priority > selected_prio) { 1613 selected = bss; 1614 selected_prio = cred->priority; 1615 } 1616 if (res > 0 && 1617 (selected_home == NULL || 1618 cred->priority > selected_home_prio)) { 1619 selected_home = bss; 1620 selected_home_prio = cred->priority; 1621 } 1622 } 1623 } 1624 1625 if (selected_home && selected_home != selected && 1626 selected_home_prio >= selected_prio) { 1627 /* Prefer network operated by the Home SP */ 1628 selected = selected_home; 1629 } 1630 1631 if (count == 0) { 1632 /* 1633 * No matching network was found based on configured 1634 * credentials. Check whether any of the enabled network blocks 1635 * have matching APs. 1636 */ 1637 if (interworking_find_network_match(wpa_s)) { 1638 wpa_printf(MSG_DEBUG, "Interworking: Possible BSS " 1639 "match for enabled network configurations"); 1640 if (wpa_s->auto_select) 1641 interworking_reconnect(wpa_s); 1642 return; 1643 } 1644 1645 if (wpa_s->auto_network_select) { 1646 wpa_printf(MSG_DEBUG, "Interworking: Continue " 1647 "scanning after ANQP fetch"); 1648 wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, 1649 0); 1650 return; 1651 } 1652 1653 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network " 1654 "with matching credentials found"); 1655 } 1656 1657 if (selected) 1658 interworking_connect(wpa_s, selected); 1659} 1660 1661 1662static struct wpa_bss_anqp * 1663interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 1664{ 1665 struct wpa_bss *other; 1666 1667 if (is_zero_ether_addr(bss->hessid)) 1668 return NULL; /* Cannot be in the same homegenous ESS */ 1669 1670 dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) { 1671 if (other == bss) 1672 continue; 1673 if (other->anqp == NULL) 1674 continue; 1675 if (other->anqp->roaming_consortium == NULL && 1676 other->anqp->nai_realm == NULL && 1677 other->anqp->anqp_3gpp == NULL && 1678 other->anqp->domain_name == NULL) 1679 continue; 1680 if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED)) 1681 continue; 1682 if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0) 1683 continue; 1684 if (bss->ssid_len != other->ssid_len || 1685 os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0) 1686 continue; 1687 1688 wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with " 1689 "already fetched BSSID " MACSTR " and " MACSTR, 1690 MAC2STR(other->bssid), MAC2STR(bss->bssid)); 1691 other->anqp->users++; 1692 return other->anqp; 1693 } 1694 1695 return NULL; 1696} 1697 1698 1699static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) 1700{ 1701 struct wpa_bss *bss; 1702 int found = 0; 1703 const u8 *ie; 1704 1705 if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) 1706 return; 1707 1708 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 1709 if (!(bss->caps & IEEE80211_CAP_ESS)) 1710 continue; 1711 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB); 1712 if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80)) 1713 continue; /* AP does not support Interworking */ 1714 1715 if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) { 1716 if (bss->anqp == NULL) { 1717 bss->anqp = interworking_match_anqp_info(wpa_s, 1718 bss); 1719 if (bss->anqp) { 1720 /* Shared data already fetched */ 1721 continue; 1722 } 1723 bss->anqp = wpa_bss_anqp_alloc(); 1724 if (bss->anqp == NULL) 1725 break; 1726 } 1727 found++; 1728 bss->flags |= WPA_BSS_ANQP_FETCH_TRIED; 1729 wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for " 1730 MACSTR, MAC2STR(bss->bssid)); 1731 interworking_anqp_send_req(wpa_s, bss); 1732 break; 1733 } 1734 } 1735 1736 if (found == 0) { 1737 wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed"); 1738 wpa_s->fetch_anqp_in_progress = 0; 1739 if (wpa_s->network_select) 1740 interworking_select_network(wpa_s); 1741 } 1742} 1743 1744 1745void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s) 1746{ 1747 struct wpa_bss *bss; 1748 1749 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) 1750 bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED; 1751 1752 wpa_s->fetch_anqp_in_progress = 1; 1753 interworking_next_anqp_fetch(wpa_s); 1754} 1755 1756 1757int interworking_fetch_anqp(struct wpa_supplicant *wpa_s) 1758{ 1759 if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) 1760 return 0; 1761 1762 wpa_s->network_select = 0; 1763 wpa_s->fetch_all_anqp = 1; 1764 1765 interworking_start_fetch_anqp(wpa_s); 1766 1767 return 0; 1768} 1769 1770 1771void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s) 1772{ 1773 if (!wpa_s->fetch_anqp_in_progress) 1774 return; 1775 1776 wpa_s->fetch_anqp_in_progress = 0; 1777} 1778 1779 1780int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, 1781 u16 info_ids[], size_t num_ids) 1782{ 1783 struct wpabuf *buf; 1784 int ret = 0; 1785 int freq; 1786 struct wpa_bss *bss; 1787 int res; 1788 1789 freq = wpa_s->assoc_freq; 1790 bss = wpa_bss_get_bssid(wpa_s, dst); 1791 if (bss) { 1792 wpa_bss_anqp_unshare_alloc(bss); 1793 freq = bss->freq; 1794 } 1795 if (freq <= 0) 1796 return -1; 1797 1798 wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)", 1799 MAC2STR(dst), (unsigned int) num_ids); 1800 1801 buf = anqp_build_req(info_ids, num_ids, NULL); 1802 if (buf == NULL) 1803 return -1; 1804 1805 res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); 1806 if (res < 0) { 1807 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); 1808 ret = -1; 1809 } else 1810 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " 1811 "%u", res); 1812 1813 wpabuf_free(buf); 1814 return ret; 1815} 1816 1817 1818static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, 1819 const u8 *sa, u16 info_id, 1820 const u8 *data, size_t slen) 1821{ 1822 const u8 *pos = data; 1823 struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa); 1824 struct wpa_bss_anqp *anqp = NULL; 1825#ifdef CONFIG_HS20 1826 u8 type; 1827#endif /* CONFIG_HS20 */ 1828 1829 if (bss) 1830 anqp = bss->anqp; 1831 1832 switch (info_id) { 1833 case ANQP_CAPABILITY_LIST: 1834 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR 1835 " ANQP Capability list", MAC2STR(sa)); 1836 break; 1837 case ANQP_VENUE_NAME: 1838 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR 1839 " Venue Name", MAC2STR(sa)); 1840 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen); 1841 if (anqp) { 1842 wpabuf_free(anqp->venue_name); 1843 anqp->venue_name = wpabuf_alloc_copy(pos, slen); 1844 } 1845 break; 1846 case ANQP_NETWORK_AUTH_TYPE: 1847 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR 1848 " Network Authentication Type information", 1849 MAC2STR(sa)); 1850 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication " 1851 "Type", pos, slen); 1852 if (anqp) { 1853 wpabuf_free(anqp->network_auth_type); 1854 anqp->network_auth_type = wpabuf_alloc_copy(pos, slen); 1855 } 1856 break; 1857 case ANQP_ROAMING_CONSORTIUM: 1858 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR 1859 " Roaming Consortium list", MAC2STR(sa)); 1860 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium", 1861 pos, slen); 1862 if (anqp) { 1863 wpabuf_free(anqp->roaming_consortium); 1864 anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen); 1865 } 1866 break; 1867 case ANQP_IP_ADDR_TYPE_AVAILABILITY: 1868 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR 1869 " IP Address Type Availability information", 1870 MAC2STR(sa)); 1871 wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability", 1872 pos, slen); 1873 if (anqp) { 1874 wpabuf_free(anqp->ip_addr_type_availability); 1875 anqp->ip_addr_type_availability = 1876 wpabuf_alloc_copy(pos, slen); 1877 } 1878 break; 1879 case ANQP_NAI_REALM: 1880 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR 1881 " NAI Realm list", MAC2STR(sa)); 1882 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen); 1883 if (anqp) { 1884 wpabuf_free(anqp->nai_realm); 1885 anqp->nai_realm = wpabuf_alloc_copy(pos, slen); 1886 } 1887 break; 1888 case ANQP_3GPP_CELLULAR_NETWORK: 1889 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR 1890 " 3GPP Cellular Network information", MAC2STR(sa)); 1891 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network", 1892 pos, slen); 1893 if (anqp) { 1894 wpabuf_free(anqp->anqp_3gpp); 1895 anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen); 1896 } 1897 break; 1898 case ANQP_DOMAIN_NAME: 1899 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR 1900 " Domain Name list", MAC2STR(sa)); 1901 wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen); 1902 if (anqp) { 1903 wpabuf_free(anqp->domain_name); 1904 anqp->domain_name = wpabuf_alloc_copy(pos, slen); 1905 } 1906 break; 1907 case ANQP_VENDOR_SPECIFIC: 1908 if (slen < 3) 1909 return; 1910 1911 switch (WPA_GET_BE24(pos)) { 1912#ifdef CONFIG_HS20 1913 case OUI_WFA: 1914 pos += 3; 1915 slen -= 3; 1916 1917 if (slen < 1) 1918 return; 1919 type = *pos++; 1920 slen--; 1921 1922 switch (type) { 1923 case HS20_ANQP_OUI_TYPE: 1924 hs20_parse_rx_hs20_anqp_resp(wpa_s, sa, pos, 1925 slen); 1926 break; 1927 default: 1928 wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP " 1929 "vendor type %u", type); 1930 break; 1931 } 1932 break; 1933#endif /* CONFIG_HS20 */ 1934 default: 1935 wpa_printf(MSG_DEBUG, "Interworking: Unsupported " 1936 "vendor-specific ANQP OUI %06x", 1937 WPA_GET_BE24(pos)); 1938 return; 1939 } 1940 break; 1941 default: 1942 wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID " 1943 "%u", info_id); 1944 break; 1945 } 1946} 1947 1948 1949void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, 1950 enum gas_query_result result, 1951 const struct wpabuf *adv_proto, 1952 const struct wpabuf *resp, u16 status_code) 1953{ 1954 struct wpa_supplicant *wpa_s = ctx; 1955 const u8 *pos; 1956 const u8 *end; 1957 u16 info_id; 1958 u16 slen; 1959 1960 if (result != GAS_QUERY_SUCCESS) 1961 return; 1962 1963 pos = wpabuf_head(adv_proto); 1964 if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO || 1965 pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) { 1966 wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement " 1967 "Protocol in response"); 1968 return; 1969 } 1970 1971 pos = wpabuf_head(resp); 1972 end = pos + wpabuf_len(resp); 1973 1974 while (pos < end) { 1975 if (pos + 4 > end) { 1976 wpa_printf(MSG_DEBUG, "ANQP: Invalid element"); 1977 break; 1978 } 1979 info_id = WPA_GET_LE16(pos); 1980 pos += 2; 1981 slen = WPA_GET_LE16(pos); 1982 pos += 2; 1983 if (pos + slen > end) { 1984 wpa_printf(MSG_DEBUG, "ANQP: Invalid element length " 1985 "for Info ID %u", info_id); 1986 break; 1987 } 1988 interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos, 1989 slen); 1990 pos += slen; 1991 } 1992} 1993 1994 1995static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s, 1996 struct wpa_scan_results *scan_res) 1997{ 1998 wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start " 1999 "ANQP fetch"); 2000 interworking_start_fetch_anqp(wpa_s); 2001} 2002 2003 2004int interworking_select(struct wpa_supplicant *wpa_s, int auto_select) 2005{ 2006 interworking_stop_fetch_anqp(wpa_s); 2007 wpa_s->network_select = 1; 2008 wpa_s->auto_network_select = 0; 2009 wpa_s->auto_select = !!auto_select; 2010 wpa_s->fetch_all_anqp = 0; 2011 wpa_printf(MSG_DEBUG, "Interworking: Start scan for network " 2012 "selection"); 2013 wpa_s->scan_res_handler = interworking_scan_res_handler; 2014 wpa_s->scan_req = MANUAL_SCAN_REQ; 2015 wpa_supplicant_req_scan(wpa_s, 0, 0); 2016 2017 return 0; 2018} 2019 2020 2021static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, 2022 enum gas_query_result result, 2023 const struct wpabuf *adv_proto, 2024 const struct wpabuf *resp, u16 status_code) 2025{ 2026 struct wpa_supplicant *wpa_s = ctx; 2027 2028 wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR 2029 " dialog_token=%d status_code=%d resp_len=%d", 2030 MAC2STR(addr), dialog_token, status_code, 2031 resp ? (int) wpabuf_len(resp) : -1); 2032 if (!resp) 2033 return; 2034 2035 wpabuf_free(wpa_s->last_gas_resp); 2036 wpa_s->last_gas_resp = wpabuf_dup(resp); 2037 if (wpa_s->last_gas_resp == NULL) 2038 return; 2039 os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN); 2040 wpa_s->last_gas_dialog_token = dialog_token; 2041} 2042 2043 2044int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, 2045 const struct wpabuf *adv_proto, 2046 const struct wpabuf *query) 2047{ 2048 struct wpabuf *buf; 2049 int ret = 0; 2050 int freq; 2051 struct wpa_bss *bss; 2052 int res; 2053 size_t len; 2054 u8 query_resp_len_limit = 0, pame_bi = 0; 2055 2056 freq = wpa_s->assoc_freq; 2057 bss = wpa_bss_get_bssid(wpa_s, dst); 2058 if (bss) 2059 freq = bss->freq; 2060 if (freq <= 0) 2061 return -1; 2062 2063 wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)", 2064 MAC2STR(dst), freq); 2065 wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto); 2066 wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query); 2067 2068 len = 3 + wpabuf_len(adv_proto) + 2; 2069 if (query) 2070 len += wpabuf_len(query); 2071 buf = gas_build_initial_req(0, len); 2072 if (buf == NULL) 2073 return -1; 2074 2075 /* Advertisement Protocol IE */ 2076 wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); 2077 wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */ 2078 wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | 2079 (pame_bi ? 0x80 : 0)); 2080 wpabuf_put_buf(buf, adv_proto); 2081 2082 /* GAS Query */ 2083 if (query) { 2084 wpabuf_put_le16(buf, wpabuf_len(query)); 2085 wpabuf_put_buf(buf, query); 2086 } else 2087 wpabuf_put_le16(buf, 0); 2088 2089 res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s); 2090 if (res < 0) { 2091 wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request"); 2092 ret = -1; 2093 } else 2094 wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token " 2095 "%u", res); 2096 2097 wpabuf_free(buf); 2098 return ret; 2099} 2100