blob: f14e6cb9356f17370447c1e00ffec50d6f46ef6b [file] [log] [blame]
Dmitry Shmidt04949592012-07-19 12:16:46 -07001/*
2 * Copyright (c) 2009, Atheros Communications, Inc.
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08003 * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
Dmitry Shmidt04949592012-07-19 12:16:46 -07004 *
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"
Dmitry Shmidt2f74e362015-01-21 13:19:05 -080010#include <sys/stat.h>
Dmitry Shmidt04949592012-07-19 12:16:46 -070011
12#include "common.h"
13#include "eloop.h"
14#include "common/ieee802_11_common.h"
15#include "common/ieee802_11_defs.h"
16#include "common/gas.h"
17#include "common/wpa_ctrl.h"
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080018#include "rsn_supp/wpa.h"
Dmitry Shmidt04949592012-07-19 12:16:46 -070019#include "wpa_supplicant_i.h"
20#include "driver_i.h"
21#include "config.h"
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080022#include "scan.h"
Roshan Pius04a9d742016-12-12 12:40:46 -080023#include "notify.h"
Dmitry Shmidt04949592012-07-19 12:16:46 -070024#include "bss.h"
Hai Shalom60840252021-02-19 19:02:11 -080025#include "bssid_ignore.h"
Dmitry Shmidt04949592012-07-19 12:16:46 -070026#include "gas_query.h"
27#include "interworking.h"
28#include "hs20_supplicant.h"
Dmitry Shmidt7d56b752015-12-22 10:59:44 -080029#include "base64.h"
Sunil Ravib0ac25f2024-07-12 01:42:03 +000030#include "notify.h"
Dmitry Shmidt04949592012-07-19 12:16:46 -070031
32
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080033#define OSU_MAX_ITEMS 10
34
35struct osu_lang_string {
36 char lang[4];
37 char text[253];
38};
39
40struct osu_icon {
41 u16 width;
42 u16 height;
43 char lang[4];
44 char icon_type[256];
45 char filename[256];
46 unsigned int id;
47 unsigned int failed:1;
48};
49
50struct osu_provider {
51 u8 bssid[ETH_ALEN];
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -070052 u8 osu_ssid[SSID_MAX_LEN];
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080053 u8 osu_ssid_len;
Hai Shalom39ba6fc2019-01-22 12:40:38 -080054 u8 osu_ssid2[SSID_MAX_LEN];
55 u8 osu_ssid2_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080056 char server_uri[256];
57 u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
58 char osu_nai[256];
Hai Shalom39ba6fc2019-01-22 12:40:38 -080059 char osu_nai2[256];
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080060 struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
61 size_t friendly_name_count;
62 struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
63 size_t serv_desc_count;
64 struct osu_icon icon[OSU_MAX_ITEMS];
65 size_t icon_count;
66};
67
68
Dmitry Shmidt849734c2016-05-27 09:59:01 -070069void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s)
70{
71 struct wpa_bss *bss = wpa_s->current_bss;
Dmitry Shmidt849734c2016-05-27 09:59:01 -070072 const u8 *ie;
73 const u8 *ext_capa;
74 u32 filter = 0;
75
Hai Shalom2cbbcd12021-03-08 18:33:38 -080076 if (!bss || !is_hs20_network(wpa_s, wpa_s->current_ssid, bss)
77#ifndef ANDROID
78 // HS 2.0 Configuration is not used in AOSP
79 || !is_hs20_config(wpa_s)
80#endif
81 ) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +000082 /* Not configuring frame filtering - BSS is not a Hotspot 2.0
83 * network */
Dmitry Shmidt849734c2016-05-27 09:59:01 -070084 return;
85 }
86
87 ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE);
88
89 /* Check if DGAF disabled bit is zero (5th byte in the IE) */
90 if (!ie || ie[1] < 5)
91 wpa_printf(MSG_DEBUG,
92 "Not configuring frame filtering - Can't extract DGAF bit");
93 else if (!(ie[6] & HS20_DGAF_DISABLED))
94 filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK;
95
96 ext_capa = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
97 if (!ext_capa || ext_capa[1] < 2) {
98 wpa_printf(MSG_DEBUG,
99 "Not configuring frame filtering - Can't extract Proxy ARP bit");
100 return;
101 }
102
Hai Shalom74f70d42019-02-11 14:42:39 -0800103 if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_PROXY_ARP))
Dmitry Shmidt849734c2016-05-27 09:59:01 -0700104 filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP |
105 WPA_DATA_FRAME_FILTER_FLAG_NA;
106
107 wpa_drv_configure_frame_filters(wpa_s, filter);
108}
109
110
Hai Shalom74f70d42019-02-11 14:42:39 -0800111void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id, int ap_release)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700112{
Hai Shalom74f70d42019-02-11 14:42:39 -0800113 int release;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800114 u8 conf;
115
Hai Shalom74f70d42019-02-11 14:42:39 -0800116 release = (HS20_VERSION >> 4) + 1;
117 if (ap_release > 0 && release > ap_release)
118 release = ap_release;
119 if (release < 2)
120 pps_mo_id = -1;
121
Dmitry Shmidt04949592012-07-19 12:16:46 -0700122 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800123 wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700124 wpabuf_put_be24(buf, OUI_WFA);
125 wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
Hai Shalom74f70d42019-02-11 14:42:39 -0800126 conf = (release - 1) << 4;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800127 if (pps_mo_id >= 0)
128 conf |= HS20_PPS_MO_ID_PRESENT;
129 wpabuf_put_u8(buf, conf);
130 if (pps_mo_id >= 0)
131 wpabuf_put_le16(buf, pps_mo_id);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700132}
133
134
Roshan Pius3a1667e2018-07-03 15:17:14 -0700135void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf,
136 const struct wpa_ssid *ssid)
137{
138 if (!ssid->roaming_consortium_selection ||
139 !ssid->roaming_consortium_selection_len)
140 return;
141
142 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
143 wpabuf_put_u8(buf, 4 + ssid->roaming_consortium_selection_len);
144 wpabuf_put_be24(buf, OUI_WFA);
145 wpabuf_put_u8(buf, HS20_ROAMING_CONS_SEL_OUI_TYPE);
146 wpabuf_put_data(buf, ssid->roaming_consortium_selection,
147 ssid->roaming_consortium_selection_len);
148}
149
150
Hai Shalom74f70d42019-02-11 14:42:39 -0800151int get_hs20_version(struct wpa_bss *bss)
152{
153 const u8 *ie;
154
155 if (!bss)
156 return 0;
157
158 ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE);
159 if (!ie || ie[1] < 5)
160 return 0;
161
162 return ((ie[6] >> 4) & 0x0f) + 1;
163}
164
Hai Shalom2cbbcd12021-03-08 18:33:38 -0800165int is_hs20_config(struct wpa_supplicant *wpa_s)
166{
167 return wpa_s->conf->hs20;
168}
Hai Shalom74f70d42019-02-11 14:42:39 -0800169
Dmitry Shmidt51b6ea82013-05-08 10:42:09 -0700170int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
171 struct wpa_bss *bss)
172{
Hai Shalom2cbbcd12021-03-08 18:33:38 -0800173 if (!ssid)
Dmitry Shmidt51b6ea82013-05-08 10:42:09 -0700174 return 0;
175
176 if (ssid->parent_cred)
177 return 1;
178
179 if (bss && !wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE))
180 return 0;
181
182 /*
183 * This may catch some non-Hotspot 2.0 cases, but it is safer to do that
184 * than cause Hotspot 2.0 connections without indication element getting
185 * added. Non-Hotspot 2.0 APs should ignore the unknown vendor element.
186 */
187
188 if (!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X))
189 return 0;
190 if (!(ssid->pairwise_cipher & WPA_CIPHER_CCMP))
191 return 0;
192 if (ssid->proto != WPA_PROTO_RSN)
193 return 0;
194
195 return 1;
196}
197
198
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800199int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
200{
201 struct wpa_cred *cred;
202
Dmitry Shmidtc2817022014-07-02 10:32:10 -0700203 if (ssid == NULL)
204 return 0;
205
206 if (ssid->update_identifier)
207 return ssid->update_identifier;
208
209 if (ssid->parent_cred == NULL)
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800210 return 0;
211
212 for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
213 if (ssid->parent_cred == cred)
214 return cred->update_identifier;
215 }
216
217 return 0;
218}
219
220
Dmitry Shmidt15907092014-03-25 10:42:57 -0700221void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
222 struct wpabuf *buf)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700223{
Dmitry Shmidt04949592012-07-19 12:16:46 -0700224 u8 *len_pos;
225
Dmitry Shmidt04949592012-07-19 12:16:46 -0700226 if (buf == NULL)
Dmitry Shmidt15907092014-03-25 10:42:57 -0700227 return;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700228
229 len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
230 wpabuf_put_be24(buf, OUI_WFA);
231 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
232 if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) {
233 wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
234 wpabuf_put_u8(buf, 0); /* Reserved */
235 if (payload)
236 wpabuf_put_data(buf, payload, payload_len);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800237 } else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) {
238 wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
239 wpabuf_put_u8(buf, 0); /* Reserved */
240 if (payload)
241 wpabuf_put_data(buf, payload, payload_len);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700242 } else {
243 u8 i;
244 wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST);
245 wpabuf_put_u8(buf, 0); /* Reserved */
246 for (i = 0; i < 32; i++) {
247 if (stypes & BIT(i))
248 wpabuf_put_u8(buf, i);
249 }
250 }
251 gas_anqp_set_element_len(buf, len_pos);
252
253 gas_anqp_set_len(buf);
Dmitry Shmidt15907092014-03-25 10:42:57 -0700254}
255
256
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700257static struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
258 size_t payload_len)
Dmitry Shmidt15907092014-03-25 10:42:57 -0700259{
260 struct wpabuf *buf;
261
262 buf = gas_anqp_build_initial_req(0, 100 + payload_len);
263 if (buf == NULL)
264 return NULL;
265
266 hs20_put_anqp_req(stypes, payload, payload_len, buf);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700267
268 return buf;
269}
270
271
272int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800273 const u8 *payload, size_t payload_len, int inmem)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700274{
275 struct wpabuf *buf;
276 int ret = 0;
277 int freq;
278 struct wpa_bss *bss;
279 int res;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800280 struct icon_entry *icon_entry;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700281
Dmitry Shmidt04949592012-07-19 12:16:46 -0700282 bss = wpa_bss_get_bssid(wpa_s, dst);
Dmitry Shmidtcc00d5d2015-05-04 10:34:12 -0700283 if (!bss) {
284 wpa_printf(MSG_WARNING,
285 "ANQP: Cannot send query to unknown BSS "
286 MACSTR, MAC2STR(dst));
Dmitry Shmidt04949592012-07-19 12:16:46 -0700287 return -1;
Dmitry Shmidtcc00d5d2015-05-04 10:34:12 -0700288 }
289
290 wpa_bss_anqp_unshare_alloc(bss);
291 freq = bss->freq;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700292
293 wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for "
294 "subtypes 0x%x", MAC2STR(dst), stypes);
295
296 buf = hs20_build_anqp_req(stypes, payload, payload_len);
297 if (buf == NULL)
298 return -1;
299
Hai Shalomb755a2a2020-04-23 21:49:02 -0700300 res = gas_query_req(wpa_s->gas, dst, freq, 0, 0, buf, anqp_resp_cb,
301 wpa_s);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700302 if (res < 0) {
303 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
Dmitry Shmidt051af732013-10-22 13:52:46 -0700304 wpabuf_free(buf);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800305 return -1;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700306 } else
307 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
308 "%u", res);
309
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800310 if (inmem) {
311 icon_entry = os_zalloc(sizeof(struct icon_entry));
312 if (!icon_entry)
313 return -1;
314 os_memcpy(icon_entry->bssid, dst, ETH_ALEN);
315 icon_entry->file_name = os_malloc(payload_len + 1);
316 if (!icon_entry->file_name) {
317 os_free(icon_entry);
318 return -1;
319 }
320 os_memcpy(icon_entry->file_name, payload, payload_len);
321 icon_entry->file_name[payload_len] = '\0';
322 icon_entry->dialog_token = res;
323
324 dl_list_add(&wpa_s->icon_head, &icon_entry->list);
325 }
326
Dmitry Shmidt04949592012-07-19 12:16:46 -0700327 return ret;
328}
329
330
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800331static struct icon_entry * hs20_find_icon(struct wpa_supplicant *wpa_s,
332 const u8 *bssid,
333 const char *file_name)
334{
335 struct icon_entry *icon;
336
337 dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000338 if (ether_addr_equal(icon->bssid, bssid) &&
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800339 os_strcmp(icon->file_name, file_name) == 0 && icon->image)
340 return icon;
341 }
342
343 return NULL;
344}
345
346
347int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
348 const char *file_name, size_t offset, size_t size,
349 char *reply, size_t buf_len)
350{
351 struct icon_entry *icon;
352 size_t out_size;
Ahmed ElArabawy0ff61c52019-12-26 12:38:39 -0800353 char *b64;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800354 size_t b64_size;
355 int reply_size;
356
357 wpa_printf(MSG_DEBUG, "HS20: Get icon " MACSTR " %s @ %u +%u (%u)",
358 MAC2STR(bssid), file_name, (unsigned int) offset,
359 (unsigned int) size, (unsigned int) buf_len);
360
361 icon = hs20_find_icon(wpa_s, bssid, file_name);
362 if (!icon || !icon->image || offset >= icon->image_len)
363 return -1;
364 if (size > icon->image_len - offset)
365 size = icon->image_len - offset;
366 out_size = buf_len - 3 /* max base64 padding */;
367 if (size * 4 > out_size * 3)
368 size = out_size * 3 / 4;
369 if (size == 0)
370 return -1;
371
372 b64 = base64_encode(&icon->image[offset], size, &b64_size);
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700373 if (b64 && buf_len >= b64_size) {
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800374 os_memcpy(reply, b64, b64_size);
375 reply_size = b64_size;
376 } else {
377 reply_size = -1;
378 }
379 os_free(b64);
380 return reply_size;
381}
382
383
384static void hs20_free_icon_entry(struct icon_entry *icon)
385{
386 wpa_printf(MSG_DEBUG, "HS20: Free stored icon from " MACSTR
387 " dialog_token=%u file_name=%s image_len=%u",
388 MAC2STR(icon->bssid), icon->dialog_token,
389 icon->file_name ? icon->file_name : "N/A",
390 (unsigned int) icon->image_len);
391 os_free(icon->file_name);
392 os_free(icon->image);
393 os_free(icon);
394}
395
396
397int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
398 const char *file_name)
399{
400 struct icon_entry *icon, *tmp;
401 int count = 0;
402
403 if (!bssid)
404 wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons");
405 else if (!file_name)
406 wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons for "
407 MACSTR, MAC2STR(bssid));
408 else
409 wpa_printf(MSG_DEBUG, "HS20: Delete stored icons for "
410 MACSTR " file name %s", MAC2STR(bssid), file_name);
411
412 dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry,
413 list) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000414 if ((!bssid || ether_addr_equal(icon->bssid, bssid)) &&
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800415 (!file_name ||
416 os_strcmp(icon->file_name, file_name) == 0)) {
417 dl_list_del(&icon->list);
418 hs20_free_icon_entry(icon);
419 count++;
420 }
421 }
422 return count == 0 ? -1 : 0;
423}
424
425
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800426static void hs20_set_osu_access_permission(const char *osu_dir,
427 const char *fname)
428{
429 struct stat statbuf;
430
431 /* Get OSU directory information */
432 if (stat(osu_dir, &statbuf) < 0) {
433 wpa_printf(MSG_WARNING, "Cannot stat the OSU directory %s",
434 osu_dir);
435 return;
436 }
437
438 if (chmod(fname, statbuf.st_mode) < 0) {
439 wpa_printf(MSG_WARNING,
440 "Cannot change the permissions for %s", fname);
441 return;
442 }
443
Hai Shalom74f70d42019-02-11 14:42:39 -0800444 if (lchown(fname, statbuf.st_uid, statbuf.st_gid) < 0) {
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800445 wpa_printf(MSG_WARNING, "Cannot change the ownership for %s",
446 fname);
447 }
448}
449
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800450
451static void hs20_remove_duplicate_icons(struct wpa_supplicant *wpa_s,
452 struct icon_entry *new_icon)
453{
454 struct icon_entry *icon, *tmp;
455
456 dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry,
457 list) {
458 if (icon == new_icon)
459 continue;
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000460 if (ether_addr_equal(icon->bssid, new_icon->bssid) &&
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800461 os_strcmp(icon->file_name, new_icon->file_name) == 0) {
462 dl_list_del(&icon->list);
463 hs20_free_icon_entry(icon);
464 }
465 }
466}
467
468
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800469static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
470 const u8 *sa, const u8 *pos,
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800471 size_t slen, u8 dialog_token)
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800472{
473 char fname[256];
474 int png;
475 FILE *f;
476 u16 data_len;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800477 struct icon_entry *icon;
478
479 dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
480 if (icon->dialog_token == dialog_token && !icon->image &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000481 ether_addr_equal(icon->bssid, sa)) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700482 icon->image = os_memdup(pos, slen);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800483 if (!icon->image)
484 return -1;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800485 icon->image_len = slen;
486 hs20_remove_duplicate_icons(wpa_s, icon);
487 wpa_msg(wpa_s, MSG_INFO,
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700488 RX_HS20_ICON MACSTR " %s %u",
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800489 MAC2STR(sa), icon->file_name,
490 (unsigned int) icon->image_len);
Roshan Pius04a9d742016-12-12 12:40:46 -0800491 wpas_notify_hs20_icon_query_done(wpa_s, sa,
492 icon->file_name,
493 icon->image,
494 icon->image_len);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800495 return 0;
496 }
497 }
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800498
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700499 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Icon Binary File",
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800500 MAC2STR(sa));
501
502 if (slen < 4) {
503 wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
504 "value from " MACSTR, MAC2STR(sa));
505 return -1;
506 }
507
508 wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos);
509 if (*pos != 0)
510 return -1;
511 pos++;
512 slen--;
513
514 if ((size_t) 1 + pos[0] > slen) {
515 wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
516 "value from " MACSTR, MAC2STR(sa));
517 return -1;
518 }
519 wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]);
520 png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0;
521 slen -= 1 + pos[0];
522 pos += 1 + pos[0];
523
524 if (slen < 2) {
525 wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
526 "value from " MACSTR, MAC2STR(sa));
527 return -1;
528 }
529 data_len = WPA_GET_LE16(pos);
530 pos += 2;
531 slen -= 2;
532
533 if (data_len > slen) {
534 wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
535 "value from " MACSTR, MAC2STR(sa));
536 return -1;
537 }
538
539 wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len);
540 if (wpa_s->conf->osu_dir == NULL)
541 return -1;
542
543 wpa_s->osu_icon_id++;
544 if (wpa_s->osu_icon_id == 0)
545 wpa_s->osu_icon_id++;
546 snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s",
547 wpa_s->conf->osu_dir, wpa_s->osu_icon_id,
548 png ? "png" : "icon");
549 f = fopen(fname, "wb");
550 if (f == NULL)
551 return -1;
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800552
553 hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
554
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800555 if (fwrite(pos, slen, 1, f) != 1) {
556 fclose(f);
557 unlink(fname);
558 return -1;
559 }
560 fclose(f);
561
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700562 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP_ICON "%s", fname);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800563 return 0;
564}
565
566
567static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx)
568{
569 struct wpa_supplicant *wpa_s = eloop_ctx;
570 if (wpa_s->fetch_osu_icon_in_progress)
571 hs20_next_osu_icon(wpa_s);
572}
573
574
575static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res)
576{
577 size_t i, j;
578 struct os_reltime now, tmp;
579 int dur;
580
581 os_get_reltime(&now);
582 os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp);
583 dur = tmp.sec * 1000 + tmp.usec / 1000;
584 wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d",
585 dur, res);
586
587 for (i = 0; i < wpa_s->osu_prov_count; i++) {
588 struct osu_provider *osu = &wpa_s->osu_prov[i];
589 for (j = 0; j < osu->icon_count; j++) {
590 struct osu_icon *icon = &osu->icon[j];
591 if (icon->id || icon->failed)
592 continue;
593 if (res < 0)
594 icon->failed = 1;
595 else
596 icon->id = wpa_s->osu_icon_id;
597 return;
598 }
599 }
600}
601
602
Dmitry Shmidt04949592012-07-19 12:16:46 -0700603void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800604 struct wpa_bss *bss, const u8 *sa,
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800605 const u8 *data, size_t slen, u8 dialog_token)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700606{
607 const u8 *pos = data;
608 u8 subtype;
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700609 struct wpa_bss_anqp *anqp = NULL;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800610 int ret;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700611
612 if (slen < 2)
613 return;
614
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700615 if (bss)
616 anqp = bss->anqp;
617
Dmitry Shmidt04949592012-07-19 12:16:46 -0700618 subtype = *pos++;
619 slen--;
620
621 pos++; /* Reserved */
622 slen--;
623
624 switch (subtype) {
625 case HS20_STYPE_CAPABILITY_LIST:
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700626 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
Dmitry Shmidt04949592012-07-19 12:16:46 -0700627 " HS Capability List", MAC2STR(sa));
628 wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen);
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800629 if (anqp) {
630 wpabuf_free(anqp->hs20_capability_list);
631 anqp->hs20_capability_list =
632 wpabuf_alloc_copy(pos, slen);
633 }
Dmitry Shmidt04949592012-07-19 12:16:46 -0700634 break;
635 case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700636 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
Dmitry Shmidt04949592012-07-19 12:16:46 -0700637 " Operator Friendly Name", MAC2STR(sa));
638 wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700639 if (anqp) {
640 wpabuf_free(anqp->hs20_operator_friendly_name);
641 anqp->hs20_operator_friendly_name =
Dmitry Shmidt04949592012-07-19 12:16:46 -0700642 wpabuf_alloc_copy(pos, slen);
643 }
644 break;
645 case HS20_STYPE_WAN_METRICS:
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800646 wpa_hexdump(MSG_DEBUG, "WAN Metrics", pos, slen);
647 if (slen < 13) {
648 wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short WAN "
649 "Metrics value from " MACSTR, MAC2STR(sa));
650 break;
651 }
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700652 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800653 " WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa),
654 pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5),
655 pos[9], pos[10], WPA_GET_LE16(pos + 11));
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700656 if (anqp) {
657 wpabuf_free(anqp->hs20_wan_metrics);
658 anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700659 }
660 break;
661 case HS20_STYPE_CONNECTION_CAPABILITY:
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700662 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
Dmitry Shmidt04949592012-07-19 12:16:46 -0700663 " Connection Capability", MAC2STR(sa));
664 wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700665 if (anqp) {
666 wpabuf_free(anqp->hs20_connection_capability);
667 anqp->hs20_connection_capability =
Dmitry Shmidt04949592012-07-19 12:16:46 -0700668 wpabuf_alloc_copy(pos, slen);
669 }
670 break;
671 case HS20_STYPE_OPERATING_CLASS:
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700672 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
Dmitry Shmidt04949592012-07-19 12:16:46 -0700673 " Operating Class", MAC2STR(sa));
674 wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700675 if (anqp) {
676 wpabuf_free(anqp->hs20_operating_class);
677 anqp->hs20_operating_class =
Dmitry Shmidt04949592012-07-19 12:16:46 -0700678 wpabuf_alloc_copy(pos, slen);
679 }
680 break;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800681 case HS20_STYPE_OSU_PROVIDERS_LIST:
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700682 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800683 " OSU Providers list", MAC2STR(sa));
684 wpa_s->num_prov_found++;
685 if (anqp) {
686 wpabuf_free(anqp->hs20_osu_providers_list);
687 anqp->hs20_osu_providers_list =
688 wpabuf_alloc_copy(pos, slen);
689 }
690 break;
691 case HS20_STYPE_ICON_BINARY_FILE:
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800692 ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen,
693 dialog_token);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800694 if (wpa_s->fetch_osu_icon_in_progress) {
695 hs20_osu_icon_fetch_result(wpa_s, ret);
696 eloop_cancel_timeout(hs20_continue_icon_fetch,
697 wpa_s, NULL);
698 eloop_register_timeout(0, 0, hs20_continue_icon_fetch,
699 wpa_s, NULL);
700 }
701 break;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700702 case HS20_STYPE_OPERATOR_ICON_METADATA:
703 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
704 " Operator Icon Metadata", MAC2STR(sa));
705 wpa_hexdump(MSG_DEBUG, "Operator Icon Metadata", pos, slen);
706 if (anqp) {
707 wpabuf_free(anqp->hs20_operator_icon_metadata);
708 anqp->hs20_operator_icon_metadata =
709 wpabuf_alloc_copy(pos, slen);
710 }
711 break;
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800712 case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
713 wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
714 " OSU Providers NAI List", MAC2STR(sa));
715 if (anqp) {
716 wpabuf_free(anqp->hs20_osu_providers_nai_list);
717 anqp->hs20_osu_providers_nai_list =
718 wpabuf_alloc_copy(pos, slen);
719 }
720 break;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700721 default:
722 wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
723 break;
724 }
725}
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800726
727
728void hs20_notify_parse_done(struct wpa_supplicant *wpa_s)
729{
730 if (!wpa_s->fetch_osu_icon_in_progress)
731 return;
732 if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL))
733 return;
734 /*
735 * We are going through icon fetch, but no icon response was received.
736 * Assume this means the current AP could not provide an answer to avoid
737 * getting stuck in fetch iteration.
738 */
739 hs20_icon_fetch_failed(wpa_s);
740}
741
742
743static void hs20_free_osu_prov_entry(struct osu_provider *prov)
744{
745}
746
747
748void hs20_free_osu_prov(struct wpa_supplicant *wpa_s)
749{
750 size_t i;
751 for (i = 0; i < wpa_s->osu_prov_count; i++)
752 hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]);
753 os_free(wpa_s->osu_prov);
754 wpa_s->osu_prov = NULL;
755 wpa_s->osu_prov_count = 0;
756}
757
758
759static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s)
760{
761 char fname[256];
762 FILE *f;
763 size_t i, j;
764
765 wpa_s->fetch_osu_info = 0;
766 wpa_s->fetch_osu_icon_in_progress = 0;
767
768 if (wpa_s->conf->osu_dir == NULL) {
769 hs20_free_osu_prov(wpa_s);
770 wpa_s->fetch_anqp_in_progress = 0;
771 return;
772 }
773
774 snprintf(fname, sizeof(fname), "%s/osu-providers.txt",
775 wpa_s->conf->osu_dir);
776 f = fopen(fname, "w");
777 if (f == NULL) {
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -0700778 wpa_msg(wpa_s, MSG_INFO,
779 "Could not write OSU provider information");
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800780 hs20_free_osu_prov(wpa_s);
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800781 wpa_s->fetch_anqp_in_progress = 0;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800782 return;
783 }
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800784
785 hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
786
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800787 for (i = 0; i < wpa_s->osu_prov_count; i++) {
788 struct osu_provider *osu = &wpa_s->osu_prov[i];
789 if (i > 0)
790 fprintf(f, "\n");
791 fprintf(f, "OSU-PROVIDER " MACSTR "\n"
792 "uri=%s\n"
793 "methods=%08x\n",
794 MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods);
795 if (osu->osu_ssid_len) {
796 fprintf(f, "osu_ssid=%s\n",
797 wpa_ssid_txt(osu->osu_ssid,
798 osu->osu_ssid_len));
799 }
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800800 if (osu->osu_ssid2_len) {
801 fprintf(f, "osu_ssid2=%s\n",
802 wpa_ssid_txt(osu->osu_ssid2,
803 osu->osu_ssid2_len));
804 }
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800805 if (osu->osu_nai[0])
806 fprintf(f, "osu_nai=%s\n", osu->osu_nai);
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800807 if (osu->osu_nai2[0])
808 fprintf(f, "osu_nai2=%s\n", osu->osu_nai2);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800809 for (j = 0; j < osu->friendly_name_count; j++) {
810 fprintf(f, "friendly_name=%s:%s\n",
811 osu->friendly_name[j].lang,
812 osu->friendly_name[j].text);
813 }
814 for (j = 0; j < osu->serv_desc_count; j++) {
815 fprintf(f, "desc=%s:%s\n",
816 osu->serv_desc[j].lang,
817 osu->serv_desc[j].text);
818 }
819 for (j = 0; j < osu->icon_count; j++) {
820 struct osu_icon *icon = &osu->icon[j];
821 if (icon->failed)
822 continue; /* could not fetch icon */
823 fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n",
824 icon->id, icon->width, icon->height, icon->lang,
825 icon->icon_type, icon->filename);
826 }
827 }
828 fclose(f);
829 hs20_free_osu_prov(wpa_s);
830
831 wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed");
832 wpa_s->fetch_anqp_in_progress = 0;
833}
834
835
836void hs20_next_osu_icon(struct wpa_supplicant *wpa_s)
837{
838 size_t i, j;
839
840 wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon");
841
842 for (i = 0; i < wpa_s->osu_prov_count; i++) {
843 struct osu_provider *osu = &wpa_s->osu_prov[i];
844 for (j = 0; j < osu->icon_count; j++) {
845 struct osu_icon *icon = &osu->icon[j];
846 if (icon->id || icon->failed)
847 continue;
848
849 wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' "
850 "from " MACSTR, icon->filename,
851 MAC2STR(osu->bssid));
852 os_get_reltime(&wpa_s->osu_icon_fetch_start);
853 if (hs20_anqp_send_req(wpa_s, osu->bssid,
854 BIT(HS20_STYPE_ICON_REQUEST),
855 (u8 *) icon->filename,
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800856 os_strlen(icon->filename),
857 0) < 0) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800858 icon->failed = 1;
859 continue;
860 }
861 return;
862 }
863 }
864
865 wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch");
866 hs20_osu_fetch_done(wpa_s);
867}
868
869
870static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
871 const u8 *osu_ssid, u8 osu_ssid_len,
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800872 const u8 *osu_ssid2, u8 osu_ssid2_len,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800873 const u8 *pos, size_t len)
874{
875 struct osu_provider *prov;
876 const u8 *end = pos + len;
877 u16 len2;
878 const u8 *pos2;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800879 u8 uri_len, osu_method_len, osu_nai_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800880
881 wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
882 prov = os_realloc_array(wpa_s->osu_prov,
883 wpa_s->osu_prov_count + 1,
884 sizeof(*prov));
885 if (prov == NULL)
886 return;
887 wpa_s->osu_prov = prov;
888 prov = &prov[wpa_s->osu_prov_count];
889 os_memset(prov, 0, sizeof(*prov));
890
891 os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
892 os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
893 prov->osu_ssid_len = osu_ssid_len;
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800894 if (osu_ssid2)
895 os_memcpy(prov->osu_ssid2, osu_ssid2, osu_ssid2_len);
896 prov->osu_ssid2_len = osu_ssid2_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800897
898 /* OSU Friendly Name Length */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800899 if (end - pos < 2) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800900 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
901 "Friendly Name Length");
902 return;
903 }
904 len2 = WPA_GET_LE16(pos);
905 pos += 2;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800906 if (len2 > end - pos) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800907 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
908 "Friendly Name Duples");
909 return;
910 }
911 pos2 = pos;
912 pos += len2;
913
914 /* OSU Friendly Name Duples */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800915 while (pos - pos2 >= 4 && prov->friendly_name_count < OSU_MAX_ITEMS) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800916 struct osu_lang_string *f;
Hai Shalom60840252021-02-19 19:02:11 -0800917 u8 slen;
918
919 slen = pos2[0];
920 if (1 + slen > pos - pos2) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800921 wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
922 break;
923 }
Hai Shalom60840252021-02-19 19:02:11 -0800924 if (slen < 3) {
925 wpa_printf(MSG_DEBUG,
926 "Invalid OSU Friendly Name (no room for language)");
927 break;
928 }
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800929 f = &prov->friendly_name[prov->friendly_name_count++];
Hai Shalom60840252021-02-19 19:02:11 -0800930 pos2++;
931 os_memcpy(f->lang, pos2, 3);
932 pos2 += 3;
933 slen -= 3;
934 os_memcpy(f->text, pos2, slen);
935 pos2 += slen;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800936 }
937
938 /* OSU Server URI */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800939 if (end - pos < 1) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800940 wpa_printf(MSG_DEBUG,
941 "HS 2.0: Not enough room for OSU Server URI length");
942 return;
943 }
944 uri_len = *pos++;
945 if (uri_len > end - pos) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800946 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
947 "URI");
948 return;
949 }
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800950 os_memcpy(prov->server_uri, pos, uri_len);
951 pos += uri_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800952
953 /* OSU Method list */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800954 if (end - pos < 1) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800955 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
956 "list length");
957 return;
958 }
959 osu_method_len = pos[0];
960 if (osu_method_len > end - pos - 1) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800961 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
962 "list");
963 return;
964 }
965 pos2 = pos + 1;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800966 pos += 1 + osu_method_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800967 while (pos2 < pos) {
968 if (*pos2 < 32)
969 prov->osu_methods |= BIT(*pos2);
970 pos2++;
971 }
972
973 /* Icons Available Length */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800974 if (end - pos < 2) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800975 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
976 "Available Length");
977 return;
978 }
979 len2 = WPA_GET_LE16(pos);
980 pos += 2;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800981 if (len2 > end - pos) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800982 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
983 "Available");
984 return;
985 }
986 pos2 = pos;
987 pos += len2;
988
989 /* Icons Available */
990 while (pos2 < pos) {
991 struct osu_icon *icon = &prov->icon[prov->icon_count];
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800992 u8 flen;
993
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800994 if (2 + 2 + 3 + 1 + 1 > pos - pos2) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800995 wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
996 break;
997 }
998
999 icon->width = WPA_GET_LE16(pos2);
1000 pos2 += 2;
1001 icon->height = WPA_GET_LE16(pos2);
1002 pos2 += 2;
1003 os_memcpy(icon->lang, pos2, 3);
1004 pos2 += 3;
1005
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001006 flen = *pos2++;
1007 if (flen > pos - pos2) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001008 wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
1009 break;
1010 }
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001011 os_memcpy(icon->icon_type, pos2, flen);
1012 pos2 += flen;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001013
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001014 if (pos - pos2 < 1) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001015 wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
1016 "Filename length");
1017 break;
1018 }
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001019 flen = *pos2++;
1020 if (flen > pos - pos2) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001021 wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
1022 "Filename");
1023 break;
1024 }
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001025 os_memcpy(icon->filename, pos2, flen);
1026 pos2 += flen;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001027
1028 prov->icon_count++;
1029 }
1030
1031 /* OSU_NAI */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001032 if (end - pos < 1) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001033 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
1034 return;
1035 }
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001036 osu_nai_len = *pos++;
1037 if (osu_nai_len > end - pos) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001038 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
1039 return;
1040 }
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001041 os_memcpy(prov->osu_nai, pos, osu_nai_len);
1042 pos += osu_nai_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001043
1044 /* OSU Service Description Length */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001045 if (end - pos < 2) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001046 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
1047 "Service Description Length");
1048 return;
1049 }
1050 len2 = WPA_GET_LE16(pos);
1051 pos += 2;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001052 if (len2 > end - pos) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001053 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
1054 "Service Description Duples");
1055 return;
1056 }
1057 pos2 = pos;
1058 pos += len2;
1059
1060 /* OSU Service Description Duples */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001061 while (pos - pos2 >= 4 && prov->serv_desc_count < OSU_MAX_ITEMS) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001062 struct osu_lang_string *f;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001063 u8 descr_len;
1064
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001065 descr_len = *pos2++;
1066 if (descr_len > pos - pos2 || descr_len < 3) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001067 wpa_printf(MSG_DEBUG, "Invalid OSU Service "
1068 "Description");
1069 break;
1070 }
1071 f = &prov->serv_desc[prov->serv_desc_count++];
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001072 os_memcpy(f->lang, pos2, 3);
1073 os_memcpy(f->text, pos2 + 3, descr_len - 3);
1074 pos2 += descr_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001075 }
1076
1077 wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
1078 MAC2STR(bss->bssid));
1079 wpa_s->osu_prov_count++;
1080}
1081
1082
1083void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
1084{
1085 struct wpa_bss *bss;
1086 struct wpabuf *prov_anqp;
1087 const u8 *pos, *end;
1088 u16 len;
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001089 const u8 *osu_ssid, *osu_ssid2;
1090 u8 osu_ssid_len, osu_ssid2_len;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001091 u8 num_providers;
1092
1093 hs20_free_osu_prov(wpa_s);
1094
1095 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001096 struct wpa_ie_data data;
1097 const u8 *ie;
1098
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001099 if (bss->anqp == NULL)
1100 continue;
1101 prov_anqp = bss->anqp->hs20_osu_providers_list;
1102 if (prov_anqp == NULL)
1103 continue;
Sunil Ravi7f769292024-07-23 22:21:32 +00001104 ie = wpa_bss_get_rsne(wpa_s, bss, NULL, false);
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001105 if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &data) == 0 &&
1106 (data.key_mgmt & WPA_KEY_MGMT_OSEN)) {
1107 osu_ssid2 = bss->ssid;
1108 osu_ssid2_len = bss->ssid_len;
1109 } else {
1110 osu_ssid2 = NULL;
1111 osu_ssid2_len = 0;
1112 }
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001113 wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
1114 MACSTR, MAC2STR(bss->bssid));
1115 wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
1116 prov_anqp);
1117 pos = wpabuf_head(prov_anqp);
1118 end = pos + wpabuf_len(prov_anqp);
1119
1120 /* OSU SSID */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001121 if (end - pos < 1)
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001122 continue;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001123 if (1 + pos[0] > end - pos) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001124 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
1125 "OSU SSID");
1126 continue;
1127 }
1128 osu_ssid_len = *pos++;
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -07001129 if (osu_ssid_len > SSID_MAX_LEN) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001130 wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
1131 "Length %u", osu_ssid_len);
1132 continue;
1133 }
1134 osu_ssid = pos;
1135 pos += osu_ssid_len;
1136
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001137 if (end - pos < 1) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001138 wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
1139 "Number of OSU Providers");
1140 continue;
1141 }
1142 num_providers = *pos++;
1143 wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u",
1144 num_providers);
1145
1146 /* OSU Providers */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001147 while (end - pos > 2 && num_providers > 0) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001148 num_providers--;
1149 len = WPA_GET_LE16(pos);
1150 pos += 2;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001151 if (len > (unsigned int) (end - pos))
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001152 break;
1153 hs20_osu_add_prov(wpa_s, bss, osu_ssid,
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001154 osu_ssid_len, osu_ssid2,
1155 osu_ssid2_len, pos, len);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001156 pos += len;
1157 }
1158
1159 if (pos != end) {
1160 wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of "
1161 "extra data after OSU Providers",
1162 (int) (end - pos));
1163 }
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001164
1165 prov_anqp = bss->anqp->hs20_osu_providers_nai_list;
1166 if (!prov_anqp)
1167 continue;
1168 wpa_printf(MSG_DEBUG,
1169 "HS 2.0: Parsing OSU Providers NAI List from "
1170 MACSTR, MAC2STR(bss->bssid));
1171 wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers NAI List",
1172 prov_anqp);
1173 pos = wpabuf_head(prov_anqp);
1174 end = pos + wpabuf_len(prov_anqp);
1175 num_providers = 0;
1176 while (end - pos > 0) {
1177 len = *pos++;
1178 if (end - pos < len) {
1179 wpa_printf(MSG_DEBUG,
1180 "HS 2.0: Not enough room for OSU_NAI");
1181 break;
1182 }
1183 if (num_providers >= wpa_s->osu_prov_count) {
1184 wpa_printf(MSG_DEBUG,
1185 "HS 2.0: Ignore unexpected OSU Provider NAI List entries");
1186 break;
1187 }
1188 os_memcpy(wpa_s->osu_prov[num_providers].osu_nai2,
1189 pos, len);
1190 pos += len;
1191 num_providers++;
1192 }
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001193 }
1194
1195 wpa_s->fetch_osu_icon_in_progress = 1;
1196 hs20_next_osu_icon(wpa_s);
1197}
1198
1199
1200static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s,
1201 struct wpa_scan_results *scan_res)
1202{
1203 wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001204 if (!wpa_s->fetch_osu_waiting_scan) {
1205 wpa_printf(MSG_DEBUG, "OSU fetch have been canceled");
1206 return;
1207 }
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001208 wpa_s->network_select = 0;
1209 wpa_s->fetch_all_anqp = 1;
1210 wpa_s->fetch_osu_info = 1;
1211 wpa_s->fetch_osu_icon_in_progress = 0;
1212
1213 interworking_start_fetch_anqp(wpa_s);
1214}
1215
1216
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -07001217int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan)
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001218{
1219 if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
1220 wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
1221 "interface disabled");
1222 return -1;
1223 }
1224
1225 if (wpa_s->scanning) {
1226 wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
1227 "scanning");
1228 return -1;
1229 }
1230
1231 if (wpa_s->conf->osu_dir == NULL) {
1232 wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
1233 "osu_dir not configured");
1234 return -1;
1235 }
1236
1237 if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
1238 wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
1239 "fetch in progress (%d, %d)",
1240 wpa_s->fetch_anqp_in_progress,
1241 wpa_s->network_select);
1242 return -1;
1243 }
1244
1245 wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch");
1246 wpa_s->num_osu_scans = 0;
1247 wpa_s->num_prov_found = 0;
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -07001248 if (skip_scan) {
1249 wpa_s->network_select = 0;
1250 wpa_s->fetch_all_anqp = 1;
1251 wpa_s->fetch_osu_info = 1;
1252 wpa_s->fetch_osu_icon_in_progress = 0;
1253
1254 interworking_start_fetch_anqp(wpa_s);
1255 } else {
1256 hs20_start_osu_scan(wpa_s);
1257 }
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001258
1259 return 0;
1260}
1261
1262
1263void hs20_start_osu_scan(struct wpa_supplicant *wpa_s)
1264{
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001265 wpa_s->fetch_osu_waiting_scan = 1;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001266 wpa_s->num_osu_scans++;
1267 wpa_s->scan_req = MANUAL_SCAN_REQ;
1268 wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
1269 wpa_supplicant_req_scan(wpa_s, 0, 0);
1270}
1271
1272
1273void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s)
1274{
1275 wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
1276 interworking_stop_fetch_anqp(wpa_s);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001277 wpa_s->fetch_osu_waiting_scan = 0;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001278 wpa_s->network_select = 0;
1279 wpa_s->fetch_osu_info = 0;
1280 wpa_s->fetch_osu_icon_in_progress = 0;
1281}
1282
1283
1284void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s)
1285{
1286 hs20_osu_icon_fetch_result(wpa_s, -1);
1287 eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
1288 eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL);
1289}
1290
1291
1292void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
1293 const char *url, u8 osu_method)
1294{
1295 if (url)
1296 wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s",
1297 osu_method, url);
1298 else
1299 wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION);
Roshan Pius04a9d742016-12-12 12:40:46 -08001300 wpas_notify_hs20_rx_subscription_remediation(wpa_s, url, osu_method);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001301}
1302
1303
1304void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
1305 u16 reauth_delay, const char *url)
1306{
1307 if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
1308 wpa_printf(MSG_DEBUG, "HS 2.0: Ignore deauthentication imminent notice since PMF was not enabled");
1309 return;
1310 }
1311
1312 wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s",
1313 code, reauth_delay, url);
Roshan Pius04a9d742016-12-12 12:40:46 -08001314 wpas_notify_hs20_rx_deauth_imminent_notice(wpa_s, code, reauth_delay, url);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001315
1316 if (code == HS20_DEAUTH_REASON_CODE_BSS) {
Hai Shalom60840252021-02-19 19:02:11 -08001317 wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to ignore list");
1318 wpa_bssid_ignore_add(wpa_s, wpa_s->bssid);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001319 /* TODO: For now, disable full ESS since some drivers may not
1320 * support disabling per BSS. */
1321 if (wpa_s->current_ssid) {
Dmitry Shmidt4582d2a2014-02-28 11:14:23 -08001322 struct os_reltime now;
1323 os_get_reltime(&now);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001324 if (now.sec + reauth_delay <=
1325 wpa_s->current_ssid->disabled_until.sec)
1326 return;
1327 wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds (BSS)",
1328 reauth_delay);
1329 wpa_s->current_ssid->disabled_until.sec =
1330 now.sec + reauth_delay;
1331 }
1332 }
1333
1334 if (code == HS20_DEAUTH_REASON_CODE_ESS && wpa_s->current_ssid) {
Dmitry Shmidt4582d2a2014-02-28 11:14:23 -08001335 struct os_reltime now;
1336 os_get_reltime(&now);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001337 if (now.sec + reauth_delay <=
1338 wpa_s->current_ssid->disabled_until.sec)
1339 return;
1340 wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds",
1341 reauth_delay);
1342 wpa_s->current_ssid->disabled_until.sec =
1343 now.sec + reauth_delay;
1344 }
1345}
Dmitry Shmidt684785c2014-05-12 13:34:29 -07001346
1347
Roshan Pius3a1667e2018-07-03 15:17:14 -07001348void hs20_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url)
1349{
1350 if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
1351 wpa_printf(MSG_DEBUG,
1352 "HS 2.0: Ignore Terms and Conditions Acceptance since PMF was not enabled");
1353 return;
1354 }
1355
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001356 wpas_notify_hs20_t_c_acceptance(wpa_s, url);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001357}
1358
1359
Dmitry Shmidt7d56b752015-12-22 10:59:44 -08001360void hs20_init(struct wpa_supplicant *wpa_s)
1361{
1362 dl_list_init(&wpa_s->icon_head);
1363}
1364
1365
Dmitry Shmidt684785c2014-05-12 13:34:29 -07001366void hs20_deinit(struct wpa_supplicant *wpa_s)
1367{
1368 eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
1369 hs20_free_osu_prov(wpa_s);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -08001370 if (wpa_s->icon_head.next)
1371 hs20_del_icon(wpa_s, NULL, NULL);
Dmitry Shmidt684785c2014-05-12 13:34:29 -07001372}