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