Branch data Line data Source code
1 : : /*
2 : : * Generic advertisement service (GAS) server
3 : : * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
4 : : *
5 : : * This software may be distributed under the terms of the BSD license.
6 : : * See README for more details.
7 : : */
8 : :
9 : : #include "includes.h"
10 : :
11 : : #include "common.h"
12 : : #include "common/ieee802_11_defs.h"
13 : : #include "common/gas.h"
14 : : #include "utils/eloop.h"
15 : : #include "hostapd.h"
16 : : #include "ap_config.h"
17 : : #include "ap_drv_ops.h"
18 : : #include "sta_info.h"
19 : : #include "gas_serv.h"
20 : :
21 : :
22 : 1 : static void convert_to_protected_dual(struct wpabuf *msg)
23 : : {
24 : 1 : u8 *categ = wpabuf_mhead_u8(msg);
25 : 1 : *categ = WLAN_ACTION_PROTECTED_DUAL;
26 : 1 : }
27 : :
28 : :
29 : : static struct gas_dialog_info *
30 : 6 : gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
31 : : {
32 : : struct sta_info *sta;
33 : 6 : struct gas_dialog_info *dia = NULL;
34 : : int i, j;
35 : :
36 : 6 : sta = ap_get_sta(hapd, addr);
37 [ + + ]: 6 : if (!sta) {
38 : : /*
39 : : * We need a STA entry to be able to maintain state for
40 : : * the GAS query.
41 : : */
42 : 5 : wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
43 : : "GAS query");
44 : 5 : sta = ap_sta_add(hapd, addr);
45 [ - + ]: 5 : if (!sta) {
46 : 0 : wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
47 : 0 : " for GAS query", MAC2STR(addr));
48 : 0 : return NULL;
49 : : }
50 : 5 : sta->flags |= WLAN_STA_GAS;
51 : : /*
52 : : * The default inactivity is 300 seconds. We don't need
53 : : * it to be that long.
54 : : */
55 : 5 : ap_sta_session_timeout(hapd, sta, 5);
56 : : } else {
57 : 1 : ap_sta_replenish_timeout(hapd, sta, 5);
58 : : }
59 : :
60 [ + - ]: 6 : if (sta->gas_dialog == NULL) {
61 : 6 : sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
62 : : sizeof(struct gas_dialog_info));
63 [ - + ]: 6 : if (sta->gas_dialog == NULL)
64 : 0 : return NULL;
65 : : }
66 : :
67 [ + - ]: 6 : for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
68 [ - + ]: 6 : if (i == GAS_DIALOG_MAX)
69 : 0 : i = 0;
70 [ - + ]: 6 : if (sta->gas_dialog[i].valid)
71 : 0 : continue;
72 : 6 : dia = &sta->gas_dialog[i];
73 : 6 : dia->valid = 1;
74 : 6 : dia->index = i;
75 : 6 : dia->dialog_token = dialog_token;
76 [ + - ]: 6 : sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
77 : 6 : return dia;
78 : : }
79 : :
80 : 0 : wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
81 : : MACSTR " dialog_token %u. Consider increasing "
82 : 0 : "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
83 : :
84 : 6 : return NULL;
85 : : }
86 : :
87 : :
88 : : struct gas_dialog_info *
89 : 30 : gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
90 : : u8 dialog_token)
91 : : {
92 : : struct sta_info *sta;
93 : : int i;
94 : :
95 : 30 : sta = ap_get_sta(hapd, addr);
96 [ - + ]: 30 : if (!sta) {
97 : 0 : wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
98 : 0 : MAC2STR(addr));
99 : 0 : return NULL;
100 : : }
101 [ + - ][ + - ]: 36 : for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
102 [ + + ][ - + ]: 36 : if (sta->gas_dialog[i].dialog_token != dialog_token ||
103 : 30 : !sta->gas_dialog[i].valid)
104 : 6 : continue;
105 : 30 : return &sta->gas_dialog[i];
106 : : }
107 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
108 : 0 : MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
109 : 30 : return NULL;
110 : : }
111 : :
112 : :
113 : 6 : void gas_serv_dialog_clear(struct gas_dialog_info *dia)
114 : : {
115 : 6 : wpabuf_free(dia->sd_resp);
116 : 6 : os_memset(dia, 0, sizeof(*dia));
117 : 6 : }
118 : :
119 : :
120 : 6 : static void gas_serv_free_dialogs(struct hostapd_data *hapd,
121 : : const u8 *sta_addr)
122 : : {
123 : : struct sta_info *sta;
124 : : int i;
125 : :
126 : 6 : sta = ap_get_sta(hapd, sta_addr);
127 [ - + ][ + - ]: 6 : if (sta == NULL || sta->gas_dialog == NULL)
128 : 0 : return;
129 : :
130 [ + + ]: 54 : for (i = 0; i < GAS_DIALOG_MAX; i++) {
131 [ - + ]: 48 : if (sta->gas_dialog[i].valid)
132 : 0 : return;
133 : : }
134 : :
135 : 6 : os_free(sta->gas_dialog);
136 : 6 : sta->gas_dialog = NULL;
137 : : }
138 : :
139 : :
140 : : #ifdef CONFIG_HS20
141 : 298 : static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
142 : : struct wpabuf *buf)
143 : : {
144 : : u8 *len;
145 : :
146 : 298 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
147 : 298 : wpabuf_put_be24(buf, OUI_WFA);
148 : 298 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
149 : 298 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
150 : 298 : wpabuf_put_u8(buf, 0); /* Reserved */
151 : 298 : wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
152 [ + + ]: 298 : if (hapd->conf->hs20_oper_friendly_name)
153 : 8 : wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
154 [ + + ]: 298 : if (hapd->conf->hs20_wan_metrics)
155 : 296 : wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
156 [ + - ]: 298 : if (hapd->conf->hs20_connection_capability)
157 : 298 : wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
158 [ + + ]: 298 : if (hapd->conf->nai_realm_data)
159 : 294 : wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
160 [ + - ]: 298 : if (hapd->conf->hs20_operating_class)
161 : 298 : wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
162 [ + + ]: 298 : if (hapd->conf->hs20_osu_providers_count)
163 : 4 : wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
164 [ + + ]: 298 : if (hapd->conf->hs20_icons_count)
165 : 4 : wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
166 : 298 : gas_anqp_set_element_len(buf, len);
167 : 298 : }
168 : : #endif /* CONFIG_HS20 */
169 : :
170 : :
171 : 151 : static void anqp_add_capab_list(struct hostapd_data *hapd,
172 : : struct wpabuf *buf)
173 : : {
174 : : u8 *len;
175 : :
176 : 151 : len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
177 : 151 : wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
178 [ + - ]: 151 : if (hapd->conf->venue_name)
179 : 151 : wpabuf_put_le16(buf, ANQP_VENUE_NAME);
180 [ + + ]: 151 : if (hapd->conf->network_auth_type)
181 : 6 : wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
182 [ + - ]: 151 : if (hapd->conf->roaming_consortium)
183 : 151 : wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
184 [ + + ]: 151 : if (hapd->conf->ipaddr_type_configured)
185 : 6 : wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
186 [ + + ]: 151 : if (hapd->conf->nai_realm_data)
187 : 149 : wpabuf_put_le16(buf, ANQP_NAI_REALM);
188 [ + + ]: 151 : if (hapd->conf->anqp_3gpp_cell_net)
189 : 149 : wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
190 [ + + ]: 151 : if (hapd->conf->domain_name)
191 : 144 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
192 : : #ifdef CONFIG_HS20
193 : 151 : anqp_add_hs_capab_list(hapd, buf);
194 : : #endif /* CONFIG_HS20 */
195 : 151 : gas_anqp_set_element_len(buf, len);
196 : 151 : }
197 : :
198 : :
199 : 9 : static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
200 : : {
201 [ + - ]: 9 : if (hapd->conf->venue_name) {
202 : : u8 *len;
203 : : unsigned int i;
204 : 9 : len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
205 : 9 : wpabuf_put_u8(buf, hapd->conf->venue_group);
206 : 9 : wpabuf_put_u8(buf, hapd->conf->venue_type);
207 [ + + ]: 27 : for (i = 0; i < hapd->conf->venue_name_count; i++) {
208 : : struct hostapd_lang_string *vn;
209 : 18 : vn = &hapd->conf->venue_name[i];
210 : 18 : wpabuf_put_u8(buf, 3 + vn->name_len);
211 : 18 : wpabuf_put_data(buf, vn->lang, 3);
212 : 18 : wpabuf_put_data(buf, vn->name, vn->name_len);
213 : : }
214 : 9 : gas_anqp_set_element_len(buf, len);
215 : : }
216 : 9 : }
217 : :
218 : :
219 : 8 : static void anqp_add_network_auth_type(struct hostapd_data *hapd,
220 : : struct wpabuf *buf)
221 : : {
222 [ + + ]: 8 : if (hapd->conf->network_auth_type) {
223 : 2 : wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
224 : 2 : wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
225 : 2 : wpabuf_put_data(buf, hapd->conf->network_auth_type,
226 : 2 : hapd->conf->network_auth_type_len);
227 : : }
228 : 8 : }
229 : :
230 : :
231 : 19 : static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
232 : : struct wpabuf *buf)
233 : : {
234 : : unsigned int i;
235 : : u8 *len;
236 : :
237 : 19 : len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
238 [ + + ]: 95 : for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
239 : : struct hostapd_roaming_consortium *rc;
240 : 76 : rc = &hapd->conf->roaming_consortium[i];
241 : 76 : wpabuf_put_u8(buf, rc->len);
242 : 76 : wpabuf_put_data(buf, rc->oi, rc->len);
243 : : }
244 : 19 : gas_anqp_set_element_len(buf, len);
245 : 19 : }
246 : :
247 : :
248 : 8 : static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
249 : : struct wpabuf *buf)
250 : : {
251 [ + + ]: 8 : if (hapd->conf->ipaddr_type_configured) {
252 : 2 : wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
253 : 2 : wpabuf_put_le16(buf, 1);
254 : 2 : wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
255 : : }
256 : 8 : }
257 : :
258 : :
259 : 239 : static void anqp_add_nai_realm_eap(struct wpabuf *buf,
260 : : struct hostapd_nai_realm_data *realm)
261 : : {
262 : : unsigned int i, j;
263 : :
264 : 239 : wpabuf_put_u8(buf, realm->eap_method_count);
265 : :
266 [ + + ]: 484 : for (i = 0; i < realm->eap_method_count; i++) {
267 : 245 : struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
268 : 245 : wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
269 : 245 : wpabuf_put_u8(buf, eap->eap_method);
270 : 245 : wpabuf_put_u8(buf, eap->num_auths);
271 [ + + ]: 603 : for (j = 0; j < eap->num_auths; j++) {
272 : 358 : wpabuf_put_u8(buf, eap->auth_id[j]);
273 : 358 : wpabuf_put_u8(buf, 1);
274 : 358 : wpabuf_put_u8(buf, eap->auth_val[j]);
275 : : }
276 : : }
277 : 239 : }
278 : :
279 : :
280 : 1 : static void anqp_add_nai_realm_data(struct wpabuf *buf,
281 : : struct hostapd_nai_realm_data *realm,
282 : : unsigned int realm_idx)
283 : : {
284 : : u8 *realm_data_len;
285 : :
286 : 1 : wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
287 : 1 : (int) os_strlen(realm->realm[realm_idx]));
288 : 1 : realm_data_len = wpabuf_put(buf, 2);
289 : 1 : wpabuf_put_u8(buf, realm->encoding);
290 : 1 : wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
291 : 1 : wpabuf_put_str(buf, realm->realm[realm_idx]);
292 : 1 : anqp_add_nai_realm_eap(buf, realm);
293 : 1 : gas_anqp_set_element_len(buf, realm_data_len);
294 : 1 : }
295 : :
296 : :
297 : 1 : static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
298 : : struct wpabuf *buf,
299 : : const u8 *home_realm,
300 : : size_t home_realm_len)
301 : : {
302 : : unsigned int i, j, k;
303 : 1 : u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
304 : : struct hostapd_nai_realm_data *realm;
305 : : const u8 *pos, *realm_name, *end;
306 : : struct {
307 : : unsigned int realm_data_idx;
308 : : unsigned int realm_idx;
309 : : } matches[10];
310 : :
311 : 1 : pos = home_realm;
312 : 1 : end = pos + home_realm_len;
313 [ - + ]: 1 : if (pos + 1 > end) {
314 : 0 : wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
315 : : home_realm, home_realm_len);
316 : 0 : return -1;
317 : : }
318 : 1 : num_realms = *pos++;
319 : :
320 [ + + ][ + - ]: 2 : for (i = 0; i < num_realms && num_matching < 10; i++) {
321 [ - + ]: 1 : if (pos + 2 > end) {
322 : 0 : wpa_hexdump(MSG_DEBUG,
323 : : "Truncated NAI Home Realm Query",
324 : : home_realm, home_realm_len);
325 : 0 : return -1;
326 : : }
327 : 1 : encoding = *pos++;
328 : 1 : realm_len = *pos++;
329 [ - + ]: 1 : if (pos + realm_len > end) {
330 : 0 : wpa_hexdump(MSG_DEBUG,
331 : : "Truncated NAI Home Realm Query",
332 : : home_realm, home_realm_len);
333 : 0 : return -1;
334 : : }
335 : 1 : realm_name = pos;
336 [ + + ][ + - ]: 3 : for (j = 0; j < hapd->conf->nai_realm_count &&
337 : 2 : num_matching < 10; j++) {
338 : : const u8 *rpos, *rend;
339 : 2 : realm = &hapd->conf->nai_realm_data[j];
340 [ - + ]: 2 : if (encoding != realm->encoding)
341 : 0 : continue;
342 : :
343 : 2 : rpos = realm_name;
344 [ + + ][ + - ]: 4 : while (rpos < realm_name + realm_len &&
345 : : num_matching < 10) {
346 [ + + ]: 24 : for (rend = rpos;
347 : 22 : rend < realm_name + realm_len; rend++) {
348 [ - + ]: 22 : if (*rend == ';')
349 : 0 : break;
350 : : }
351 [ + - ][ + + ]: 4 : for (k = 0; k < MAX_NAI_REALMS &&
352 [ + - ]: 2 : realm->realm[k] &&
353 : 2 : num_matching < 10; k++) {
354 [ + + ]: 2 : if ((int) os_strlen(realm->realm[k]) !=
355 [ - + ]: 1 : rend - rpos ||
356 : 1 : os_strncmp((char *) rpos,
357 : : realm->realm[k],
358 : : rend - rpos) != 0)
359 : 1 : continue;
360 : 1 : matches[num_matching].realm_data_idx =
361 : : j;
362 : 1 : matches[num_matching].realm_idx = k;
363 : 1 : num_matching++;
364 : : }
365 : 2 : rpos = rend + 1;
366 : : }
367 : : }
368 : 1 : pos += realm_len;
369 : : }
370 : :
371 : 1 : realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
372 : 1 : wpabuf_put_le16(buf, num_matching);
373 : :
374 : : /*
375 : : * There are two ways to format. 1. each realm in a NAI Realm Data unit
376 : : * 2. all realms that share the same EAP methods in a NAI Realm Data
377 : : * unit. The first format is likely to be bigger in size than the
378 : : * second, but may be easier to parse and process by the receiver.
379 : : */
380 [ + + ]: 2 : for (i = 0; i < num_matching; i++) {
381 : 1 : wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
382 : : matches[i].realm_data_idx, matches[i].realm_idx);
383 : 1 : realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
384 : 1 : anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
385 : : }
386 : 1 : gas_anqp_set_element_len(buf, realm_list_len);
387 : 1 : return 0;
388 : : }
389 : :
390 : :
391 : 134 : static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
392 : : const u8 *home_realm, size_t home_realm_len,
393 : : int nai_realm, int nai_home_realm)
394 : : {
395 [ + + ][ + + ]: 265 : if (nai_realm && hapd->conf->nai_realm_data) {
396 : : u8 *len;
397 : : unsigned int i, j;
398 : 131 : len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
399 : 131 : wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
400 [ + + ]: 369 : for (i = 0; i < hapd->conf->nai_realm_count; i++) {
401 : : u8 *realm_data_len, *realm_len;
402 : : struct hostapd_nai_realm_data *realm;
403 : :
404 : 238 : realm = &hapd->conf->nai_realm_data[i];
405 : 238 : realm_data_len = wpabuf_put(buf, 2);
406 : 238 : wpabuf_put_u8(buf, realm->encoding);
407 : 238 : realm_len = wpabuf_put(buf, 1);
408 [ + + ]: 478 : for (j = 0; realm->realm[j]; j++) {
409 [ + + ]: 240 : if (j > 0)
410 : 2 : wpabuf_put_u8(buf, ';');
411 : 240 : wpabuf_put_str(buf, realm->realm[j]);
412 : : }
413 : 238 : *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
414 : 238 : anqp_add_nai_realm_eap(buf, realm);
415 : 238 : gas_anqp_set_element_len(buf, realm_data_len);
416 : : }
417 : 131 : gas_anqp_set_element_len(buf, len);
418 [ + + ][ + - ]: 3 : } else if (nai_home_realm && hapd->conf->nai_realm_data) {
419 : 1 : hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
420 : : home_realm_len);
421 : : }
422 : 134 : }
423 : :
424 : :
425 : 27 : static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
426 : : struct wpabuf *buf)
427 : : {
428 [ + + ]: 27 : if (hapd->conf->anqp_3gpp_cell_net) {
429 : 25 : wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
430 : 25 : wpabuf_put_le16(buf,
431 : 25 : hapd->conf->anqp_3gpp_cell_net_len);
432 : 25 : wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
433 : 25 : hapd->conf->anqp_3gpp_cell_net_len);
434 : : }
435 : 27 : }
436 : :
437 : :
438 : 112 : static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
439 : : {
440 [ + + ]: 112 : if (hapd->conf->domain_name) {
441 : 105 : wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
442 : 105 : wpabuf_put_le16(buf, hapd->conf->domain_name_len);
443 : 105 : wpabuf_put_data(buf, hapd->conf->domain_name,
444 : 105 : hapd->conf->domain_name_len);
445 : : }
446 : 112 : }
447 : :
448 : :
449 : : #ifdef CONFIG_HS20
450 : :
451 : 8 : static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
452 : : struct wpabuf *buf)
453 : : {
454 [ + + ]: 8 : if (hapd->conf->hs20_oper_friendly_name) {
455 : : u8 *len;
456 : : unsigned int i;
457 : 2 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
458 : 2 : wpabuf_put_be24(buf, OUI_WFA);
459 : 2 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
460 : 2 : wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
461 : 2 : wpabuf_put_u8(buf, 0); /* Reserved */
462 [ + + ]: 6 : for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
463 : : {
464 : : struct hostapd_lang_string *vn;
465 : 4 : vn = &hapd->conf->hs20_oper_friendly_name[i];
466 : 4 : wpabuf_put_u8(buf, 3 + vn->name_len);
467 : 4 : wpabuf_put_data(buf, vn->lang, 3);
468 : 4 : wpabuf_put_data(buf, vn->name, vn->name_len);
469 : : }
470 : 2 : gas_anqp_set_element_len(buf, len);
471 : : }
472 : 8 : }
473 : :
474 : :
475 : 27 : static void anqp_add_wan_metrics(struct hostapd_data *hapd,
476 : : struct wpabuf *buf)
477 : : {
478 [ + + ]: 27 : if (hapd->conf->hs20_wan_metrics) {
479 : 26 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
480 : 26 : wpabuf_put_be24(buf, OUI_WFA);
481 : 26 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
482 : 26 : wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
483 : 26 : wpabuf_put_u8(buf, 0); /* Reserved */
484 : 26 : wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
485 : 26 : gas_anqp_set_element_len(buf, len);
486 : : }
487 : 27 : }
488 : :
489 : :
490 : 19 : static void anqp_add_connection_capability(struct hostapd_data *hapd,
491 : : struct wpabuf *buf)
492 : : {
493 [ + - ]: 19 : if (hapd->conf->hs20_connection_capability) {
494 : 19 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
495 : 19 : wpabuf_put_be24(buf, OUI_WFA);
496 : 19 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
497 : 19 : wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
498 : 19 : wpabuf_put_u8(buf, 0); /* Reserved */
499 : 19 : wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
500 : 19 : hapd->conf->hs20_connection_capability_len);
501 : 19 : gas_anqp_set_element_len(buf, len);
502 : : }
503 : 19 : }
504 : :
505 : :
506 : 8 : static void anqp_add_operating_class(struct hostapd_data *hapd,
507 : : struct wpabuf *buf)
508 : : {
509 [ + - ]: 8 : if (hapd->conf->hs20_operating_class) {
510 : 8 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
511 : 8 : wpabuf_put_be24(buf, OUI_WFA);
512 : 8 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
513 : 8 : wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
514 : 8 : wpabuf_put_u8(buf, 0); /* Reserved */
515 : 8 : wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
516 : 8 : hapd->conf->hs20_operating_class_len);
517 : 8 : gas_anqp_set_element_len(buf, len);
518 : : }
519 : 8 : }
520 : :
521 : :
522 : 2 : static void anqp_add_osu_provider(struct wpabuf *buf,
523 : : struct hostapd_bss_config *bss,
524 : : struct hs20_osu_provider *p)
525 : : {
526 : : u8 *len, *len2, *count;
527 : : unsigned int i;
528 : :
529 : 2 : len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
530 : :
531 : : /* OSU Friendly Name Duples */
532 : 2 : len2 = wpabuf_put(buf, 2);
533 [ + + ]: 6 : for (i = 0; i < p->friendly_name_count; i++) {
534 : 4 : struct hostapd_lang_string *s = &p->friendly_name[i];
535 : 4 : wpabuf_put_u8(buf, 3 + s->name_len);
536 : 4 : wpabuf_put_data(buf, s->lang, 3);
537 : 4 : wpabuf_put_data(buf, s->name, s->name_len);
538 : : }
539 : 2 : WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
540 : :
541 : : /* OSU Server URI */
542 [ + - ]: 2 : if (p->server_uri) {
543 : 2 : wpabuf_put_u8(buf, os_strlen(p->server_uri));
544 : 2 : wpabuf_put_str(buf, p->server_uri);
545 : : } else
546 : 0 : wpabuf_put_u8(buf, 0);
547 : :
548 : : /* OSU Method List */
549 : 2 : count = wpabuf_put(buf, 1);
550 [ + + ]: 4 : for (i = 0; p->method_list[i] >= 0; i++)
551 : 2 : wpabuf_put_u8(buf, p->method_list[i]);
552 : 2 : *count = i;
553 : :
554 : : /* Icons Available */
555 : 2 : len2 = wpabuf_put(buf, 2);
556 [ + + ]: 4 : for (i = 0; i < p->icons_count; i++) {
557 : : size_t j;
558 : 2 : struct hs20_icon *icon = NULL;
559 : :
560 [ + + ][ + - ]: 4 : for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
561 [ + - ]: 2 : if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
562 : : 0)
563 : 2 : icon = &bss->hs20_icons[j];
564 : : }
565 [ - + ]: 2 : if (!icon)
566 : 0 : continue; /* icon info not found */
567 : :
568 : 2 : wpabuf_put_le16(buf, icon->width);
569 : 2 : wpabuf_put_le16(buf, icon->height);
570 : 2 : wpabuf_put_data(buf, icon->language, 3);
571 : 2 : wpabuf_put_u8(buf, os_strlen(icon->type));
572 : 2 : wpabuf_put_str(buf, icon->type);
573 : 2 : wpabuf_put_u8(buf, os_strlen(icon->name));
574 : 2 : wpabuf_put_str(buf, icon->name);
575 : : }
576 : 2 : WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
577 : :
578 : : /* OSU_NAI */
579 [ - + ]: 2 : if (p->osu_nai) {
580 : 0 : wpabuf_put_u8(buf, os_strlen(p->osu_nai));
581 : 0 : wpabuf_put_str(buf, p->osu_nai);
582 : : } else
583 : 2 : wpabuf_put_u8(buf, 0);
584 : :
585 : : /* OSU Service Description Duples */
586 : 2 : len2 = wpabuf_put(buf, 2);
587 [ + + ]: 6 : for (i = 0; i < p->service_desc_count; i++) {
588 : 4 : struct hostapd_lang_string *s = &p->service_desc[i];
589 : 4 : wpabuf_put_u8(buf, 3 + s->name_len);
590 : 4 : wpabuf_put_data(buf, s->lang, 3);
591 : 4 : wpabuf_put_data(buf, s->name, s->name_len);
592 : : }
593 : 2 : WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
594 : :
595 : 2 : WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
596 : 2 : }
597 : :
598 : :
599 : 8 : static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
600 : : struct wpabuf *buf)
601 : : {
602 [ + + ]: 8 : if (hapd->conf->hs20_osu_providers_count) {
603 : : size_t i;
604 : 2 : u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
605 : 2 : wpabuf_put_be24(buf, OUI_WFA);
606 : 2 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
607 : 2 : wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
608 : 2 : wpabuf_put_u8(buf, 0); /* Reserved */
609 : :
610 : : /* OSU SSID */
611 : 2 : wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
612 : 2 : wpabuf_put_data(buf, hapd->conf->osu_ssid,
613 : 2 : hapd->conf->osu_ssid_len);
614 : :
615 : : /* Number of OSU Providers */
616 : 2 : wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
617 : :
618 [ + + ]: 4 : for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
619 : 2 : anqp_add_osu_provider(
620 : : buf, hapd->conf,
621 : 2 : &hapd->conf->hs20_osu_providers[i]);
622 : : }
623 : :
624 : 2 : gas_anqp_set_element_len(buf, len);
625 : : }
626 : 8 : }
627 : :
628 : :
629 : 2 : static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
630 : : struct wpabuf *buf,
631 : : const u8 *name, size_t name_len)
632 : : {
633 : : struct hs20_icon *icon;
634 : : size_t i;
635 : : u8 *len;
636 : :
637 : 2 : wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
638 : : name, name_len);
639 [ + - ]: 2 : for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
640 : 2 : icon = &hapd->conf->hs20_icons[i];
641 [ + - ][ + - ]: 2 : if (name_len == os_strlen(icon->name) &&
642 : 2 : os_memcmp(name, icon->name, name_len) == 0)
643 : 2 : break;
644 : : }
645 : :
646 [ + - ]: 2 : if (i < hapd->conf->hs20_icons_count)
647 : 2 : icon = &hapd->conf->hs20_icons[i];
648 : : else
649 : 0 : icon = NULL;
650 : :
651 : 2 : len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
652 : 2 : wpabuf_put_be24(buf, OUI_WFA);
653 : 2 : wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
654 : 2 : wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
655 : 2 : wpabuf_put_u8(buf, 0); /* Reserved */
656 : :
657 [ + - ]: 2 : if (icon) {
658 : : char *data;
659 : : size_t data_len;
660 : :
661 : 2 : data = os_readfile(icon->file, &data_len);
662 [ - + ][ + - ]: 2 : if (data == NULL || data_len > 65535) {
663 : 0 : wpabuf_put_u8(buf, 2); /* Download Status:
664 : : * Unspecified file error */
665 : 0 : wpabuf_put_u8(buf, 0);
666 : 0 : wpabuf_put_le16(buf, 0);
667 : : } else {
668 : 2 : wpabuf_put_u8(buf, 0); /* Download Status: Success */
669 : 2 : wpabuf_put_u8(buf, os_strlen(icon->type));
670 : 2 : wpabuf_put_str(buf, icon->type);
671 : 2 : wpabuf_put_le16(buf, data_len);
672 : 2 : wpabuf_put_data(buf, data, data_len);
673 : : }
674 : 2 : os_free(data);
675 : : } else {
676 : 0 : wpabuf_put_u8(buf, 1); /* Download Status: File not found */
677 : 0 : wpabuf_put_u8(buf, 0);
678 : 0 : wpabuf_put_le16(buf, 0);
679 : : }
680 : :
681 : 2 : gas_anqp_set_element_len(buf, len);
682 : 2 : }
683 : :
684 : : #endif /* CONFIG_HS20 */
685 : :
686 : :
687 : : static struct wpabuf *
688 : 161 : gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
689 : : unsigned int request,
690 : : struct gas_dialog_info *di,
691 : : const u8 *home_realm, size_t home_realm_len,
692 : : const u8 *icon_name, size_t icon_name_len)
693 : : {
694 : : struct wpabuf *buf;
695 : : size_t len;
696 : :
697 : 161 : len = 1400;
698 [ + + ]: 161 : if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
699 : 134 : len += 1000;
700 [ + + ]: 161 : if (request & ANQP_REQ_ICON_REQUEST)
701 : 2 : len += 65536;
702 : :
703 : 161 : buf = wpabuf_alloc(len);
704 [ - + ]: 161 : if (buf == NULL)
705 : 0 : return NULL;
706 : :
707 [ + + ]: 161 : if (request & ANQP_REQ_CAPABILITY_LIST)
708 : 151 : anqp_add_capab_list(hapd, buf);
709 [ + + ]: 161 : if (request & ANQP_REQ_VENUE_NAME)
710 : 9 : anqp_add_venue_name(hapd, buf);
711 [ + + ]: 161 : if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
712 : 8 : anqp_add_network_auth_type(hapd, buf);
713 [ + + ]: 161 : if (request & ANQP_REQ_ROAMING_CONSORTIUM)
714 : 19 : anqp_add_roaming_consortium(hapd, buf);
715 [ + + ]: 161 : if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
716 : 8 : anqp_add_ip_addr_type_availability(hapd, buf);
717 [ + + ]: 161 : if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
718 : 134 : anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
719 : : request & ANQP_REQ_NAI_REALM,
720 : : request & ANQP_REQ_NAI_HOME_REALM);
721 [ + + ]: 161 : if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
722 : 27 : anqp_add_3gpp_cellular_network(hapd, buf);
723 [ + + ]: 161 : if (request & ANQP_REQ_DOMAIN_NAME)
724 : 112 : anqp_add_domain_name(hapd, buf);
725 : :
726 : : #ifdef CONFIG_HS20
727 [ + + ]: 161 : if (request & ANQP_REQ_HS_CAPABILITY_LIST)
728 : 147 : anqp_add_hs_capab_list(hapd, buf);
729 [ + + ]: 161 : if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
730 : 8 : anqp_add_operator_friendly_name(hapd, buf);
731 [ + + ]: 161 : if (request & ANQP_REQ_WAN_METRICS)
732 : 27 : anqp_add_wan_metrics(hapd, buf);
733 [ + + ]: 161 : if (request & ANQP_REQ_CONNECTION_CAPABILITY)
734 : 19 : anqp_add_connection_capability(hapd, buf);
735 [ + + ]: 161 : if (request & ANQP_REQ_OPERATING_CLASS)
736 : 8 : anqp_add_operating_class(hapd, buf);
737 [ + + ]: 161 : if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
738 : 8 : anqp_add_osu_providers_list(hapd, buf);
739 [ + + ]: 161 : if (request & ANQP_REQ_ICON_REQUEST)
740 : 2 : anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
741 : : #endif /* CONFIG_HS20 */
742 : :
743 : 161 : return buf;
744 : : }
745 : :
746 : :
747 : 0 : static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
748 : : {
749 : 0 : struct gas_dialog_info *dia = eloop_data;
750 : :
751 : 0 : wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
752 : 0 : "dialog token %d", dia->dialog_token);
753 : :
754 : 0 : gas_serv_dialog_clear(dia);
755 : 0 : }
756 : :
757 : :
758 : : struct anqp_query_info {
759 : : unsigned int request;
760 : : unsigned int remote_request;
761 : : const u8 *home_realm_query;
762 : : size_t home_realm_query_len;
763 : : const u8 *icon_name;
764 : : size_t icon_name_len;
765 : : u16 remote_delay;
766 : : };
767 : :
768 : :
769 : 684 : static void set_anqp_req(unsigned int bit, const char *name, int local,
770 : : unsigned int remote, u16 remote_delay,
771 : : struct anqp_query_info *qi)
772 : : {
773 : 684 : qi->request |= bit;
774 [ + + ]: 684 : if (local) {
775 : 648 : wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
776 [ - + ]: 36 : } else if (bit & remote) {
777 : 0 : wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
778 : 0 : qi->remote_request |= bit;
779 [ # # ]: 0 : if (remote_delay > qi->remote_delay)
780 : 0 : qi->remote_delay = remote_delay;
781 : : } else {
782 : 36 : wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
783 : : }
784 : 684 : }
785 : :
786 : :
787 : 467 : static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
788 : : struct anqp_query_info *qi)
789 : : {
790 [ + + + + : 467 : switch (info_id) {
+ + + +
- ]
791 : : case ANQP_CAPABILITY_LIST:
792 : 151 : set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
793 : : 0, qi);
794 : 151 : break;
795 : : case ANQP_VENUE_NAME:
796 : 9 : set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
797 : 9 : hapd->conf->venue_name != NULL, 0, 0, qi);
798 : 9 : break;
799 : : case ANQP_NETWORK_AUTH_TYPE:
800 : 8 : set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
801 : 8 : hapd->conf->network_auth_type != NULL,
802 : : 0, 0, qi);
803 : 8 : break;
804 : : case ANQP_ROAMING_CONSORTIUM:
805 : 19 : set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
806 : 19 : hapd->conf->roaming_consortium != NULL, 0, 0, qi);
807 : 19 : break;
808 : : case ANQP_IP_ADDR_TYPE_AVAILABILITY:
809 : 8 : set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
810 : : "IP Addr Type Availability",
811 : 8 : hapd->conf->ipaddr_type_configured,
812 : : 0, 0, qi);
813 : 8 : break;
814 : : case ANQP_NAI_REALM:
815 : 133 : set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
816 : 133 : hapd->conf->nai_realm_data != NULL,
817 : : 0, 0, qi);
818 : 133 : break;
819 : : case ANQP_3GPP_CELLULAR_NETWORK:
820 : 27 : set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
821 : : "3GPP Cellular Network",
822 : 27 : hapd->conf->anqp_3gpp_cell_net != NULL,
823 : : 0, 0, qi);
824 : 27 : break;
825 : : case ANQP_DOMAIN_NAME:
826 : 112 : set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
827 : 112 : hapd->conf->domain_name != NULL,
828 : : 0, 0, qi);
829 : 112 : break;
830 : : default:
831 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
832 : : info_id);
833 : 0 : break;
834 : : }
835 : 467 : }
836 : :
837 : :
838 : 158 : static void rx_anqp_query_list(struct hostapd_data *hapd,
839 : : const u8 *pos, const u8 *end,
840 : : struct anqp_query_info *qi)
841 : : {
842 : 158 : wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
843 : 158 : (unsigned int) (end - pos) / 2);
844 : :
845 [ + + ]: 625 : while (pos + 2 <= end) {
846 : 467 : rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
847 : 467 : pos += 2;
848 : : }
849 : 158 : }
850 : :
851 : :
852 : : #ifdef CONFIG_HS20
853 : :
854 : 217 : static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
855 : : struct anqp_query_info *qi)
856 : : {
857 [ + + + + : 217 : switch (subtype) {
+ + - ]
858 : : case HS20_STYPE_CAPABILITY_LIST:
859 : 147 : set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
860 : : 1, 0, 0, qi);
861 : 147 : break;
862 : : case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
863 : 8 : set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
864 : : "Operator Friendly Name",
865 : 8 : hapd->conf->hs20_oper_friendly_name != NULL,
866 : : 0, 0, qi);
867 : 8 : break;
868 : : case HS20_STYPE_WAN_METRICS:
869 : 27 : set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
870 : 27 : hapd->conf->hs20_wan_metrics != NULL,
871 : : 0, 0, qi);
872 : 27 : break;
873 : : case HS20_STYPE_CONNECTION_CAPABILITY:
874 : 19 : set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
875 : : "Connection Capability",
876 : 19 : hapd->conf->hs20_connection_capability != NULL,
877 : : 0, 0, qi);
878 : 19 : break;
879 : : case HS20_STYPE_OPERATING_CLASS:
880 : 8 : set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
881 : 8 : hapd->conf->hs20_operating_class != NULL,
882 : : 0, 0, qi);
883 : 8 : break;
884 : : case HS20_STYPE_OSU_PROVIDERS_LIST:
885 : 8 : set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
886 : 8 : hapd->conf->hs20_osu_providers_count, 0, 0, qi);
887 : 8 : break;
888 : : default:
889 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
890 : : subtype);
891 : 0 : break;
892 : : }
893 : 217 : }
894 : :
895 : :
896 : 1 : static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
897 : : const u8 *pos, const u8 *end,
898 : : struct anqp_query_info *qi)
899 : : {
900 : 1 : qi->request |= ANQP_REQ_NAI_HOME_REALM;
901 : 1 : qi->home_realm_query = pos;
902 : 1 : qi->home_realm_query_len = end - pos;
903 [ + - ]: 1 : if (hapd->conf->nai_realm_data != NULL) {
904 : 1 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
905 : : "(local)");
906 : : } else {
907 : 0 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
908 : : "available");
909 : : }
910 : 1 : }
911 : :
912 : :
913 : 2 : static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
914 : : const u8 *pos, const u8 *end,
915 : : struct anqp_query_info *qi)
916 : : {
917 : 2 : qi->request |= ANQP_REQ_ICON_REQUEST;
918 : 2 : qi->icon_name = pos;
919 : 2 : qi->icon_name_len = end - pos;
920 [ + - ]: 2 : if (hapd->conf->hs20_icons_count) {
921 : 2 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
922 : : "(local)");
923 : : } else {
924 : 0 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
925 : : "available");
926 : : }
927 : 2 : }
928 : :
929 : :
930 : 150 : static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
931 : : const u8 *pos, const u8 *end,
932 : : struct anqp_query_info *qi)
933 : : {
934 : : u32 oui;
935 : : u8 subtype;
936 : :
937 [ - + ]: 150 : if (pos + 4 > end) {
938 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
939 : : "Query element");
940 : 0 : return;
941 : : }
942 : :
943 : 150 : oui = WPA_GET_BE24(pos);
944 : 150 : pos += 3;
945 [ - + ]: 150 : if (oui != OUI_WFA) {
946 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
947 : : oui);
948 : 0 : return;
949 : : }
950 : :
951 [ - + ]: 150 : if (*pos != HS20_ANQP_OUI_TYPE) {
952 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
953 : 0 : *pos);
954 : 0 : return;
955 : : }
956 : 150 : pos++;
957 : :
958 [ - + ]: 150 : if (pos + 1 >= end)
959 : 0 : return;
960 : :
961 : 150 : subtype = *pos++;
962 : 150 : pos++; /* Reserved */
963 [ + + + - ]: 150 : switch (subtype) {
964 : : case HS20_STYPE_QUERY_LIST:
965 : 147 : wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
966 [ + + ]: 364 : while (pos < end) {
967 : 217 : rx_anqp_hs_query_list(hapd, *pos, qi);
968 : 217 : pos++;
969 : : }
970 : 147 : break;
971 : : case HS20_STYPE_NAI_HOME_REALM_QUERY:
972 : 1 : rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
973 : 1 : break;
974 : : case HS20_STYPE_ICON_REQUEST:
975 : 2 : rx_anqp_hs_icon_request(hapd, pos, end, qi);
976 : 2 : break;
977 : : default:
978 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
979 : : "%u", subtype);
980 : 150 : break;
981 : : }
982 : : }
983 : :
984 : : #endif /* CONFIG_HS20 */
985 : :
986 : :
987 : 161 : static void gas_serv_req_local_processing(struct hostapd_data *hapd,
988 : : const u8 *sa, u8 dialog_token,
989 : : struct anqp_query_info *qi, int prot)
990 : : {
991 : : struct wpabuf *buf, *tx_buf;
992 : :
993 : 161 : buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
994 : : qi->home_realm_query,
995 : : qi->home_realm_query_len,
996 : : qi->icon_name, qi->icon_name_len);
997 : 161 : wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
998 : : buf);
999 [ - + ]: 161 : if (!buf)
1000 : 0 : return;
1001 : :
1002 [ + + ][ + + ]: 161 : if (wpabuf_len(buf) > hapd->gas_frag_limit ||
1003 : 6 : hapd->conf->gas_comeback_delay) {
1004 : : struct gas_dialog_info *di;
1005 : 6 : u16 comeback_delay = 1;
1006 : :
1007 [ + + ]: 6 : if (hapd->conf->gas_comeback_delay) {
1008 : : /* Testing - allow overriding of the delay value */
1009 : 1 : comeback_delay = hapd->conf->gas_comeback_delay;
1010 : : }
1011 : :
1012 : 6 : wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
1013 : : "initial response - use GAS comeback");
1014 : 6 : di = gas_dialog_create(hapd, sa, dialog_token);
1015 [ - + ]: 6 : if (!di) {
1016 : 0 : wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
1017 : : "for " MACSTR " (dialog token %u)",
1018 : 0 : MAC2STR(sa), dialog_token);
1019 : 0 : wpabuf_free(buf);
1020 : 0 : return;
1021 : : }
1022 : 6 : di->prot = prot;
1023 : 6 : di->sd_resp = buf;
1024 : 6 : di->sd_resp_pos = 0;
1025 : 6 : tx_buf = gas_anqp_build_initial_resp_buf(
1026 : : dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
1027 : : NULL);
1028 : : } else {
1029 : 155 : wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
1030 : 155 : tx_buf = gas_anqp_build_initial_resp_buf(
1031 : : dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
1032 : 155 : wpabuf_free(buf);
1033 : : }
1034 [ - + ]: 161 : if (!tx_buf)
1035 : 0 : return;
1036 [ + + ]: 161 : if (prot)
1037 : 1 : convert_to_protected_dual(tx_buf);
1038 : 322 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1039 : 161 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1040 : 161 : wpabuf_free(tx_buf);
1041 : : }
1042 : :
1043 : :
1044 : 161 : static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
1045 : : const u8 *sa,
1046 : : const u8 *data, size_t len, int prot)
1047 : : {
1048 : 161 : const u8 *pos = data;
1049 : 161 : const u8 *end = data + len;
1050 : : const u8 *next;
1051 : : u8 dialog_token;
1052 : : u16 slen;
1053 : : struct anqp_query_info qi;
1054 : : const u8 *adv_proto;
1055 : :
1056 [ - + ]: 161 : if (len < 1 + 2)
1057 : 0 : return;
1058 : :
1059 : 161 : os_memset(&qi, 0, sizeof(qi));
1060 : :
1061 : 161 : dialog_token = *pos++;
1062 : 161 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1063 : : "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
1064 : 966 : MAC2STR(sa), dialog_token);
1065 : :
1066 [ - + ]: 161 : if (*pos != WLAN_EID_ADV_PROTO) {
1067 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1068 : 0 : "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
1069 : 0 : return;
1070 : : }
1071 : 161 : adv_proto = pos++;
1072 : :
1073 : 161 : slen = *pos++;
1074 : 161 : next = pos + slen;
1075 [ + - ][ - + ]: 161 : if (next > end || slen < 2) {
1076 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1077 : : "GAS: Invalid IE in GAS Initial Request");
1078 : 0 : return;
1079 : : }
1080 : 161 : pos++; /* skip QueryRespLenLimit and PAME-BI */
1081 : :
1082 [ - + ]: 161 : if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
1083 : : struct wpabuf *buf;
1084 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1085 : : "GAS: Unsupported GAS advertisement protocol id %u",
1086 : 0 : *pos);
1087 [ # # ]: 0 : if (sa[0] & 0x01)
1088 : 0 : return; /* Invalid source address - drop silently */
1089 : 0 : buf = gas_build_initial_resp(
1090 : : dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
1091 : 0 : 0, 2 + slen + 2);
1092 [ # # ]: 0 : if (buf == NULL)
1093 : 0 : return;
1094 : 0 : wpabuf_put_data(buf, adv_proto, 2 + slen);
1095 : 0 : wpabuf_put_le16(buf, 0); /* Query Response Length */
1096 [ # # ]: 0 : if (prot)
1097 : 0 : convert_to_protected_dual(buf);
1098 : 0 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1099 : 0 : wpabuf_head(buf), wpabuf_len(buf));
1100 : 0 : wpabuf_free(buf);
1101 : 0 : return;
1102 : : }
1103 : :
1104 : 161 : pos = next;
1105 : : /* Query Request */
1106 [ - + ]: 161 : if (pos + 2 > end)
1107 : 0 : return;
1108 : 161 : slen = WPA_GET_LE16(pos);
1109 : 161 : pos += 2;
1110 [ - + ]: 161 : if (pos + slen > end)
1111 : 0 : return;
1112 : 161 : end = pos + slen;
1113 : :
1114 : : /* ANQP Query Request */
1115 [ + + ]: 469 : while (pos < end) {
1116 : : u16 info_id, elen;
1117 : :
1118 [ - + ]: 308 : if (pos + 4 > end)
1119 : 0 : return;
1120 : :
1121 : 308 : info_id = WPA_GET_LE16(pos);
1122 : 308 : pos += 2;
1123 : 308 : elen = WPA_GET_LE16(pos);
1124 : 308 : pos += 2;
1125 : :
1126 [ - + ]: 308 : if (pos + elen > end) {
1127 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
1128 : 0 : return;
1129 : : }
1130 : :
1131 [ + + - ]: 308 : switch (info_id) {
1132 : : case ANQP_QUERY_LIST:
1133 : 158 : rx_anqp_query_list(hapd, pos, pos + elen, &qi);
1134 : 158 : break;
1135 : : #ifdef CONFIG_HS20
1136 : : case ANQP_VENDOR_SPECIFIC:
1137 : 150 : rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
1138 : 150 : break;
1139 : : #endif /* CONFIG_HS20 */
1140 : : default:
1141 : 0 : wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
1142 : : "Request element %u", info_id);
1143 : 0 : break;
1144 : : }
1145 : :
1146 : 308 : pos += elen;
1147 : : }
1148 : :
1149 : 161 : gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
1150 : : }
1151 : :
1152 : :
1153 : 0 : void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
1154 : : struct gas_dialog_info *dialog)
1155 : : {
1156 : : struct wpabuf *buf, *tx_buf;
1157 : 0 : u8 dialog_token = dialog->dialog_token;
1158 : : size_t frag_len;
1159 : :
1160 [ # # ]: 0 : if (dialog->sd_resp == NULL) {
1161 : 0 : buf = gas_serv_build_gas_resp_payload(hapd,
1162 : : dialog->all_requested,
1163 : : dialog, NULL, 0, NULL, 0);
1164 : 0 : wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
1165 : : buf);
1166 [ # # ]: 0 : if (!buf)
1167 : 0 : goto tx_gas_response_done;
1168 : 0 : dialog->sd_resp = buf;
1169 : 0 : dialog->sd_resp_pos = 0;
1170 : : }
1171 : 0 : frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1172 [ # # ][ # # ]: 0 : if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
[ # # ]
1173 : 0 : hapd->conf->gas_comeback_delay) {
1174 : 0 : u16 comeback_delay_tus = dialog->comeback_delay +
1175 : : GAS_SERV_COMEBACK_DELAY_FUDGE;
1176 : : u32 comeback_delay_secs, comeback_delay_usecs;
1177 : :
1178 [ # # ]: 0 : if (hapd->conf->gas_comeback_delay) {
1179 : : /* Testing - allow overriding of the delay value */
1180 : 0 : comeback_delay_tus = hapd->conf->gas_comeback_delay;
1181 : : }
1182 : :
1183 : 0 : wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
1184 : : "%u) and comeback delay %u, "
1185 : : "requesting comebacks", (unsigned int) frag_len,
1186 : 0 : (unsigned int) hapd->gas_frag_limit,
1187 : 0 : dialog->comeback_delay);
1188 : 0 : tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
1189 : : WLAN_STATUS_SUCCESS,
1190 : : comeback_delay_tus,
1191 : : NULL);
1192 [ # # ]: 0 : if (tx_buf) {
1193 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG,
1194 : : "GAS: Tx GAS Initial Resp (comeback = 10TU)");
1195 [ # # ]: 0 : if (dialog->prot)
1196 : 0 : convert_to_protected_dual(tx_buf);
1197 : 0 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
1198 : : dst,
1199 : 0 : wpabuf_head(tx_buf),
1200 : : wpabuf_len(tx_buf));
1201 : : }
1202 : 0 : wpabuf_free(tx_buf);
1203 : :
1204 : : /* start a timer of 1.5 * comeback-delay */
1205 : 0 : comeback_delay_tus = comeback_delay_tus +
1206 : : (comeback_delay_tus / 2);
1207 : 0 : comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
1208 : 0 : comeback_delay_usecs = (comeback_delay_tus * 1024) -
1209 : 0 : (comeback_delay_secs * 1000000);
1210 : 0 : eloop_register_timeout(comeback_delay_secs,
1211 : : comeback_delay_usecs,
1212 : : gas_serv_clear_cached_ies, dialog,
1213 : : NULL);
1214 : 0 : goto tx_gas_response_done;
1215 : : }
1216 : :
1217 : 0 : buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1218 : : dialog->sd_resp_pos, frag_len);
1219 [ # # ]: 0 : if (buf == NULL) {
1220 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
1221 : : "failed");
1222 : 0 : goto tx_gas_response_done;
1223 : : }
1224 : 0 : tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
1225 : : WLAN_STATUS_SUCCESS, 0, buf);
1226 : 0 : wpabuf_free(buf);
1227 [ # # ]: 0 : if (tx_buf == NULL)
1228 : 0 : goto tx_gas_response_done;
1229 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
1230 : : "Response (frag_id %d frag_len %d)",
1231 : 0 : dialog->sd_frag_id, (int) frag_len);
1232 : 0 : dialog->sd_frag_id++;
1233 : :
1234 [ # # ]: 0 : if (dialog->prot)
1235 : 0 : convert_to_protected_dual(tx_buf);
1236 : 0 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
1237 : 0 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1238 : 0 : wpabuf_free(tx_buf);
1239 : : tx_gas_response_done:
1240 : 0 : gas_serv_clear_cached_ies(dialog, NULL);
1241 : 0 : }
1242 : :
1243 : :
1244 : 30 : static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
1245 : : const u8 *sa,
1246 : : const u8 *data, size_t len, int prot)
1247 : : {
1248 : : struct gas_dialog_info *dialog;
1249 : : struct wpabuf *buf, *tx_buf;
1250 : : u8 dialog_token;
1251 : : size_t frag_len;
1252 : 30 : int more = 0;
1253 : :
1254 : 30 : wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
1255 [ - + ]: 30 : if (len < 1)
1256 : 0 : return;
1257 : 30 : dialog_token = *data;
1258 : 30 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
1259 : : dialog_token);
1260 : :
1261 : 30 : dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
1262 [ - + ]: 30 : if (!dialog) {
1263 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
1264 : : "response fragment for " MACSTR " dialog token %u",
1265 : 0 : MAC2STR(sa), dialog_token);
1266 : :
1267 [ # # ]: 0 : if (sa[0] & 0x01)
1268 : 0 : return; /* Invalid source address - drop silently */
1269 : 0 : tx_buf = gas_anqp_build_comeback_resp_buf(
1270 : : dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
1271 : : 0, NULL);
1272 [ # # ]: 0 : if (tx_buf == NULL)
1273 : 0 : return;
1274 : 0 : goto send_resp;
1275 : : }
1276 : :
1277 [ - + ]: 30 : if (dialog->sd_resp == NULL) {
1278 : 0 : wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
1279 : : dialog->requested, dialog->received);
1280 [ # # ]: 0 : if ((dialog->requested & dialog->received) !=
1281 : 0 : dialog->requested) {
1282 : 0 : wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
1283 : : "from remote processing");
1284 : 0 : gas_serv_dialog_clear(dialog);
1285 : 0 : tx_buf = gas_anqp_build_comeback_resp_buf(
1286 : : dialog_token,
1287 : : WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
1288 : : NULL);
1289 [ # # ]: 0 : if (tx_buf == NULL)
1290 : 0 : return;
1291 : 0 : goto send_resp;
1292 : : }
1293 : :
1294 : 0 : buf = gas_serv_build_gas_resp_payload(hapd,
1295 : : dialog->all_requested,
1296 : : dialog, NULL, 0, NULL, 0);
1297 : 0 : wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
1298 : : buf);
1299 [ # # ]: 0 : if (!buf)
1300 : 0 : goto rx_gas_comeback_req_done;
1301 : 0 : dialog->sd_resp = buf;
1302 : 0 : dialog->sd_resp_pos = 0;
1303 : : }
1304 : 30 : frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
1305 [ + + ]: 30 : if (frag_len > hapd->gas_frag_limit) {
1306 : 24 : frag_len = hapd->gas_frag_limit;
1307 : 24 : more = 1;
1308 : : }
1309 : 30 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
1310 : : (unsigned int) frag_len);
1311 : 30 : buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
1312 : : dialog->sd_resp_pos, frag_len);
1313 [ - + ]: 30 : if (buf == NULL) {
1314 : 0 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
1315 : : "buffer");
1316 : 0 : goto rx_gas_comeback_req_done;
1317 : : }
1318 : 30 : tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
1319 : : WLAN_STATUS_SUCCESS,
1320 : 30 : dialog->sd_frag_id,
1321 : : more, 0, buf);
1322 : 30 : wpabuf_free(buf);
1323 [ - + ]: 30 : if (tx_buf == NULL)
1324 : 0 : goto rx_gas_comeback_req_done;
1325 : 30 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
1326 : : "(frag_id %d more=%d frag_len=%d)",
1327 : 30 : dialog->sd_frag_id, more, (int) frag_len);
1328 : 30 : dialog->sd_frag_id++;
1329 : 30 : dialog->sd_resp_pos += frag_len;
1330 : :
1331 [ + + ]: 30 : if (more) {
1332 : 24 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
1333 : : "to be sent",
1334 : 24 : (int) (wpabuf_len(dialog->sd_resp) -
1335 : 24 : dialog->sd_resp_pos));
1336 : : } else {
1337 : 6 : wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
1338 : : "SD response sent");
1339 : 6 : gas_serv_dialog_clear(dialog);
1340 : 6 : gas_serv_free_dialogs(hapd, sa);
1341 : : }
1342 : :
1343 : : send_resp:
1344 [ - + ]: 30 : if (prot)
1345 : 0 : convert_to_protected_dual(tx_buf);
1346 : 60 : hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
1347 : 30 : wpabuf_head(tx_buf), wpabuf_len(tx_buf));
1348 : 30 : wpabuf_free(tx_buf);
1349 : 30 : return;
1350 : :
1351 : : rx_gas_comeback_req_done:
1352 : 30 : gas_serv_clear_cached_ies(dialog, NULL);
1353 : : }
1354 : :
1355 : :
1356 : 223 : static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
1357 : : int freq)
1358 : : {
1359 : 223 : struct hostapd_data *hapd = ctx;
1360 : : const struct ieee80211_mgmt *mgmt;
1361 : : size_t hdr_len;
1362 : : const u8 *sa, *data;
1363 : : int prot;
1364 : :
1365 : 223 : mgmt = (const struct ieee80211_mgmt *) buf;
1366 : 223 : hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
1367 [ - + ]: 223 : if (hdr_len > len)
1368 : 0 : return;
1369 [ + + ][ - + ]: 223 : if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
1370 : 1 : mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
1371 : 0 : return;
1372 : : /*
1373 : : * Note: Public Action and Protected Dual of Public Action frames share
1374 : : * the same payload structure, so it is fine to use definitions of
1375 : : * Public Action frames to process both.
1376 : : */
1377 : 223 : prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
1378 : 223 : sa = mgmt->sa;
1379 : 223 : len -= hdr_len;
1380 : 223 : data = &mgmt->u.action.u.public_action.action;
1381 [ + + + ]: 223 : switch (data[0]) {
1382 : : case WLAN_PA_GAS_INITIAL_REQ:
1383 : 161 : gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
1384 : 161 : break;
1385 : : case WLAN_PA_GAS_COMEBACK_REQ:
1386 : 30 : gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
1387 : 223 : break;
1388 : : }
1389 : : }
1390 : :
1391 : :
1392 : 429 : int gas_serv_init(struct hostapd_data *hapd)
1393 : : {
1394 : 429 : hapd->public_action_cb2 = gas_serv_rx_public_action;
1395 : 429 : hapd->public_action_cb2_ctx = hapd;
1396 : 429 : hapd->gas_frag_limit = 1400;
1397 [ - + ]: 429 : if (hapd->conf->gas_frag_limit > 0)
1398 : 0 : hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
1399 : 429 : return 0;
1400 : : }
1401 : :
1402 : :
1403 : 429 : void gas_serv_deinit(struct hostapd_data *hapd)
1404 : : {
1405 : 429 : }
|