/src/hostap/wpa_supplicant/hs20_supplicant.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2009, Atheros Communications, Inc. |
3 | | * Copyright (c) 2011-2013, 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 | | #include <sys/stat.h> |
11 | | |
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" |
18 | | #include "rsn_supp/wpa.h" |
19 | | #include "wpa_supplicant_i.h" |
20 | | #include "driver_i.h" |
21 | | #include "config.h" |
22 | | #include "scan.h" |
23 | | #include "bss.h" |
24 | | #include "bssid_ignore.h" |
25 | | #include "gas_query.h" |
26 | | #include "interworking.h" |
27 | | #include "hs20_supplicant.h" |
28 | | #include "base64.h" |
29 | | #include "notify.h" |
30 | | |
31 | | |
32 | | void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s) |
33 | 0 | { |
34 | 0 | struct wpa_bss *bss = wpa_s->current_bss; |
35 | 0 | const u8 *ie; |
36 | 0 | const u8 *ext_capa; |
37 | 0 | u32 filter = 0; |
38 | |
|
39 | 0 | if (!bss || !is_hs20_network(wpa_s, wpa_s->current_ssid, bss)) { |
40 | | /* Not configuring frame filtering - BSS is not a Hotspot 2.0 |
41 | | * network */ |
42 | 0 | return; |
43 | 0 | } |
44 | | |
45 | 0 | ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE); |
46 | | |
47 | | /* Check if DGAF disabled bit is zero (5th byte in the IE) */ |
48 | 0 | if (!ie || ie[1] < 5) |
49 | 0 | wpa_printf(MSG_DEBUG, |
50 | 0 | "Not configuring frame filtering - Can't extract DGAF bit"); |
51 | 0 | else if (!(ie[6] & HS20_DGAF_DISABLED)) |
52 | 0 | filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK; |
53 | |
|
54 | 0 | ext_capa = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB); |
55 | 0 | if (!ext_capa || ext_capa[1] < 2) { |
56 | 0 | wpa_printf(MSG_DEBUG, |
57 | 0 | "Not configuring frame filtering - Can't extract Proxy ARP bit"); |
58 | 0 | return; |
59 | 0 | } |
60 | | |
61 | 0 | if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_PROXY_ARP)) |
62 | 0 | filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP | |
63 | 0 | WPA_DATA_FRAME_FILTER_FLAG_NA; |
64 | |
|
65 | 0 | wpa_drv_configure_frame_filters(wpa_s, filter); |
66 | 0 | } |
67 | | |
68 | | |
69 | | void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id, int ap_release) |
70 | 0 | { |
71 | 0 | int release; |
72 | 0 | u8 conf; |
73 | |
|
74 | 0 | release = (HS20_VERSION >> 4) + 1; |
75 | 0 | if (ap_release > 0 && release > ap_release) |
76 | 0 | release = ap_release; |
77 | 0 | if (release < 2) |
78 | 0 | pps_mo_id = -1; |
79 | |
|
80 | 0 | wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); |
81 | 0 | wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5); |
82 | 0 | wpabuf_put_be24(buf, OUI_WFA); |
83 | 0 | wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE); |
84 | 0 | conf = (release - 1) << 4; |
85 | 0 | if (pps_mo_id >= 0) |
86 | 0 | conf |= HS20_PPS_MO_ID_PRESENT; |
87 | 0 | wpabuf_put_u8(buf, conf); |
88 | 0 | if (pps_mo_id >= 0) |
89 | 0 | wpabuf_put_le16(buf, pps_mo_id); |
90 | 0 | } |
91 | | |
92 | | |
93 | | void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf, |
94 | | const struct wpa_ssid *ssid) |
95 | 0 | { |
96 | 0 | if (!ssid->roaming_consortium_selection || |
97 | 0 | !ssid->roaming_consortium_selection_len) |
98 | 0 | return; |
99 | | |
100 | 0 | wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); |
101 | 0 | wpabuf_put_u8(buf, 4 + ssid->roaming_consortium_selection_len); |
102 | 0 | wpabuf_put_be24(buf, OUI_WFA); |
103 | 0 | wpabuf_put_u8(buf, HS20_ROAMING_CONS_SEL_OUI_TYPE); |
104 | 0 | wpabuf_put_data(buf, ssid->roaming_consortium_selection, |
105 | 0 | ssid->roaming_consortium_selection_len); |
106 | 0 | } |
107 | | |
108 | | |
109 | | int get_hs20_version(struct wpa_bss *bss) |
110 | 0 | { |
111 | 0 | const u8 *ie; |
112 | |
|
113 | 0 | if (!bss) |
114 | 0 | return 0; |
115 | | |
116 | 0 | ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE); |
117 | 0 | if (!ie || ie[1] < 5) |
118 | 0 | return 0; |
119 | | |
120 | 0 | return ((ie[6] >> 4) & 0x0f) + 1; |
121 | 0 | } |
122 | | |
123 | | |
124 | | int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, |
125 | | struct wpa_bss *bss) |
126 | 0 | { |
127 | 0 | if (!wpa_s->conf->hs20 || !ssid) |
128 | 0 | return 0; |
129 | | |
130 | 0 | if (ssid->parent_cred) |
131 | 0 | return 1; |
132 | | |
133 | 0 | if (bss && !wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) |
134 | 0 | return 0; |
135 | | |
136 | | /* |
137 | | * This may catch some non-Hotspot 2.0 cases, but it is safer to do that |
138 | | * than cause Hotspot 2.0 connections without indication element getting |
139 | | * added. Non-Hotspot 2.0 APs should ignore the unknown vendor element. |
140 | | */ |
141 | | |
142 | 0 | if (!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X)) |
143 | 0 | return 0; |
144 | 0 | if (!(ssid->pairwise_cipher & WPA_CIPHER_CCMP)) |
145 | 0 | return 0; |
146 | 0 | if (ssid->proto != WPA_PROTO_RSN) |
147 | 0 | return 0; |
148 | | |
149 | 0 | return 1; |
150 | 0 | } |
151 | | |
152 | | |
153 | | int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) |
154 | 0 | { |
155 | 0 | struct wpa_cred *cred; |
156 | |
|
157 | 0 | if (ssid == NULL) |
158 | 0 | return 0; |
159 | | |
160 | 0 | if (ssid->update_identifier) |
161 | 0 | return ssid->update_identifier; |
162 | | |
163 | 0 | if (ssid->parent_cred == NULL) |
164 | 0 | return 0; |
165 | | |
166 | 0 | for (cred = wpa_s->conf->cred; cred; cred = cred->next) { |
167 | 0 | if (ssid->parent_cred == cred) |
168 | 0 | return cred->update_identifier; |
169 | 0 | } |
170 | | |
171 | 0 | return 0; |
172 | 0 | } |
173 | | |
174 | | |
175 | | void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, |
176 | | struct wpabuf *buf) |
177 | 0 | { |
178 | 0 | u8 *len_pos; |
179 | |
|
180 | 0 | if (buf == NULL) |
181 | 0 | return; |
182 | | |
183 | 0 | len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); |
184 | 0 | wpabuf_put_be24(buf, OUI_WFA); |
185 | 0 | wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); |
186 | 0 | if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) { |
187 | 0 | wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); |
188 | 0 | wpabuf_put_u8(buf, 0); /* Reserved */ |
189 | 0 | if (payload) |
190 | 0 | wpabuf_put_data(buf, payload, payload_len); |
191 | 0 | } else { |
192 | 0 | u8 i; |
193 | 0 | wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST); |
194 | 0 | wpabuf_put_u8(buf, 0); /* Reserved */ |
195 | 0 | for (i = 0; i < 32; i++) { |
196 | 0 | if (stypes & BIT(i)) |
197 | 0 | wpabuf_put_u8(buf, i); |
198 | 0 | } |
199 | 0 | } |
200 | 0 | gas_anqp_set_element_len(buf, len_pos); |
201 | |
|
202 | 0 | gas_anqp_set_len(buf); |
203 | 0 | } |
204 | | |
205 | | |
206 | | static struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, |
207 | | size_t payload_len) |
208 | 0 | { |
209 | 0 | struct wpabuf *buf; |
210 | |
|
211 | 0 | buf = gas_anqp_build_initial_req(0, 100 + payload_len); |
212 | 0 | if (buf == NULL) |
213 | 0 | return NULL; |
214 | | |
215 | 0 | hs20_put_anqp_req(stypes, payload, payload_len, buf); |
216 | |
|
217 | 0 | return buf; |
218 | 0 | } |
219 | | |
220 | | |
221 | | int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, |
222 | | const u8 *payload, size_t payload_len) |
223 | 0 | { |
224 | 0 | struct wpabuf *buf; |
225 | 0 | int ret = 0; |
226 | 0 | int freq; |
227 | 0 | struct wpa_bss *bss; |
228 | 0 | int res; |
229 | |
|
230 | 0 | bss = wpa_bss_get_bssid(wpa_s, dst); |
231 | 0 | if (!bss) { |
232 | 0 | wpa_printf(MSG_WARNING, |
233 | 0 | "ANQP: Cannot send query to unknown BSS " |
234 | 0 | MACSTR, MAC2STR(dst)); |
235 | 0 | return -1; |
236 | 0 | } |
237 | | |
238 | 0 | wpa_bss_anqp_unshare_alloc(bss); |
239 | 0 | freq = bss->freq; |
240 | |
|
241 | 0 | wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for " |
242 | 0 | "subtypes 0x%x", MAC2STR(dst), stypes); |
243 | |
|
244 | 0 | buf = hs20_build_anqp_req(stypes, payload, payload_len); |
245 | 0 | if (buf == NULL) |
246 | 0 | return -1; |
247 | | |
248 | 0 | res = gas_query_req(wpa_s->gas, dst, freq, 0, 0, buf, anqp_resp_cb, |
249 | 0 | wpa_s); |
250 | 0 | if (res < 0) { |
251 | 0 | wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); |
252 | 0 | wpabuf_free(buf); |
253 | 0 | return -1; |
254 | 0 | } else |
255 | 0 | wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " |
256 | 0 | "%u", res); |
257 | | |
258 | 0 | return ret; |
259 | 0 | } |
260 | | |
261 | | |
262 | | void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, |
263 | | struct wpa_bss *bss, const u8 *sa, |
264 | | const u8 *data, size_t slen, u8 dialog_token) |
265 | 0 | { |
266 | 0 | const u8 *pos = data; |
267 | 0 | u8 subtype; |
268 | 0 | struct wpa_bss_anqp *anqp = NULL; |
269 | |
|
270 | 0 | if (slen < 2) |
271 | 0 | return; |
272 | | |
273 | 0 | if (bss) |
274 | 0 | anqp = bss->anqp; |
275 | |
|
276 | 0 | subtype = *pos++; |
277 | 0 | slen--; |
278 | |
|
279 | 0 | pos++; /* Reserved */ |
280 | 0 | slen--; |
281 | |
|
282 | 0 | switch (subtype) { |
283 | 0 | case HS20_STYPE_CAPABILITY_LIST: |
284 | 0 | wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR |
285 | 0 | " HS Capability List", MAC2STR(sa)); |
286 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen); |
287 | 0 | if (anqp) { |
288 | 0 | wpabuf_free(anqp->hs20_capability_list); |
289 | 0 | anqp->hs20_capability_list = |
290 | 0 | wpabuf_alloc_copy(pos, slen); |
291 | 0 | } |
292 | 0 | break; |
293 | 0 | case HS20_STYPE_OPERATOR_FRIENDLY_NAME: |
294 | 0 | wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR |
295 | 0 | " Operator Friendly Name", MAC2STR(sa)); |
296 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen); |
297 | 0 | if (anqp) { |
298 | 0 | wpabuf_free(anqp->hs20_operator_friendly_name); |
299 | 0 | anqp->hs20_operator_friendly_name = |
300 | 0 | wpabuf_alloc_copy(pos, slen); |
301 | 0 | } |
302 | 0 | break; |
303 | 0 | case HS20_STYPE_WAN_METRICS: |
304 | 0 | wpa_hexdump(MSG_DEBUG, "WAN Metrics", pos, slen); |
305 | 0 | if (slen < 13) { |
306 | 0 | wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short WAN " |
307 | 0 | "Metrics value from " MACSTR, MAC2STR(sa)); |
308 | 0 | break; |
309 | 0 | } |
310 | 0 | wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR |
311 | 0 | " WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa), |
312 | 0 | pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5), |
313 | 0 | pos[9], pos[10], WPA_GET_LE16(pos + 11)); |
314 | 0 | if (anqp) { |
315 | 0 | wpabuf_free(anqp->hs20_wan_metrics); |
316 | 0 | anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen); |
317 | 0 | } |
318 | 0 | break; |
319 | 0 | case HS20_STYPE_CONNECTION_CAPABILITY: |
320 | 0 | wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR |
321 | 0 | " Connection Capability", MAC2STR(sa)); |
322 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen); |
323 | 0 | if (anqp) { |
324 | 0 | wpabuf_free(anqp->hs20_connection_capability); |
325 | 0 | anqp->hs20_connection_capability = |
326 | 0 | wpabuf_alloc_copy(pos, slen); |
327 | 0 | } |
328 | 0 | break; |
329 | 0 | case HS20_STYPE_OPERATING_CLASS: |
330 | 0 | wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR |
331 | 0 | " Operating Class", MAC2STR(sa)); |
332 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen); |
333 | 0 | if (anqp) { |
334 | 0 | wpabuf_free(anqp->hs20_operating_class); |
335 | 0 | anqp->hs20_operating_class = |
336 | 0 | wpabuf_alloc_copy(pos, slen); |
337 | 0 | } |
338 | 0 | break; |
339 | 0 | default: |
340 | 0 | wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype); |
341 | 0 | break; |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | |
346 | | void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, |
347 | | u16 reauth_delay, const char *url) |
348 | 4.47k | { |
349 | 4.47k | if (!wpa_sm_pmf_enabled(wpa_s->wpa)) { |
350 | 4.47k | wpa_printf(MSG_DEBUG, "HS 2.0: Ignore deauthentication imminent notice since PMF was not enabled"); |
351 | 4.47k | return; |
352 | 4.47k | } |
353 | | |
354 | 0 | wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s", |
355 | 0 | code, reauth_delay, url); |
356 | |
|
357 | 0 | if (code == HS20_DEAUTH_REASON_CODE_BSS) { |
358 | 0 | wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to ignore list"); |
359 | 0 | wpa_bssid_ignore_add(wpa_s, wpa_s->bssid); |
360 | | /* TODO: For now, disable full ESS since some drivers may not |
361 | | * support disabling per BSS. */ |
362 | 0 | if (wpa_s->current_ssid) { |
363 | 0 | struct os_reltime now; |
364 | 0 | os_get_reltime(&now); |
365 | 0 | if (now.sec + reauth_delay <= |
366 | 0 | wpa_s->current_ssid->disabled_until.sec) |
367 | 0 | return; |
368 | 0 | wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds (BSS)", |
369 | 0 | reauth_delay); |
370 | 0 | wpa_s->current_ssid->disabled_until.sec = |
371 | 0 | now.sec + reauth_delay; |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | 0 | if (code == HS20_DEAUTH_REASON_CODE_ESS && wpa_s->current_ssid) { |
376 | 0 | struct os_reltime now; |
377 | 0 | os_get_reltime(&now); |
378 | 0 | if (now.sec + reauth_delay <= |
379 | 0 | wpa_s->current_ssid->disabled_until.sec) |
380 | 0 | return; |
381 | 0 | wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds", |
382 | 0 | reauth_delay); |
383 | 0 | wpa_s->current_ssid->disabled_until.sec = |
384 | 0 | now.sec + reauth_delay; |
385 | 0 | } |
386 | 0 | } |
387 | | |
388 | | |
389 | | void hs20_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url) |
390 | 763 | { |
391 | 763 | if (!wpa_sm_pmf_enabled(wpa_s->wpa)) { |
392 | 763 | wpa_printf(MSG_DEBUG, |
393 | 763 | "HS 2.0: Ignore Terms and Conditions Acceptance since PMF was not enabled"); |
394 | 763 | return; |
395 | 763 | } |
396 | | |
397 | 0 | wpas_notify_hs20_t_c_acceptance(wpa_s, url); |
398 | 0 | } |