1f05cddf9SRui Paulo /* 2f05cddf9SRui Paulo * Copyright (c) 2009, Atheros Communications, Inc. 35b9c547cSRui Paulo * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. 4f05cddf9SRui Paulo * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 7f05cddf9SRui Paulo */ 8f05cddf9SRui Paulo 9f05cddf9SRui Paulo #include "includes.h" 105b9c547cSRui Paulo #include <sys/stat.h> 11f05cddf9SRui Paulo 12f05cddf9SRui Paulo #include "common.h" 13f05cddf9SRui Paulo #include "eloop.h" 14f05cddf9SRui Paulo #include "common/ieee802_11_common.h" 15f05cddf9SRui Paulo #include "common/ieee802_11_defs.h" 16f05cddf9SRui Paulo #include "common/gas.h" 17f05cddf9SRui Paulo #include "common/wpa_ctrl.h" 185b9c547cSRui Paulo #include "rsn_supp/wpa.h" 19f05cddf9SRui Paulo #include "wpa_supplicant_i.h" 20f05cddf9SRui Paulo #include "driver_i.h" 21f05cddf9SRui Paulo #include "config.h" 225b9c547cSRui Paulo #include "scan.h" 23f05cddf9SRui Paulo #include "bss.h" 24*c1d255d3SCy Schubert #include "bssid_ignore.h" 25f05cddf9SRui Paulo #include "gas_query.h" 26f05cddf9SRui Paulo #include "interworking.h" 27f05cddf9SRui Paulo #include "hs20_supplicant.h" 28780fb4a2SCy Schubert #include "base64.h" 29f05cddf9SRui Paulo 30f05cddf9SRui Paulo 315b9c547cSRui Paulo #define OSU_MAX_ITEMS 10 325b9c547cSRui Paulo 335b9c547cSRui Paulo struct osu_lang_string { 345b9c547cSRui Paulo char lang[4]; 355b9c547cSRui Paulo char text[253]; 365b9c547cSRui Paulo }; 375b9c547cSRui Paulo 385b9c547cSRui Paulo struct osu_icon { 395b9c547cSRui Paulo u16 width; 405b9c547cSRui Paulo u16 height; 415b9c547cSRui Paulo char lang[4]; 425b9c547cSRui Paulo char icon_type[256]; 435b9c547cSRui Paulo char filename[256]; 445b9c547cSRui Paulo unsigned int id; 455b9c547cSRui Paulo unsigned int failed:1; 465b9c547cSRui Paulo }; 475b9c547cSRui Paulo 485b9c547cSRui Paulo struct osu_provider { 495b9c547cSRui Paulo u8 bssid[ETH_ALEN]; 50325151a3SRui Paulo u8 osu_ssid[SSID_MAX_LEN]; 515b9c547cSRui Paulo u8 osu_ssid_len; 5285732ac8SCy Schubert u8 osu_ssid2[SSID_MAX_LEN]; 5385732ac8SCy Schubert u8 osu_ssid2_len; 545b9c547cSRui Paulo char server_uri[256]; 555b9c547cSRui Paulo u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */ 565b9c547cSRui Paulo char osu_nai[256]; 5785732ac8SCy Schubert char osu_nai2[256]; 585b9c547cSRui Paulo struct osu_lang_string friendly_name[OSU_MAX_ITEMS]; 595b9c547cSRui Paulo size_t friendly_name_count; 605b9c547cSRui Paulo struct osu_lang_string serv_desc[OSU_MAX_ITEMS]; 615b9c547cSRui Paulo size_t serv_desc_count; 625b9c547cSRui Paulo struct osu_icon icon[OSU_MAX_ITEMS]; 635b9c547cSRui Paulo size_t icon_count; 645b9c547cSRui Paulo }; 655b9c547cSRui Paulo 665b9c547cSRui Paulo 67780fb4a2SCy Schubert void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s) 68780fb4a2SCy Schubert { 69780fb4a2SCy Schubert struct wpa_bss *bss = wpa_s->current_bss; 70780fb4a2SCy Schubert u8 *bssid = wpa_s->bssid; 71780fb4a2SCy Schubert const u8 *ie; 72780fb4a2SCy Schubert const u8 *ext_capa; 73780fb4a2SCy Schubert u32 filter = 0; 74780fb4a2SCy Schubert 75780fb4a2SCy Schubert if (!bss || !is_hs20_network(wpa_s, wpa_s->current_ssid, bss)) { 76780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 77780fb4a2SCy Schubert "Not configuring frame filtering - BSS " MACSTR 78780fb4a2SCy Schubert " is not a Hotspot 2.0 network", MAC2STR(bssid)); 79780fb4a2SCy Schubert return; 80780fb4a2SCy Schubert } 81780fb4a2SCy Schubert 82780fb4a2SCy Schubert ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE); 83780fb4a2SCy Schubert 84780fb4a2SCy Schubert /* Check if DGAF disabled bit is zero (5th byte in the IE) */ 85780fb4a2SCy Schubert if (!ie || ie[1] < 5) 86780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 87780fb4a2SCy Schubert "Not configuring frame filtering - Can't extract DGAF bit"); 88780fb4a2SCy Schubert else if (!(ie[6] & HS20_DGAF_DISABLED)) 89780fb4a2SCy Schubert filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK; 90780fb4a2SCy Schubert 91780fb4a2SCy Schubert ext_capa = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB); 92780fb4a2SCy Schubert if (!ext_capa || ext_capa[1] < 2) { 93780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 94780fb4a2SCy Schubert "Not configuring frame filtering - Can't extract Proxy ARP bit"); 95780fb4a2SCy Schubert return; 96780fb4a2SCy Schubert } 97780fb4a2SCy Schubert 984bc52338SCy Schubert if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_PROXY_ARP)) 99780fb4a2SCy Schubert filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP | 100780fb4a2SCy Schubert WPA_DATA_FRAME_FILTER_FLAG_NA; 101780fb4a2SCy Schubert 102780fb4a2SCy Schubert wpa_drv_configure_frame_filters(wpa_s, filter); 103780fb4a2SCy Schubert } 104780fb4a2SCy Schubert 105780fb4a2SCy Schubert 1064bc52338SCy Schubert void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id, int ap_release) 107f05cddf9SRui Paulo { 1084bc52338SCy Schubert int release; 1095b9c547cSRui Paulo u8 conf; 1105b9c547cSRui Paulo 1114bc52338SCy Schubert release = (HS20_VERSION >> 4) + 1; 1124bc52338SCy Schubert if (ap_release > 0 && release > ap_release) 1134bc52338SCy Schubert release = ap_release; 1144bc52338SCy Schubert if (release < 2) 1154bc52338SCy Schubert pps_mo_id = -1; 1164bc52338SCy Schubert 117f05cddf9SRui Paulo wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); 1185b9c547cSRui Paulo wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5); 119f05cddf9SRui Paulo wpabuf_put_be24(buf, OUI_WFA); 120f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE); 1214bc52338SCy Schubert conf = (release - 1) << 4; 1225b9c547cSRui Paulo if (pps_mo_id >= 0) 1235b9c547cSRui Paulo conf |= HS20_PPS_MO_ID_PRESENT; 1245b9c547cSRui Paulo wpabuf_put_u8(buf, conf); 1255b9c547cSRui Paulo if (pps_mo_id >= 0) 1265b9c547cSRui Paulo wpabuf_put_le16(buf, pps_mo_id); 127f05cddf9SRui Paulo } 128f05cddf9SRui Paulo 129f05cddf9SRui Paulo 13085732ac8SCy Schubert void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf, 13185732ac8SCy Schubert const struct wpa_ssid *ssid) 13285732ac8SCy Schubert { 13385732ac8SCy Schubert if (!ssid->roaming_consortium_selection || 13485732ac8SCy Schubert !ssid->roaming_consortium_selection_len) 13585732ac8SCy Schubert return; 13685732ac8SCy Schubert 13785732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); 13885732ac8SCy Schubert wpabuf_put_u8(buf, 4 + ssid->roaming_consortium_selection_len); 13985732ac8SCy Schubert wpabuf_put_be24(buf, OUI_WFA); 14085732ac8SCy Schubert wpabuf_put_u8(buf, HS20_ROAMING_CONS_SEL_OUI_TYPE); 14185732ac8SCy Schubert wpabuf_put_data(buf, ssid->roaming_consortium_selection, 14285732ac8SCy Schubert ssid->roaming_consortium_selection_len); 14385732ac8SCy Schubert } 14485732ac8SCy Schubert 14585732ac8SCy Schubert 1464bc52338SCy Schubert int get_hs20_version(struct wpa_bss *bss) 1474bc52338SCy Schubert { 1484bc52338SCy Schubert const u8 *ie; 1494bc52338SCy Schubert 1504bc52338SCy Schubert if (!bss) 1514bc52338SCy Schubert return 0; 1524bc52338SCy Schubert 1534bc52338SCy Schubert ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE); 1544bc52338SCy Schubert if (!ie || ie[1] < 5) 1554bc52338SCy Schubert return 0; 1564bc52338SCy Schubert 1574bc52338SCy Schubert return ((ie[6] >> 4) & 0x0f) + 1; 1584bc52338SCy Schubert } 1594bc52338SCy Schubert 1604bc52338SCy Schubert 1615b9c547cSRui Paulo int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, 1625b9c547cSRui Paulo struct wpa_bss *bss) 163f05cddf9SRui Paulo { 1645b9c547cSRui Paulo if (!wpa_s->conf->hs20 || !ssid) 1655b9c547cSRui Paulo return 0; 1665b9c547cSRui Paulo 1675b9c547cSRui Paulo if (ssid->parent_cred) 1685b9c547cSRui Paulo return 1; 1695b9c547cSRui Paulo 1705b9c547cSRui Paulo if (bss && !wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) 1715b9c547cSRui Paulo return 0; 1725b9c547cSRui Paulo 1735b9c547cSRui Paulo /* 1745b9c547cSRui Paulo * This may catch some non-Hotspot 2.0 cases, but it is safer to do that 1755b9c547cSRui Paulo * than cause Hotspot 2.0 connections without indication element getting 1765b9c547cSRui Paulo * added. Non-Hotspot 2.0 APs should ignore the unknown vendor element. 1775b9c547cSRui Paulo */ 1785b9c547cSRui Paulo 1795b9c547cSRui Paulo if (!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X)) 1805b9c547cSRui Paulo return 0; 1815b9c547cSRui Paulo if (!(ssid->pairwise_cipher & WPA_CIPHER_CCMP)) 1825b9c547cSRui Paulo return 0; 1835b9c547cSRui Paulo if (ssid->proto != WPA_PROTO_RSN) 1845b9c547cSRui Paulo return 0; 1855b9c547cSRui Paulo 1865b9c547cSRui Paulo return 1; 1875b9c547cSRui Paulo } 1885b9c547cSRui Paulo 1895b9c547cSRui Paulo 1905b9c547cSRui Paulo int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) 1915b9c547cSRui Paulo { 1925b9c547cSRui Paulo struct wpa_cred *cred; 1935b9c547cSRui Paulo 1945b9c547cSRui Paulo if (ssid == NULL) 1955b9c547cSRui Paulo return 0; 1965b9c547cSRui Paulo 1975b9c547cSRui Paulo if (ssid->update_identifier) 1985b9c547cSRui Paulo return ssid->update_identifier; 1995b9c547cSRui Paulo 2005b9c547cSRui Paulo if (ssid->parent_cred == NULL) 2015b9c547cSRui Paulo return 0; 2025b9c547cSRui Paulo 2035b9c547cSRui Paulo for (cred = wpa_s->conf->cred; cred; cred = cred->next) { 2045b9c547cSRui Paulo if (ssid->parent_cred == cred) 2055b9c547cSRui Paulo return cred->update_identifier; 2065b9c547cSRui Paulo } 2075b9c547cSRui Paulo 2085b9c547cSRui Paulo return 0; 2095b9c547cSRui Paulo } 2105b9c547cSRui Paulo 2115b9c547cSRui Paulo 2125b9c547cSRui Paulo void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, 2135b9c547cSRui Paulo struct wpabuf *buf) 2145b9c547cSRui Paulo { 215f05cddf9SRui Paulo u8 *len_pos; 216f05cddf9SRui Paulo 217f05cddf9SRui Paulo if (buf == NULL) 2185b9c547cSRui Paulo return; 219f05cddf9SRui Paulo 220f05cddf9SRui Paulo len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); 221f05cddf9SRui Paulo wpabuf_put_be24(buf, OUI_WFA); 222f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); 223f05cddf9SRui Paulo if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) { 224f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); 225f05cddf9SRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */ 226f05cddf9SRui Paulo if (payload) 227f05cddf9SRui Paulo wpabuf_put_data(buf, payload, payload_len); 2285b9c547cSRui Paulo } else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) { 2295b9c547cSRui Paulo wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); 2305b9c547cSRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */ 2315b9c547cSRui Paulo if (payload) 2325b9c547cSRui Paulo wpabuf_put_data(buf, payload, payload_len); 233f05cddf9SRui Paulo } else { 234f05cddf9SRui Paulo u8 i; 235f05cddf9SRui Paulo wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST); 236f05cddf9SRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */ 237f05cddf9SRui Paulo for (i = 0; i < 32; i++) { 238f05cddf9SRui Paulo if (stypes & BIT(i)) 239f05cddf9SRui Paulo wpabuf_put_u8(buf, i); 240f05cddf9SRui Paulo } 241f05cddf9SRui Paulo } 242f05cddf9SRui Paulo gas_anqp_set_element_len(buf, len_pos); 243f05cddf9SRui Paulo 244f05cddf9SRui Paulo gas_anqp_set_len(buf); 2455b9c547cSRui Paulo } 2465b9c547cSRui Paulo 2475b9c547cSRui Paulo 248780fb4a2SCy Schubert static struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, 2495b9c547cSRui Paulo size_t payload_len) 2505b9c547cSRui Paulo { 2515b9c547cSRui Paulo struct wpabuf *buf; 2525b9c547cSRui Paulo 2535b9c547cSRui Paulo buf = gas_anqp_build_initial_req(0, 100 + payload_len); 2545b9c547cSRui Paulo if (buf == NULL) 2555b9c547cSRui Paulo return NULL; 2565b9c547cSRui Paulo 2575b9c547cSRui Paulo hs20_put_anqp_req(stypes, payload, payload_len, buf); 258f05cddf9SRui Paulo 259f05cddf9SRui Paulo return buf; 260f05cddf9SRui Paulo } 261f05cddf9SRui Paulo 262f05cddf9SRui Paulo 263f05cddf9SRui Paulo int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, 264780fb4a2SCy Schubert const u8 *payload, size_t payload_len, int inmem) 265f05cddf9SRui Paulo { 266f05cddf9SRui Paulo struct wpabuf *buf; 267f05cddf9SRui Paulo int ret = 0; 268f05cddf9SRui Paulo int freq; 269f05cddf9SRui Paulo struct wpa_bss *bss; 270f05cddf9SRui Paulo int res; 271780fb4a2SCy Schubert struct icon_entry *icon_entry; 272f05cddf9SRui Paulo 273f05cddf9SRui Paulo bss = wpa_bss_get_bssid(wpa_s, dst); 274325151a3SRui Paulo if (!bss) { 275325151a3SRui Paulo wpa_printf(MSG_WARNING, 276325151a3SRui Paulo "ANQP: Cannot send query to unknown BSS " 277325151a3SRui Paulo MACSTR, MAC2STR(dst)); 278325151a3SRui Paulo return -1; 279325151a3SRui Paulo } 280325151a3SRui Paulo 281f05cddf9SRui Paulo wpa_bss_anqp_unshare_alloc(bss); 282f05cddf9SRui Paulo freq = bss->freq; 283f05cddf9SRui Paulo 284f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for " 285f05cddf9SRui Paulo "subtypes 0x%x", MAC2STR(dst), stypes); 286f05cddf9SRui Paulo 287f05cddf9SRui Paulo buf = hs20_build_anqp_req(stypes, payload, payload_len); 288f05cddf9SRui Paulo if (buf == NULL) 289f05cddf9SRui Paulo return -1; 290f05cddf9SRui Paulo 291*c1d255d3SCy Schubert res = gas_query_req(wpa_s->gas, dst, freq, 0, 0, buf, anqp_resp_cb, 292*c1d255d3SCy Schubert wpa_s); 293f05cddf9SRui Paulo if (res < 0) { 294f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); 2955b9c547cSRui Paulo wpabuf_free(buf); 296780fb4a2SCy Schubert return -1; 297f05cddf9SRui Paulo } else 298f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " 299f05cddf9SRui Paulo "%u", res); 300f05cddf9SRui Paulo 301780fb4a2SCy Schubert if (inmem) { 302780fb4a2SCy Schubert icon_entry = os_zalloc(sizeof(struct icon_entry)); 303780fb4a2SCy Schubert if (!icon_entry) 304780fb4a2SCy Schubert return -1; 305780fb4a2SCy Schubert os_memcpy(icon_entry->bssid, dst, ETH_ALEN); 306780fb4a2SCy Schubert icon_entry->file_name = os_malloc(payload_len + 1); 307780fb4a2SCy Schubert if (!icon_entry->file_name) { 308780fb4a2SCy Schubert os_free(icon_entry); 309780fb4a2SCy Schubert return -1; 310780fb4a2SCy Schubert } 311780fb4a2SCy Schubert os_memcpy(icon_entry->file_name, payload, payload_len); 312780fb4a2SCy Schubert icon_entry->file_name[payload_len] = '\0'; 313780fb4a2SCy Schubert icon_entry->dialog_token = res; 314780fb4a2SCy Schubert 315780fb4a2SCy Schubert dl_list_add(&wpa_s->icon_head, &icon_entry->list); 316780fb4a2SCy Schubert } 317780fb4a2SCy Schubert 318f05cddf9SRui Paulo return ret; 319f05cddf9SRui Paulo } 320f05cddf9SRui Paulo 321f05cddf9SRui Paulo 322780fb4a2SCy Schubert static struct icon_entry * hs20_find_icon(struct wpa_supplicant *wpa_s, 323780fb4a2SCy Schubert const u8 *bssid, 324780fb4a2SCy Schubert const char *file_name) 325780fb4a2SCy Schubert { 326780fb4a2SCy Schubert struct icon_entry *icon; 327780fb4a2SCy Schubert 328780fb4a2SCy Schubert dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) { 329780fb4a2SCy Schubert if (os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0 && 330780fb4a2SCy Schubert os_strcmp(icon->file_name, file_name) == 0 && icon->image) 331780fb4a2SCy Schubert return icon; 332780fb4a2SCy Schubert } 333780fb4a2SCy Schubert 334780fb4a2SCy Schubert return NULL; 335780fb4a2SCy Schubert } 336780fb4a2SCy Schubert 337780fb4a2SCy Schubert 338780fb4a2SCy Schubert int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, 339780fb4a2SCy Schubert const char *file_name, size_t offset, size_t size, 340780fb4a2SCy Schubert char *reply, size_t buf_len) 341780fb4a2SCy Schubert { 342780fb4a2SCy Schubert struct icon_entry *icon; 343780fb4a2SCy Schubert size_t out_size; 344*c1d255d3SCy Schubert char *b64; 345780fb4a2SCy Schubert size_t b64_size; 346780fb4a2SCy Schubert int reply_size; 347780fb4a2SCy Schubert 348780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "HS20: Get icon " MACSTR " %s @ %u +%u (%u)", 349780fb4a2SCy Schubert MAC2STR(bssid), file_name, (unsigned int) offset, 350780fb4a2SCy Schubert (unsigned int) size, (unsigned int) buf_len); 351780fb4a2SCy Schubert 352780fb4a2SCy Schubert icon = hs20_find_icon(wpa_s, bssid, file_name); 353780fb4a2SCy Schubert if (!icon || !icon->image || offset >= icon->image_len) 354780fb4a2SCy Schubert return -1; 355780fb4a2SCy Schubert if (size > icon->image_len - offset) 356780fb4a2SCy Schubert size = icon->image_len - offset; 357780fb4a2SCy Schubert out_size = buf_len - 3 /* max base64 padding */; 358780fb4a2SCy Schubert if (size * 4 > out_size * 3) 359780fb4a2SCy Schubert size = out_size * 3 / 4; 360780fb4a2SCy Schubert if (size == 0) 361780fb4a2SCy Schubert return -1; 362780fb4a2SCy Schubert 363780fb4a2SCy Schubert b64 = base64_encode(&icon->image[offset], size, &b64_size); 364780fb4a2SCy Schubert if (b64 && buf_len >= b64_size) { 365780fb4a2SCy Schubert os_memcpy(reply, b64, b64_size); 366780fb4a2SCy Schubert reply_size = b64_size; 367780fb4a2SCy Schubert } else { 368780fb4a2SCy Schubert reply_size = -1; 369780fb4a2SCy Schubert } 370780fb4a2SCy Schubert os_free(b64); 371780fb4a2SCy Schubert return reply_size; 372780fb4a2SCy Schubert } 373780fb4a2SCy Schubert 374780fb4a2SCy Schubert 375780fb4a2SCy Schubert static void hs20_free_icon_entry(struct icon_entry *icon) 376780fb4a2SCy Schubert { 377780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "HS20: Free stored icon from " MACSTR 378780fb4a2SCy Schubert " dialog_token=%u file_name=%s image_len=%u", 379780fb4a2SCy Schubert MAC2STR(icon->bssid), icon->dialog_token, 380780fb4a2SCy Schubert icon->file_name ? icon->file_name : "N/A", 381780fb4a2SCy Schubert (unsigned int) icon->image_len); 382780fb4a2SCy Schubert os_free(icon->file_name); 383780fb4a2SCy Schubert os_free(icon->image); 384780fb4a2SCy Schubert os_free(icon); 385780fb4a2SCy Schubert } 386780fb4a2SCy Schubert 387780fb4a2SCy Schubert 388780fb4a2SCy Schubert int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, 389780fb4a2SCy Schubert const char *file_name) 390780fb4a2SCy Schubert { 391780fb4a2SCy Schubert struct icon_entry *icon, *tmp; 392780fb4a2SCy Schubert int count = 0; 393780fb4a2SCy Schubert 394780fb4a2SCy Schubert if (!bssid) 395780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons"); 396780fb4a2SCy Schubert else if (!file_name) 397780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons for " 398780fb4a2SCy Schubert MACSTR, MAC2STR(bssid)); 399780fb4a2SCy Schubert else 400780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "HS20: Delete stored icons for " 401780fb4a2SCy Schubert MACSTR " file name %s", MAC2STR(bssid), file_name); 402780fb4a2SCy Schubert 403780fb4a2SCy Schubert dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry, 404780fb4a2SCy Schubert list) { 405780fb4a2SCy Schubert if ((!bssid || os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0) && 406780fb4a2SCy Schubert (!file_name || 407780fb4a2SCy Schubert os_strcmp(icon->file_name, file_name) == 0)) { 408780fb4a2SCy Schubert dl_list_del(&icon->list); 409780fb4a2SCy Schubert hs20_free_icon_entry(icon); 410780fb4a2SCy Schubert count++; 411780fb4a2SCy Schubert } 412780fb4a2SCy Schubert } 413780fb4a2SCy Schubert return count == 0 ? -1 : 0; 414780fb4a2SCy Schubert } 415780fb4a2SCy Schubert 416780fb4a2SCy Schubert 4175b9c547cSRui Paulo static void hs20_set_osu_access_permission(const char *osu_dir, 4185b9c547cSRui Paulo const char *fname) 4195b9c547cSRui Paulo { 4205b9c547cSRui Paulo struct stat statbuf; 4215b9c547cSRui Paulo 4225b9c547cSRui Paulo /* Get OSU directory information */ 4235b9c547cSRui Paulo if (stat(osu_dir, &statbuf) < 0) { 4245b9c547cSRui Paulo wpa_printf(MSG_WARNING, "Cannot stat the OSU directory %s", 4255b9c547cSRui Paulo osu_dir); 4265b9c547cSRui Paulo return; 4275b9c547cSRui Paulo } 4285b9c547cSRui Paulo 4295b9c547cSRui Paulo if (chmod(fname, statbuf.st_mode) < 0) { 4305b9c547cSRui Paulo wpa_printf(MSG_WARNING, 4315b9c547cSRui Paulo "Cannot change the permissions for %s", fname); 4325b9c547cSRui Paulo return; 4335b9c547cSRui Paulo } 4345b9c547cSRui Paulo 4354bc52338SCy Schubert if (lchown(fname, statbuf.st_uid, statbuf.st_gid) < 0) { 4365b9c547cSRui Paulo wpa_printf(MSG_WARNING, "Cannot change the ownership for %s", 4375b9c547cSRui Paulo fname); 4385b9c547cSRui Paulo } 4395b9c547cSRui Paulo } 4405b9c547cSRui Paulo 441780fb4a2SCy Schubert 442780fb4a2SCy Schubert static void hs20_remove_duplicate_icons(struct wpa_supplicant *wpa_s, 443780fb4a2SCy Schubert struct icon_entry *new_icon) 444780fb4a2SCy Schubert { 445780fb4a2SCy Schubert struct icon_entry *icon, *tmp; 446780fb4a2SCy Schubert 447780fb4a2SCy Schubert dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry, 448780fb4a2SCy Schubert list) { 449780fb4a2SCy Schubert if (icon == new_icon) 450780fb4a2SCy Schubert continue; 451780fb4a2SCy Schubert if (os_memcmp(icon->bssid, new_icon->bssid, ETH_ALEN) == 0 && 452780fb4a2SCy Schubert os_strcmp(icon->file_name, new_icon->file_name) == 0) { 453780fb4a2SCy Schubert dl_list_del(&icon->list); 454780fb4a2SCy Schubert hs20_free_icon_entry(icon); 455780fb4a2SCy Schubert } 456780fb4a2SCy Schubert } 457780fb4a2SCy Schubert } 458780fb4a2SCy Schubert 459780fb4a2SCy Schubert 4605b9c547cSRui Paulo static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s, 4615b9c547cSRui Paulo const u8 *sa, const u8 *pos, 462780fb4a2SCy Schubert size_t slen, u8 dialog_token) 4635b9c547cSRui Paulo { 4645b9c547cSRui Paulo char fname[256]; 4655b9c547cSRui Paulo int png; 4665b9c547cSRui Paulo FILE *f; 4675b9c547cSRui Paulo u16 data_len; 468780fb4a2SCy Schubert struct icon_entry *icon; 4695b9c547cSRui Paulo 470780fb4a2SCy Schubert dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) { 471780fb4a2SCy Schubert if (icon->dialog_token == dialog_token && !icon->image && 472780fb4a2SCy Schubert os_memcmp(icon->bssid, sa, ETH_ALEN) == 0) { 47385732ac8SCy Schubert icon->image = os_memdup(pos, slen); 474780fb4a2SCy Schubert if (!icon->image) 475780fb4a2SCy Schubert return -1; 476780fb4a2SCy Schubert icon->image_len = slen; 477780fb4a2SCy Schubert hs20_remove_duplicate_icons(wpa_s, icon); 478780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, 479780fb4a2SCy Schubert RX_HS20_ICON MACSTR " %s %u", 480780fb4a2SCy Schubert MAC2STR(sa), icon->file_name, 481780fb4a2SCy Schubert (unsigned int) icon->image_len); 482780fb4a2SCy Schubert return 0; 483780fb4a2SCy Schubert } 484780fb4a2SCy Schubert } 485780fb4a2SCy Schubert 486780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Icon Binary File", 4875b9c547cSRui Paulo MAC2STR(sa)); 4885b9c547cSRui Paulo 4895b9c547cSRui Paulo if (slen < 4) { 4905b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " 4915b9c547cSRui Paulo "value from " MACSTR, MAC2STR(sa)); 4925b9c547cSRui Paulo return -1; 4935b9c547cSRui Paulo } 4945b9c547cSRui Paulo 4955b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos); 4965b9c547cSRui Paulo if (*pos != 0) 4975b9c547cSRui Paulo return -1; 4985b9c547cSRui Paulo pos++; 4995b9c547cSRui Paulo slen--; 5005b9c547cSRui Paulo 5015b9c547cSRui Paulo if ((size_t) 1 + pos[0] > slen) { 5025b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " 5035b9c547cSRui Paulo "value from " MACSTR, MAC2STR(sa)); 5045b9c547cSRui Paulo return -1; 5055b9c547cSRui Paulo } 5065b9c547cSRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]); 5075b9c547cSRui Paulo png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0; 5085b9c547cSRui Paulo slen -= 1 + pos[0]; 5095b9c547cSRui Paulo pos += 1 + pos[0]; 5105b9c547cSRui Paulo 5115b9c547cSRui Paulo if (slen < 2) { 5125b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " 5135b9c547cSRui Paulo "value from " MACSTR, MAC2STR(sa)); 5145b9c547cSRui Paulo return -1; 5155b9c547cSRui Paulo } 5165b9c547cSRui Paulo data_len = WPA_GET_LE16(pos); 5175b9c547cSRui Paulo pos += 2; 5185b9c547cSRui Paulo slen -= 2; 5195b9c547cSRui Paulo 5205b9c547cSRui Paulo if (data_len > slen) { 5215b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " 5225b9c547cSRui Paulo "value from " MACSTR, MAC2STR(sa)); 5235b9c547cSRui Paulo return -1; 5245b9c547cSRui Paulo } 5255b9c547cSRui Paulo 5265b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len); 5275b9c547cSRui Paulo if (wpa_s->conf->osu_dir == NULL) 5285b9c547cSRui Paulo return -1; 5295b9c547cSRui Paulo 5305b9c547cSRui Paulo wpa_s->osu_icon_id++; 5315b9c547cSRui Paulo if (wpa_s->osu_icon_id == 0) 5325b9c547cSRui Paulo wpa_s->osu_icon_id++; 5335b9c547cSRui Paulo snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s", 5345b9c547cSRui Paulo wpa_s->conf->osu_dir, wpa_s->osu_icon_id, 5355b9c547cSRui Paulo png ? "png" : "icon"); 5365b9c547cSRui Paulo f = fopen(fname, "wb"); 5375b9c547cSRui Paulo if (f == NULL) 5385b9c547cSRui Paulo return -1; 5395b9c547cSRui Paulo 5405b9c547cSRui Paulo hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname); 5415b9c547cSRui Paulo 5425b9c547cSRui Paulo if (fwrite(pos, slen, 1, f) != 1) { 5435b9c547cSRui Paulo fclose(f); 5445b9c547cSRui Paulo unlink(fname); 5455b9c547cSRui Paulo return -1; 5465b9c547cSRui Paulo } 5475b9c547cSRui Paulo fclose(f); 5485b9c547cSRui Paulo 549780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP_ICON "%s", fname); 5505b9c547cSRui Paulo return 0; 5515b9c547cSRui Paulo } 5525b9c547cSRui Paulo 5535b9c547cSRui Paulo 5545b9c547cSRui Paulo static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx) 5555b9c547cSRui Paulo { 5565b9c547cSRui Paulo struct wpa_supplicant *wpa_s = eloop_ctx; 5575b9c547cSRui Paulo if (wpa_s->fetch_osu_icon_in_progress) 5585b9c547cSRui Paulo hs20_next_osu_icon(wpa_s); 5595b9c547cSRui Paulo } 5605b9c547cSRui Paulo 5615b9c547cSRui Paulo 5625b9c547cSRui Paulo static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res) 5635b9c547cSRui Paulo { 5645b9c547cSRui Paulo size_t i, j; 5655b9c547cSRui Paulo struct os_reltime now, tmp; 5665b9c547cSRui Paulo int dur; 5675b9c547cSRui Paulo 5685b9c547cSRui Paulo os_get_reltime(&now); 5695b9c547cSRui Paulo os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp); 5705b9c547cSRui Paulo dur = tmp.sec * 1000 + tmp.usec / 1000; 5715b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d", 5725b9c547cSRui Paulo dur, res); 5735b9c547cSRui Paulo 5745b9c547cSRui Paulo for (i = 0; i < wpa_s->osu_prov_count; i++) { 5755b9c547cSRui Paulo struct osu_provider *osu = &wpa_s->osu_prov[i]; 5765b9c547cSRui Paulo for (j = 0; j < osu->icon_count; j++) { 5775b9c547cSRui Paulo struct osu_icon *icon = &osu->icon[j]; 5785b9c547cSRui Paulo if (icon->id || icon->failed) 5795b9c547cSRui Paulo continue; 5805b9c547cSRui Paulo if (res < 0) 5815b9c547cSRui Paulo icon->failed = 1; 5825b9c547cSRui Paulo else 5835b9c547cSRui Paulo icon->id = wpa_s->osu_icon_id; 5845b9c547cSRui Paulo return; 5855b9c547cSRui Paulo } 5865b9c547cSRui Paulo } 5875b9c547cSRui Paulo } 5885b9c547cSRui Paulo 5895b9c547cSRui Paulo 590f05cddf9SRui Paulo void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, 5915b9c547cSRui Paulo struct wpa_bss *bss, const u8 *sa, 592780fb4a2SCy Schubert const u8 *data, size_t slen, u8 dialog_token) 593f05cddf9SRui Paulo { 594f05cddf9SRui Paulo const u8 *pos = data; 595f05cddf9SRui Paulo u8 subtype; 596f05cddf9SRui Paulo struct wpa_bss_anqp *anqp = NULL; 5975b9c547cSRui Paulo int ret; 598f05cddf9SRui Paulo 599f05cddf9SRui Paulo if (slen < 2) 600f05cddf9SRui Paulo return; 601f05cddf9SRui Paulo 602f05cddf9SRui Paulo if (bss) 603f05cddf9SRui Paulo anqp = bss->anqp; 604f05cddf9SRui Paulo 605f05cddf9SRui Paulo subtype = *pos++; 606f05cddf9SRui Paulo slen--; 607f05cddf9SRui Paulo 608f05cddf9SRui Paulo pos++; /* Reserved */ 609f05cddf9SRui Paulo slen--; 610f05cddf9SRui Paulo 611f05cddf9SRui Paulo switch (subtype) { 612f05cddf9SRui Paulo case HS20_STYPE_CAPABILITY_LIST: 613780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR 614f05cddf9SRui Paulo " HS Capability List", MAC2STR(sa)); 615f05cddf9SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen); 6165b9c547cSRui Paulo if (anqp) { 6175b9c547cSRui Paulo wpabuf_free(anqp->hs20_capability_list); 6185b9c547cSRui Paulo anqp->hs20_capability_list = 6195b9c547cSRui Paulo wpabuf_alloc_copy(pos, slen); 6205b9c547cSRui Paulo } 621f05cddf9SRui Paulo break; 622f05cddf9SRui Paulo case HS20_STYPE_OPERATOR_FRIENDLY_NAME: 623780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR 624f05cddf9SRui Paulo " Operator Friendly Name", MAC2STR(sa)); 625f05cddf9SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen); 626f05cddf9SRui Paulo if (anqp) { 627f05cddf9SRui Paulo wpabuf_free(anqp->hs20_operator_friendly_name); 628f05cddf9SRui Paulo anqp->hs20_operator_friendly_name = 629f05cddf9SRui Paulo wpabuf_alloc_copy(pos, slen); 630f05cddf9SRui Paulo } 631f05cddf9SRui Paulo break; 632f05cddf9SRui Paulo case HS20_STYPE_WAN_METRICS: 633f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "WAN Metrics", pos, slen); 634f05cddf9SRui Paulo if (slen < 13) { 635f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short WAN " 636f05cddf9SRui Paulo "Metrics value from " MACSTR, MAC2STR(sa)); 637f05cddf9SRui Paulo break; 638f05cddf9SRui Paulo } 639780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR 640f05cddf9SRui Paulo " WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa), 641f05cddf9SRui Paulo pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5), 642f05cddf9SRui Paulo pos[9], pos[10], WPA_GET_LE16(pos + 11)); 643f05cddf9SRui Paulo if (anqp) { 644f05cddf9SRui Paulo wpabuf_free(anqp->hs20_wan_metrics); 645f05cddf9SRui Paulo anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen); 646f05cddf9SRui Paulo } 647f05cddf9SRui Paulo break; 648f05cddf9SRui Paulo case HS20_STYPE_CONNECTION_CAPABILITY: 649780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR 650f05cddf9SRui Paulo " Connection Capability", MAC2STR(sa)); 651f05cddf9SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen); 652f05cddf9SRui Paulo if (anqp) { 653f05cddf9SRui Paulo wpabuf_free(anqp->hs20_connection_capability); 654f05cddf9SRui Paulo anqp->hs20_connection_capability = 655f05cddf9SRui Paulo wpabuf_alloc_copy(pos, slen); 656f05cddf9SRui Paulo } 657f05cddf9SRui Paulo break; 658f05cddf9SRui Paulo case HS20_STYPE_OPERATING_CLASS: 659780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR 660f05cddf9SRui Paulo " Operating Class", MAC2STR(sa)); 661f05cddf9SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen); 662f05cddf9SRui Paulo if (anqp) { 663f05cddf9SRui Paulo wpabuf_free(anqp->hs20_operating_class); 664f05cddf9SRui Paulo anqp->hs20_operating_class = 665f05cddf9SRui Paulo wpabuf_alloc_copy(pos, slen); 666f05cddf9SRui Paulo } 667f05cddf9SRui Paulo break; 6685b9c547cSRui Paulo case HS20_STYPE_OSU_PROVIDERS_LIST: 669780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR 6705b9c547cSRui Paulo " OSU Providers list", MAC2STR(sa)); 6715b9c547cSRui Paulo wpa_s->num_prov_found++; 6725b9c547cSRui Paulo if (anqp) { 6735b9c547cSRui Paulo wpabuf_free(anqp->hs20_osu_providers_list); 6745b9c547cSRui Paulo anqp->hs20_osu_providers_list = 6755b9c547cSRui Paulo wpabuf_alloc_copy(pos, slen); 6765b9c547cSRui Paulo } 6775b9c547cSRui Paulo break; 6785b9c547cSRui Paulo case HS20_STYPE_ICON_BINARY_FILE: 679780fb4a2SCy Schubert ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen, 680780fb4a2SCy Schubert dialog_token); 6815b9c547cSRui Paulo if (wpa_s->fetch_osu_icon_in_progress) { 6825b9c547cSRui Paulo hs20_osu_icon_fetch_result(wpa_s, ret); 6835b9c547cSRui Paulo eloop_cancel_timeout(hs20_continue_icon_fetch, 6845b9c547cSRui Paulo wpa_s, NULL); 6855b9c547cSRui Paulo eloop_register_timeout(0, 0, hs20_continue_icon_fetch, 6865b9c547cSRui Paulo wpa_s, NULL); 6875b9c547cSRui Paulo } 6885b9c547cSRui Paulo break; 68985732ac8SCy Schubert case HS20_STYPE_OPERATOR_ICON_METADATA: 69085732ac8SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR 69185732ac8SCy Schubert " Operator Icon Metadata", MAC2STR(sa)); 69285732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "Operator Icon Metadata", pos, slen); 69385732ac8SCy Schubert if (anqp) { 69485732ac8SCy Schubert wpabuf_free(anqp->hs20_operator_icon_metadata); 69585732ac8SCy Schubert anqp->hs20_operator_icon_metadata = 69685732ac8SCy Schubert wpabuf_alloc_copy(pos, slen); 69785732ac8SCy Schubert } 69885732ac8SCy Schubert break; 69985732ac8SCy Schubert case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: 70085732ac8SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR 70185732ac8SCy Schubert " OSU Providers NAI List", MAC2STR(sa)); 70285732ac8SCy Schubert if (anqp) { 70385732ac8SCy Schubert wpabuf_free(anqp->hs20_osu_providers_nai_list); 70485732ac8SCy Schubert anqp->hs20_osu_providers_nai_list = 70585732ac8SCy Schubert wpabuf_alloc_copy(pos, slen); 70685732ac8SCy Schubert } 70785732ac8SCy Schubert break; 708f05cddf9SRui Paulo default: 709f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype); 710f05cddf9SRui Paulo break; 711f05cddf9SRui Paulo } 712f05cddf9SRui Paulo } 7135b9c547cSRui Paulo 7145b9c547cSRui Paulo 7155b9c547cSRui Paulo void hs20_notify_parse_done(struct wpa_supplicant *wpa_s) 7165b9c547cSRui Paulo { 7175b9c547cSRui Paulo if (!wpa_s->fetch_osu_icon_in_progress) 7185b9c547cSRui Paulo return; 7195b9c547cSRui Paulo if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL)) 7205b9c547cSRui Paulo return; 7215b9c547cSRui Paulo /* 7225b9c547cSRui Paulo * We are going through icon fetch, but no icon response was received. 7235b9c547cSRui Paulo * Assume this means the current AP could not provide an answer to avoid 7245b9c547cSRui Paulo * getting stuck in fetch iteration. 7255b9c547cSRui Paulo */ 7265b9c547cSRui Paulo hs20_icon_fetch_failed(wpa_s); 7275b9c547cSRui Paulo } 7285b9c547cSRui Paulo 7295b9c547cSRui Paulo 7305b9c547cSRui Paulo static void hs20_free_osu_prov_entry(struct osu_provider *prov) 7315b9c547cSRui Paulo { 7325b9c547cSRui Paulo } 7335b9c547cSRui Paulo 7345b9c547cSRui Paulo 7355b9c547cSRui Paulo void hs20_free_osu_prov(struct wpa_supplicant *wpa_s) 7365b9c547cSRui Paulo { 7375b9c547cSRui Paulo size_t i; 7385b9c547cSRui Paulo for (i = 0; i < wpa_s->osu_prov_count; i++) 7395b9c547cSRui Paulo hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]); 7405b9c547cSRui Paulo os_free(wpa_s->osu_prov); 7415b9c547cSRui Paulo wpa_s->osu_prov = NULL; 7425b9c547cSRui Paulo wpa_s->osu_prov_count = 0; 7435b9c547cSRui Paulo } 7445b9c547cSRui Paulo 7455b9c547cSRui Paulo 7465b9c547cSRui Paulo static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s) 7475b9c547cSRui Paulo { 7485b9c547cSRui Paulo char fname[256]; 7495b9c547cSRui Paulo FILE *f; 7505b9c547cSRui Paulo size_t i, j; 7515b9c547cSRui Paulo 7525b9c547cSRui Paulo wpa_s->fetch_osu_info = 0; 7535b9c547cSRui Paulo wpa_s->fetch_osu_icon_in_progress = 0; 7545b9c547cSRui Paulo 7555b9c547cSRui Paulo if (wpa_s->conf->osu_dir == NULL) { 7565b9c547cSRui Paulo hs20_free_osu_prov(wpa_s); 7575b9c547cSRui Paulo wpa_s->fetch_anqp_in_progress = 0; 7585b9c547cSRui Paulo return; 7595b9c547cSRui Paulo } 7605b9c547cSRui Paulo 7615b9c547cSRui Paulo snprintf(fname, sizeof(fname), "%s/osu-providers.txt", 7625b9c547cSRui Paulo wpa_s->conf->osu_dir); 7635b9c547cSRui Paulo f = fopen(fname, "w"); 7645b9c547cSRui Paulo if (f == NULL) { 765780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, 766780fb4a2SCy Schubert "Could not write OSU provider information"); 7675b9c547cSRui Paulo hs20_free_osu_prov(wpa_s); 768780fb4a2SCy Schubert wpa_s->fetch_anqp_in_progress = 0; 7695b9c547cSRui Paulo return; 7705b9c547cSRui Paulo } 7715b9c547cSRui Paulo 7725b9c547cSRui Paulo hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname); 7735b9c547cSRui Paulo 7745b9c547cSRui Paulo for (i = 0; i < wpa_s->osu_prov_count; i++) { 7755b9c547cSRui Paulo struct osu_provider *osu = &wpa_s->osu_prov[i]; 7765b9c547cSRui Paulo if (i > 0) 7775b9c547cSRui Paulo fprintf(f, "\n"); 7785b9c547cSRui Paulo fprintf(f, "OSU-PROVIDER " MACSTR "\n" 7795b9c547cSRui Paulo "uri=%s\n" 7805b9c547cSRui Paulo "methods=%08x\n", 7815b9c547cSRui Paulo MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods); 7825b9c547cSRui Paulo if (osu->osu_ssid_len) { 7835b9c547cSRui Paulo fprintf(f, "osu_ssid=%s\n", 7845b9c547cSRui Paulo wpa_ssid_txt(osu->osu_ssid, 7855b9c547cSRui Paulo osu->osu_ssid_len)); 7865b9c547cSRui Paulo } 78785732ac8SCy Schubert if (osu->osu_ssid2_len) { 78885732ac8SCy Schubert fprintf(f, "osu_ssid2=%s\n", 78985732ac8SCy Schubert wpa_ssid_txt(osu->osu_ssid2, 79085732ac8SCy Schubert osu->osu_ssid2_len)); 79185732ac8SCy Schubert } 7925b9c547cSRui Paulo if (osu->osu_nai[0]) 7935b9c547cSRui Paulo fprintf(f, "osu_nai=%s\n", osu->osu_nai); 79485732ac8SCy Schubert if (osu->osu_nai2[0]) 79585732ac8SCy Schubert fprintf(f, "osu_nai2=%s\n", osu->osu_nai2); 7965b9c547cSRui Paulo for (j = 0; j < osu->friendly_name_count; j++) { 7975b9c547cSRui Paulo fprintf(f, "friendly_name=%s:%s\n", 7985b9c547cSRui Paulo osu->friendly_name[j].lang, 7995b9c547cSRui Paulo osu->friendly_name[j].text); 8005b9c547cSRui Paulo } 8015b9c547cSRui Paulo for (j = 0; j < osu->serv_desc_count; j++) { 8025b9c547cSRui Paulo fprintf(f, "desc=%s:%s\n", 8035b9c547cSRui Paulo osu->serv_desc[j].lang, 8045b9c547cSRui Paulo osu->serv_desc[j].text); 8055b9c547cSRui Paulo } 8065b9c547cSRui Paulo for (j = 0; j < osu->icon_count; j++) { 8075b9c547cSRui Paulo struct osu_icon *icon = &osu->icon[j]; 8085b9c547cSRui Paulo if (icon->failed) 8095b9c547cSRui Paulo continue; /* could not fetch icon */ 8105b9c547cSRui Paulo fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n", 8115b9c547cSRui Paulo icon->id, icon->width, icon->height, icon->lang, 8125b9c547cSRui Paulo icon->icon_type, icon->filename); 8135b9c547cSRui Paulo } 8145b9c547cSRui Paulo } 8155b9c547cSRui Paulo fclose(f); 8165b9c547cSRui Paulo hs20_free_osu_prov(wpa_s); 8175b9c547cSRui Paulo 8185b9c547cSRui Paulo wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed"); 8195b9c547cSRui Paulo wpa_s->fetch_anqp_in_progress = 0; 8205b9c547cSRui Paulo } 8215b9c547cSRui Paulo 8225b9c547cSRui Paulo 8235b9c547cSRui Paulo void hs20_next_osu_icon(struct wpa_supplicant *wpa_s) 8245b9c547cSRui Paulo { 8255b9c547cSRui Paulo size_t i, j; 8265b9c547cSRui Paulo 8275b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon"); 8285b9c547cSRui Paulo 8295b9c547cSRui Paulo for (i = 0; i < wpa_s->osu_prov_count; i++) { 8305b9c547cSRui Paulo struct osu_provider *osu = &wpa_s->osu_prov[i]; 8315b9c547cSRui Paulo for (j = 0; j < osu->icon_count; j++) { 8325b9c547cSRui Paulo struct osu_icon *icon = &osu->icon[j]; 8335b9c547cSRui Paulo if (icon->id || icon->failed) 8345b9c547cSRui Paulo continue; 8355b9c547cSRui Paulo 8365b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' " 8375b9c547cSRui Paulo "from " MACSTR, icon->filename, 8385b9c547cSRui Paulo MAC2STR(osu->bssid)); 8395b9c547cSRui Paulo os_get_reltime(&wpa_s->osu_icon_fetch_start); 8405b9c547cSRui Paulo if (hs20_anqp_send_req(wpa_s, osu->bssid, 8415b9c547cSRui Paulo BIT(HS20_STYPE_ICON_REQUEST), 8425b9c547cSRui Paulo (u8 *) icon->filename, 843780fb4a2SCy Schubert os_strlen(icon->filename), 844780fb4a2SCy Schubert 0) < 0) { 8455b9c547cSRui Paulo icon->failed = 1; 8465b9c547cSRui Paulo continue; 8475b9c547cSRui Paulo } 8485b9c547cSRui Paulo return; 8495b9c547cSRui Paulo } 8505b9c547cSRui Paulo } 8515b9c547cSRui Paulo 8525b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch"); 8535b9c547cSRui Paulo hs20_osu_fetch_done(wpa_s); 8545b9c547cSRui Paulo } 8555b9c547cSRui Paulo 8565b9c547cSRui Paulo 8575b9c547cSRui Paulo static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, 8585b9c547cSRui Paulo const u8 *osu_ssid, u8 osu_ssid_len, 85985732ac8SCy Schubert const u8 *osu_ssid2, u8 osu_ssid2_len, 8605b9c547cSRui Paulo const u8 *pos, size_t len) 8615b9c547cSRui Paulo { 8625b9c547cSRui Paulo struct osu_provider *prov; 8635b9c547cSRui Paulo const u8 *end = pos + len; 8645b9c547cSRui Paulo u16 len2; 8655b9c547cSRui Paulo const u8 *pos2; 8665b9c547cSRui Paulo u8 uri_len, osu_method_len, osu_nai_len; 8675b9c547cSRui Paulo 8685b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len); 8695b9c547cSRui Paulo prov = os_realloc_array(wpa_s->osu_prov, 8705b9c547cSRui Paulo wpa_s->osu_prov_count + 1, 8715b9c547cSRui Paulo sizeof(*prov)); 8725b9c547cSRui Paulo if (prov == NULL) 8735b9c547cSRui Paulo return; 8745b9c547cSRui Paulo wpa_s->osu_prov = prov; 8755b9c547cSRui Paulo prov = &prov[wpa_s->osu_prov_count]; 8765b9c547cSRui Paulo os_memset(prov, 0, sizeof(*prov)); 8775b9c547cSRui Paulo 8785b9c547cSRui Paulo os_memcpy(prov->bssid, bss->bssid, ETH_ALEN); 8795b9c547cSRui Paulo os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len); 8805b9c547cSRui Paulo prov->osu_ssid_len = osu_ssid_len; 88185732ac8SCy Schubert if (osu_ssid2) 88285732ac8SCy Schubert os_memcpy(prov->osu_ssid2, osu_ssid2, osu_ssid2_len); 88385732ac8SCy Schubert prov->osu_ssid2_len = osu_ssid2_len; 8845b9c547cSRui Paulo 8855b9c547cSRui Paulo /* OSU Friendly Name Length */ 886780fb4a2SCy Schubert if (end - pos < 2) { 8875b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " 8885b9c547cSRui Paulo "Friendly Name Length"); 8895b9c547cSRui Paulo return; 8905b9c547cSRui Paulo } 8915b9c547cSRui Paulo len2 = WPA_GET_LE16(pos); 8925b9c547cSRui Paulo pos += 2; 8935b9c547cSRui Paulo if (len2 > end - pos) { 8945b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " 8955b9c547cSRui Paulo "Friendly Name Duples"); 8965b9c547cSRui Paulo return; 8975b9c547cSRui Paulo } 8985b9c547cSRui Paulo pos2 = pos; 8995b9c547cSRui Paulo pos += len2; 9005b9c547cSRui Paulo 9015b9c547cSRui Paulo /* OSU Friendly Name Duples */ 902780fb4a2SCy Schubert while (pos - pos2 >= 4 && prov->friendly_name_count < OSU_MAX_ITEMS) { 9035b9c547cSRui Paulo struct osu_lang_string *f; 904*c1d255d3SCy Schubert u8 slen; 905*c1d255d3SCy Schubert 906*c1d255d3SCy Schubert slen = pos2[0]; 907*c1d255d3SCy Schubert if (1 + slen > pos - pos2) { 9085b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name"); 9095b9c547cSRui Paulo break; 9105b9c547cSRui Paulo } 911*c1d255d3SCy Schubert if (slen < 3) { 912*c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 913*c1d255d3SCy Schubert "Invalid OSU Friendly Name (no room for language)"); 914*c1d255d3SCy Schubert break; 915*c1d255d3SCy Schubert } 9165b9c547cSRui Paulo f = &prov->friendly_name[prov->friendly_name_count++]; 917*c1d255d3SCy Schubert pos2++; 918*c1d255d3SCy Schubert os_memcpy(f->lang, pos2, 3); 919*c1d255d3SCy Schubert pos2 += 3; 920*c1d255d3SCy Schubert slen -= 3; 921*c1d255d3SCy Schubert os_memcpy(f->text, pos2, slen); 922*c1d255d3SCy Schubert pos2 += slen; 9235b9c547cSRui Paulo } 9245b9c547cSRui Paulo 9255b9c547cSRui Paulo /* OSU Server URI */ 926780fb4a2SCy Schubert if (end - pos < 1) { 9275b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 9285b9c547cSRui Paulo "HS 2.0: Not enough room for OSU Server URI length"); 9295b9c547cSRui Paulo return; 9305b9c547cSRui Paulo } 9315b9c547cSRui Paulo uri_len = *pos++; 9325b9c547cSRui Paulo if (uri_len > end - pos) { 9335b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server " 9345b9c547cSRui Paulo "URI"); 9355b9c547cSRui Paulo return; 9365b9c547cSRui Paulo } 9375b9c547cSRui Paulo os_memcpy(prov->server_uri, pos, uri_len); 9385b9c547cSRui Paulo pos += uri_len; 9395b9c547cSRui Paulo 9405b9c547cSRui Paulo /* OSU Method list */ 941780fb4a2SCy Schubert if (end - pos < 1) { 9425b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " 9435b9c547cSRui Paulo "list length"); 9445b9c547cSRui Paulo return; 9455b9c547cSRui Paulo } 9465b9c547cSRui Paulo osu_method_len = pos[0]; 9475b9c547cSRui Paulo if (osu_method_len > end - pos - 1) { 9485b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " 9495b9c547cSRui Paulo "list"); 9505b9c547cSRui Paulo return; 9515b9c547cSRui Paulo } 9525b9c547cSRui Paulo pos2 = pos + 1; 9535b9c547cSRui Paulo pos += 1 + osu_method_len; 9545b9c547cSRui Paulo while (pos2 < pos) { 9555b9c547cSRui Paulo if (*pos2 < 32) 9565b9c547cSRui Paulo prov->osu_methods |= BIT(*pos2); 9575b9c547cSRui Paulo pos2++; 9585b9c547cSRui Paulo } 9595b9c547cSRui Paulo 9605b9c547cSRui Paulo /* Icons Available Length */ 961780fb4a2SCy Schubert if (end - pos < 2) { 9625b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons " 9635b9c547cSRui Paulo "Available Length"); 9645b9c547cSRui Paulo return; 9655b9c547cSRui Paulo } 9665b9c547cSRui Paulo len2 = WPA_GET_LE16(pos); 9675b9c547cSRui Paulo pos += 2; 9685b9c547cSRui Paulo if (len2 > end - pos) { 9695b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons " 9705b9c547cSRui Paulo "Available"); 9715b9c547cSRui Paulo return; 9725b9c547cSRui Paulo } 9735b9c547cSRui Paulo pos2 = pos; 9745b9c547cSRui Paulo pos += len2; 9755b9c547cSRui Paulo 9765b9c547cSRui Paulo /* Icons Available */ 9775b9c547cSRui Paulo while (pos2 < pos) { 9785b9c547cSRui Paulo struct osu_icon *icon = &prov->icon[prov->icon_count]; 9795b9c547cSRui Paulo u8 flen; 9805b9c547cSRui Paulo 981780fb4a2SCy Schubert if (2 + 2 + 3 + 1 + 1 > pos - pos2) { 9825b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata"); 9835b9c547cSRui Paulo break; 9845b9c547cSRui Paulo } 9855b9c547cSRui Paulo 9865b9c547cSRui Paulo icon->width = WPA_GET_LE16(pos2); 9875b9c547cSRui Paulo pos2 += 2; 9885b9c547cSRui Paulo icon->height = WPA_GET_LE16(pos2); 9895b9c547cSRui Paulo pos2 += 2; 9905b9c547cSRui Paulo os_memcpy(icon->lang, pos2, 3); 9915b9c547cSRui Paulo pos2 += 3; 9925b9c547cSRui Paulo 993780fb4a2SCy Schubert flen = *pos2++; 994780fb4a2SCy Schubert if (flen > pos - pos2) { 9955b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type"); 9965b9c547cSRui Paulo break; 9975b9c547cSRui Paulo } 998780fb4a2SCy Schubert os_memcpy(icon->icon_type, pos2, flen); 999780fb4a2SCy Schubert pos2 += flen; 10005b9c547cSRui Paulo 1001780fb4a2SCy Schubert if (pos - pos2 < 1) { 10025b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " 10035b9c547cSRui Paulo "Filename length"); 10045b9c547cSRui Paulo break; 10055b9c547cSRui Paulo } 1006780fb4a2SCy Schubert flen = *pos2++; 1007780fb4a2SCy Schubert if (flen > pos - pos2) { 10085b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " 10095b9c547cSRui Paulo "Filename"); 10105b9c547cSRui Paulo break; 10115b9c547cSRui Paulo } 1012780fb4a2SCy Schubert os_memcpy(icon->filename, pos2, flen); 1013780fb4a2SCy Schubert pos2 += flen; 10145b9c547cSRui Paulo 10155b9c547cSRui Paulo prov->icon_count++; 10165b9c547cSRui Paulo } 10175b9c547cSRui Paulo 10185b9c547cSRui Paulo /* OSU_NAI */ 1019780fb4a2SCy Schubert if (end - pos < 1) { 10205b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); 10215b9c547cSRui Paulo return; 10225b9c547cSRui Paulo } 1023780fb4a2SCy Schubert osu_nai_len = *pos++; 1024780fb4a2SCy Schubert if (osu_nai_len > end - pos) { 10255b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); 10265b9c547cSRui Paulo return; 10275b9c547cSRui Paulo } 1028780fb4a2SCy Schubert os_memcpy(prov->osu_nai, pos, osu_nai_len); 1029780fb4a2SCy Schubert pos += osu_nai_len; 10305b9c547cSRui Paulo 10315b9c547cSRui Paulo /* OSU Service Description Length */ 1032780fb4a2SCy Schubert if (end - pos < 2) { 10335b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " 10345b9c547cSRui Paulo "Service Description Length"); 10355b9c547cSRui Paulo return; 10365b9c547cSRui Paulo } 10375b9c547cSRui Paulo len2 = WPA_GET_LE16(pos); 10385b9c547cSRui Paulo pos += 2; 10395b9c547cSRui Paulo if (len2 > end - pos) { 10405b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " 10415b9c547cSRui Paulo "Service Description Duples"); 10425b9c547cSRui Paulo return; 10435b9c547cSRui Paulo } 10445b9c547cSRui Paulo pos2 = pos; 10455b9c547cSRui Paulo pos += len2; 10465b9c547cSRui Paulo 10475b9c547cSRui Paulo /* OSU Service Description Duples */ 1048780fb4a2SCy Schubert while (pos - pos2 >= 4 && prov->serv_desc_count < OSU_MAX_ITEMS) { 10495b9c547cSRui Paulo struct osu_lang_string *f; 10505b9c547cSRui Paulo u8 descr_len; 10515b9c547cSRui Paulo 1052780fb4a2SCy Schubert descr_len = *pos2++; 1053780fb4a2SCy Schubert if (descr_len > pos - pos2 || descr_len < 3) { 10545b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Invalid OSU Service " 10555b9c547cSRui Paulo "Description"); 10565b9c547cSRui Paulo break; 10575b9c547cSRui Paulo } 10585b9c547cSRui Paulo f = &prov->serv_desc[prov->serv_desc_count++]; 1059780fb4a2SCy Schubert os_memcpy(f->lang, pos2, 3); 1060780fb4a2SCy Schubert os_memcpy(f->text, pos2 + 3, descr_len - 3); 1061780fb4a2SCy Schubert pos2 += descr_len; 10625b9c547cSRui Paulo } 10635b9c547cSRui Paulo 10645b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR, 10655b9c547cSRui Paulo MAC2STR(bss->bssid)); 10665b9c547cSRui Paulo wpa_s->osu_prov_count++; 10675b9c547cSRui Paulo } 10685b9c547cSRui Paulo 10695b9c547cSRui Paulo 10705b9c547cSRui Paulo void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) 10715b9c547cSRui Paulo { 10725b9c547cSRui Paulo struct wpa_bss *bss; 10735b9c547cSRui Paulo struct wpabuf *prov_anqp; 10745b9c547cSRui Paulo const u8 *pos, *end; 10755b9c547cSRui Paulo u16 len; 107685732ac8SCy Schubert const u8 *osu_ssid, *osu_ssid2; 107785732ac8SCy Schubert u8 osu_ssid_len, osu_ssid2_len; 10785b9c547cSRui Paulo u8 num_providers; 10795b9c547cSRui Paulo 10805b9c547cSRui Paulo hs20_free_osu_prov(wpa_s); 10815b9c547cSRui Paulo 10825b9c547cSRui Paulo dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 108385732ac8SCy Schubert struct wpa_ie_data data; 108485732ac8SCy Schubert const u8 *ie; 108585732ac8SCy Schubert 10865b9c547cSRui Paulo if (bss->anqp == NULL) 10875b9c547cSRui Paulo continue; 10885b9c547cSRui Paulo prov_anqp = bss->anqp->hs20_osu_providers_list; 10895b9c547cSRui Paulo if (prov_anqp == NULL) 10905b9c547cSRui Paulo continue; 109185732ac8SCy Schubert ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); 109285732ac8SCy Schubert if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &data) == 0 && 109385732ac8SCy Schubert (data.key_mgmt & WPA_KEY_MGMT_OSEN)) { 109485732ac8SCy Schubert osu_ssid2 = bss->ssid; 109585732ac8SCy Schubert osu_ssid2_len = bss->ssid_len; 109685732ac8SCy Schubert } else { 109785732ac8SCy Schubert osu_ssid2 = NULL; 109885732ac8SCy Schubert osu_ssid2_len = 0; 109985732ac8SCy Schubert } 11005b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from " 11015b9c547cSRui Paulo MACSTR, MAC2STR(bss->bssid)); 11025b9c547cSRui Paulo wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list", 11035b9c547cSRui Paulo prov_anqp); 11045b9c547cSRui Paulo pos = wpabuf_head(prov_anqp); 11055b9c547cSRui Paulo end = pos + wpabuf_len(prov_anqp); 11065b9c547cSRui Paulo 11075b9c547cSRui Paulo /* OSU SSID */ 1108780fb4a2SCy Schubert if (end - pos < 1) 11095b9c547cSRui Paulo continue; 1110780fb4a2SCy Schubert if (1 + pos[0] > end - pos) { 11115b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " 11125b9c547cSRui Paulo "OSU SSID"); 11135b9c547cSRui Paulo continue; 11145b9c547cSRui Paulo } 11155b9c547cSRui Paulo osu_ssid_len = *pos++; 1116325151a3SRui Paulo if (osu_ssid_len > SSID_MAX_LEN) { 11175b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID " 11185b9c547cSRui Paulo "Length %u", osu_ssid_len); 11195b9c547cSRui Paulo continue; 11205b9c547cSRui Paulo } 11215b9c547cSRui Paulo osu_ssid = pos; 11225b9c547cSRui Paulo pos += osu_ssid_len; 11235b9c547cSRui Paulo 1124780fb4a2SCy Schubert if (end - pos < 1) { 11255b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " 11265b9c547cSRui Paulo "Number of OSU Providers"); 11275b9c547cSRui Paulo continue; 11285b9c547cSRui Paulo } 11295b9c547cSRui Paulo num_providers = *pos++; 11305b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u", 11315b9c547cSRui Paulo num_providers); 11325b9c547cSRui Paulo 11335b9c547cSRui Paulo /* OSU Providers */ 1134780fb4a2SCy Schubert while (end - pos > 2 && num_providers > 0) { 11355b9c547cSRui Paulo num_providers--; 11365b9c547cSRui Paulo len = WPA_GET_LE16(pos); 11375b9c547cSRui Paulo pos += 2; 11385b9c547cSRui Paulo if (len > (unsigned int) (end - pos)) 11395b9c547cSRui Paulo break; 11405b9c547cSRui Paulo hs20_osu_add_prov(wpa_s, bss, osu_ssid, 114185732ac8SCy Schubert osu_ssid_len, osu_ssid2, 114285732ac8SCy Schubert osu_ssid2_len, pos, len); 11435b9c547cSRui Paulo pos += len; 11445b9c547cSRui Paulo } 11455b9c547cSRui Paulo 11465b9c547cSRui Paulo if (pos != end) { 11475b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of " 11485b9c547cSRui Paulo "extra data after OSU Providers", 11495b9c547cSRui Paulo (int) (end - pos)); 11505b9c547cSRui Paulo } 115185732ac8SCy Schubert 115285732ac8SCy Schubert prov_anqp = bss->anqp->hs20_osu_providers_nai_list; 115385732ac8SCy Schubert if (!prov_anqp) 115485732ac8SCy Schubert continue; 115585732ac8SCy Schubert wpa_printf(MSG_DEBUG, 115685732ac8SCy Schubert "HS 2.0: Parsing OSU Providers NAI List from " 115785732ac8SCy Schubert MACSTR, MAC2STR(bss->bssid)); 115885732ac8SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers NAI List", 115985732ac8SCy Schubert prov_anqp); 116085732ac8SCy Schubert pos = wpabuf_head(prov_anqp); 116185732ac8SCy Schubert end = pos + wpabuf_len(prov_anqp); 116285732ac8SCy Schubert num_providers = 0; 116385732ac8SCy Schubert while (end - pos > 0) { 116485732ac8SCy Schubert len = *pos++; 116585732ac8SCy Schubert if (end - pos < len) { 116685732ac8SCy Schubert wpa_printf(MSG_DEBUG, 116785732ac8SCy Schubert "HS 2.0: Not enough room for OSU_NAI"); 116885732ac8SCy Schubert break; 116985732ac8SCy Schubert } 117085732ac8SCy Schubert if (num_providers >= wpa_s->osu_prov_count) { 117185732ac8SCy Schubert wpa_printf(MSG_DEBUG, 117285732ac8SCy Schubert "HS 2.0: Ignore unexpected OSU Provider NAI List entries"); 117385732ac8SCy Schubert break; 117485732ac8SCy Schubert } 117585732ac8SCy Schubert os_memcpy(wpa_s->osu_prov[num_providers].osu_nai2, 117685732ac8SCy Schubert pos, len); 117785732ac8SCy Schubert pos += len; 117885732ac8SCy Schubert num_providers++; 117985732ac8SCy Schubert } 11805b9c547cSRui Paulo } 11815b9c547cSRui Paulo 11825b9c547cSRui Paulo wpa_s->fetch_osu_icon_in_progress = 1; 11835b9c547cSRui Paulo hs20_next_osu_icon(wpa_s); 11845b9c547cSRui Paulo } 11855b9c547cSRui Paulo 11865b9c547cSRui Paulo 11875b9c547cSRui Paulo static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s, 11885b9c547cSRui Paulo struct wpa_scan_results *scan_res) 11895b9c547cSRui Paulo { 11905b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed"); 11915b9c547cSRui Paulo if (!wpa_s->fetch_osu_waiting_scan) { 11925b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "OSU fetch have been canceled"); 11935b9c547cSRui Paulo return; 11945b9c547cSRui Paulo } 11955b9c547cSRui Paulo wpa_s->network_select = 0; 11965b9c547cSRui Paulo wpa_s->fetch_all_anqp = 1; 11975b9c547cSRui Paulo wpa_s->fetch_osu_info = 1; 11985b9c547cSRui Paulo wpa_s->fetch_osu_icon_in_progress = 0; 11995b9c547cSRui Paulo 12005b9c547cSRui Paulo interworking_start_fetch_anqp(wpa_s); 12015b9c547cSRui Paulo } 12025b9c547cSRui Paulo 12035b9c547cSRui Paulo 1204780fb4a2SCy Schubert int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan) 12055b9c547cSRui Paulo { 12065b9c547cSRui Paulo if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { 12075b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " 12085b9c547cSRui Paulo "interface disabled"); 12095b9c547cSRui Paulo return -1; 12105b9c547cSRui Paulo } 12115b9c547cSRui Paulo 12125b9c547cSRui Paulo if (wpa_s->scanning) { 12135b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " 12145b9c547cSRui Paulo "scanning"); 12155b9c547cSRui Paulo return -1; 12165b9c547cSRui Paulo } 12175b9c547cSRui Paulo 12185b9c547cSRui Paulo if (wpa_s->conf->osu_dir == NULL) { 12195b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " 12205b9c547cSRui Paulo "osu_dir not configured"); 12215b9c547cSRui Paulo return -1; 12225b9c547cSRui Paulo } 12235b9c547cSRui Paulo 12245b9c547cSRui Paulo if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) { 12255b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " 12265b9c547cSRui Paulo "fetch in progress (%d, %d)", 12275b9c547cSRui Paulo wpa_s->fetch_anqp_in_progress, 12285b9c547cSRui Paulo wpa_s->network_select); 12295b9c547cSRui Paulo return -1; 12305b9c547cSRui Paulo } 12315b9c547cSRui Paulo 12325b9c547cSRui Paulo wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch"); 12335b9c547cSRui Paulo wpa_s->num_osu_scans = 0; 12345b9c547cSRui Paulo wpa_s->num_prov_found = 0; 1235780fb4a2SCy Schubert if (skip_scan) { 1236780fb4a2SCy Schubert wpa_s->network_select = 0; 1237780fb4a2SCy Schubert wpa_s->fetch_all_anqp = 1; 1238780fb4a2SCy Schubert wpa_s->fetch_osu_info = 1; 1239780fb4a2SCy Schubert wpa_s->fetch_osu_icon_in_progress = 0; 1240780fb4a2SCy Schubert 1241780fb4a2SCy Schubert interworking_start_fetch_anqp(wpa_s); 1242780fb4a2SCy Schubert } else { 12435b9c547cSRui Paulo hs20_start_osu_scan(wpa_s); 1244780fb4a2SCy Schubert } 12455b9c547cSRui Paulo 12465b9c547cSRui Paulo return 0; 12475b9c547cSRui Paulo } 12485b9c547cSRui Paulo 12495b9c547cSRui Paulo 12505b9c547cSRui Paulo void hs20_start_osu_scan(struct wpa_supplicant *wpa_s) 12515b9c547cSRui Paulo { 12525b9c547cSRui Paulo wpa_s->fetch_osu_waiting_scan = 1; 12535b9c547cSRui Paulo wpa_s->num_osu_scans++; 12545b9c547cSRui Paulo wpa_s->scan_req = MANUAL_SCAN_REQ; 12555b9c547cSRui Paulo wpa_s->scan_res_handler = hs20_osu_scan_res_handler; 12565b9c547cSRui Paulo wpa_supplicant_req_scan(wpa_s, 0, 0); 12575b9c547cSRui Paulo } 12585b9c547cSRui Paulo 12595b9c547cSRui Paulo 12605b9c547cSRui Paulo void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s) 12615b9c547cSRui Paulo { 12625b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Cancel OSU fetch"); 12635b9c547cSRui Paulo interworking_stop_fetch_anqp(wpa_s); 12645b9c547cSRui Paulo wpa_s->fetch_osu_waiting_scan = 0; 12655b9c547cSRui Paulo wpa_s->network_select = 0; 12665b9c547cSRui Paulo wpa_s->fetch_osu_info = 0; 12675b9c547cSRui Paulo wpa_s->fetch_osu_icon_in_progress = 0; 12685b9c547cSRui Paulo } 12695b9c547cSRui Paulo 12705b9c547cSRui Paulo 12715b9c547cSRui Paulo void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s) 12725b9c547cSRui Paulo { 12735b9c547cSRui Paulo hs20_osu_icon_fetch_result(wpa_s, -1); 12745b9c547cSRui Paulo eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL); 12755b9c547cSRui Paulo eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL); 12765b9c547cSRui Paulo } 12775b9c547cSRui Paulo 12785b9c547cSRui Paulo 12795b9c547cSRui Paulo void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s, 12805b9c547cSRui Paulo const char *url, u8 osu_method) 12815b9c547cSRui Paulo { 12825b9c547cSRui Paulo if (url) 12835b9c547cSRui Paulo wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s", 12845b9c547cSRui Paulo osu_method, url); 12855b9c547cSRui Paulo else 12865b9c547cSRui Paulo wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION); 12875b9c547cSRui Paulo } 12885b9c547cSRui Paulo 12895b9c547cSRui Paulo 12905b9c547cSRui Paulo void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, 12915b9c547cSRui Paulo u16 reauth_delay, const char *url) 12925b9c547cSRui Paulo { 12935b9c547cSRui Paulo if (!wpa_sm_pmf_enabled(wpa_s->wpa)) { 12945b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Ignore deauthentication imminent notice since PMF was not enabled"); 12955b9c547cSRui Paulo return; 12965b9c547cSRui Paulo } 12975b9c547cSRui Paulo 12985b9c547cSRui Paulo wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s", 12995b9c547cSRui Paulo code, reauth_delay, url); 13005b9c547cSRui Paulo 13015b9c547cSRui Paulo if (code == HS20_DEAUTH_REASON_CODE_BSS) { 1302*c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to ignore list"); 1303*c1d255d3SCy Schubert wpa_bssid_ignore_add(wpa_s, wpa_s->bssid); 13045b9c547cSRui Paulo /* TODO: For now, disable full ESS since some drivers may not 13055b9c547cSRui Paulo * support disabling per BSS. */ 13065b9c547cSRui Paulo if (wpa_s->current_ssid) { 13075b9c547cSRui Paulo struct os_reltime now; 13085b9c547cSRui Paulo os_get_reltime(&now); 13095b9c547cSRui Paulo if (now.sec + reauth_delay <= 13105b9c547cSRui Paulo wpa_s->current_ssid->disabled_until.sec) 13115b9c547cSRui Paulo return; 13125b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds (BSS)", 13135b9c547cSRui Paulo reauth_delay); 13145b9c547cSRui Paulo wpa_s->current_ssid->disabled_until.sec = 13155b9c547cSRui Paulo now.sec + reauth_delay; 13165b9c547cSRui Paulo } 13175b9c547cSRui Paulo } 13185b9c547cSRui Paulo 13195b9c547cSRui Paulo if (code == HS20_DEAUTH_REASON_CODE_ESS && wpa_s->current_ssid) { 13205b9c547cSRui Paulo struct os_reltime now; 13215b9c547cSRui Paulo os_get_reltime(&now); 13225b9c547cSRui Paulo if (now.sec + reauth_delay <= 13235b9c547cSRui Paulo wpa_s->current_ssid->disabled_until.sec) 13245b9c547cSRui Paulo return; 13255b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds", 13265b9c547cSRui Paulo reauth_delay); 13275b9c547cSRui Paulo wpa_s->current_ssid->disabled_until.sec = 13285b9c547cSRui Paulo now.sec + reauth_delay; 13295b9c547cSRui Paulo } 13305b9c547cSRui Paulo } 13315b9c547cSRui Paulo 13325b9c547cSRui Paulo 133385732ac8SCy Schubert void hs20_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url) 133485732ac8SCy Schubert { 133585732ac8SCy Schubert if (!wpa_sm_pmf_enabled(wpa_s->wpa)) { 133685732ac8SCy Schubert wpa_printf(MSG_DEBUG, 133785732ac8SCy Schubert "HS 2.0: Ignore Terms and Conditions Acceptance since PMF was not enabled"); 133885732ac8SCy Schubert return; 133985732ac8SCy Schubert } 134085732ac8SCy Schubert 134185732ac8SCy Schubert wpa_msg(wpa_s, MSG_INFO, HS20_T_C_ACCEPTANCE "%s", url); 134285732ac8SCy Schubert } 134385732ac8SCy Schubert 134485732ac8SCy Schubert 1345780fb4a2SCy Schubert void hs20_init(struct wpa_supplicant *wpa_s) 1346780fb4a2SCy Schubert { 1347780fb4a2SCy Schubert dl_list_init(&wpa_s->icon_head); 1348780fb4a2SCy Schubert } 1349780fb4a2SCy Schubert 1350780fb4a2SCy Schubert 13515b9c547cSRui Paulo void hs20_deinit(struct wpa_supplicant *wpa_s) 13525b9c547cSRui Paulo { 13535b9c547cSRui Paulo eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL); 13545b9c547cSRui Paulo hs20_free_osu_prov(wpa_s); 1355780fb4a2SCy Schubert if (wpa_s->icon_head.next) 1356780fb4a2SCy Schubert hs20_del_icon(wpa_s, NULL, NULL); 13575b9c547cSRui Paulo } 1358