1 /*
2 * IEEE 802.11 Common routines
3 * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
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 "defs.h"
13 #include "ieee802_11_defs.h"
14 #include "ieee802_11_common.h"
15 #include "common/wpa_supplicant_i.h"
16
17 /**
18 * get_ie - Fetch a specified information element from IEs buffer
19 * @ies: Information elements buffer
20 * @len: Information elements buffer length
21 * @eid: Information element identifier (WLAN_EID_*)
22 * Returns: Pointer to the information element (id field) or %NULL if not found
23 *
24 * This function returns the first matching information element in the IEs
25 * buffer or %NULL in case the element is not found.
26 */
get_ie(const u8 * ies,size_t len,u8 eid)27 const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
28 {
29 const struct element *elem;
30
31 if (!ies)
32 return NULL;
33
34 for_each_element_id(elem, eid, ies, len)
35 return &elem->id;
36
37 return NULL;
38 }
39
ieee802_11_ie_count(const u8 * ies,size_t ies_len)40 int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
41 {
42 const struct element *elem;
43 int count = 0;
44
45 if (ies == NULL)
46 return 0;
47
48 for_each_element(elem, ies, ies_len)
49 count++;
50
51 return count;
52 }
53
54
55
get_vendor_ie(const u8 * ies,size_t len,u32 vendor_type)56 const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
57 {
58 const struct element *elem;
59
60 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
61 if (elem->datalen >= 4 &&
62 vendor_type == WPA_GET_BE32(elem->data))
63 return &elem->id;
64 }
65
66 return NULL;
67 }
68
69
mbo_add_ie(u8 * buf,size_t len,const u8 * attr,size_t attr_len)70 size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
71 {
72 /*
73 * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
74 * OUI (3), OUI type (1).
75 */
76 if (len < 6 + attr_len) {
77 wpa_printf(MSG_DEBUG,
78 "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
79 len, attr_len);
80 return 0;
81 }
82
83 *buf++ = WLAN_EID_VENDOR_SPECIFIC;
84 *buf++ = attr_len + 4;
85 WPA_PUT_BE24(buf, OUI_WFA);
86 buf += 3;
87 *buf++ = MBO_OUI_TYPE;
88 os_memcpy(buf, attr, attr_len);
89
90 return 6 + attr_len;
91 }
92
ieee802_11_parse_candidate_list(const char * pos,u8 * nei_rep,size_t nei_rep_len)93 int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
94 size_t nei_rep_len)
95 {
96 u8 *nei_pos = nei_rep;
97 const char *end;
98
99 /*
100 * BSS Transition Candidate List Entries - Neighbor Report elements
101 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
102 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
103 */
104 while (pos) {
105 u8 *nei_start;
106 long int val;
107 char *endptr, *tmp;
108
109 pos = os_strstr(pos, " neighbor=");
110 if (!pos)
111 break;
112 if (nei_pos + 15 > nei_rep + nei_rep_len) {
113 wpa_printf(MSG_DEBUG,
114 "Not enough room for additional neighbor");
115 return -1;
116 }
117 pos += 10;
118
119 nei_start = nei_pos;
120 *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
121 nei_pos++; /* length to be filled in */
122
123 if (hwaddr_aton2(pos, nei_pos) < 0) {
124 wpa_printf(MSG_DEBUG, "Invalid BSSID");
125 return -1;
126 }
127 nei_pos += ETH_ALEN;
128 pos += 17;
129 if (*pos != ',') {
130 wpa_printf(MSG_DEBUG, "Missing BSSID Information");
131 return -1;
132 }
133 pos++;
134
135 val = strtol(pos, &endptr, 0);
136 WPA_PUT_LE32(nei_pos, val);
137 nei_pos += 4;
138 if (*endptr != ',') {
139 wpa_printf(MSG_DEBUG, "Missing Operating Class");
140 return -1;
141 }
142 pos = endptr + 1;
143
144 *nei_pos++ = atoi(pos); /* Operating Class */
145 pos = os_strchr(pos, ',');
146 if (pos == NULL) {
147 wpa_printf(MSG_DEBUG, "Missing Channel Number");
148 return -1;
149 }
150 pos++;
151
152 *nei_pos++ = atoi(pos); /* Channel Number */
153 pos = os_strchr(pos, ',');
154 if (pos == NULL) {
155 wpa_printf(MSG_DEBUG, "Missing PHY Type");
156 return -1;
157 }
158 pos++;
159
160 *nei_pos++ = atoi(pos); /* PHY Type */
161 end = os_strchr(pos, ' ');
162 tmp = os_strchr(pos, ',');
163 if (tmp && (!end || tmp < end)) {
164 /* Optional Subelements (hexdump) */
165 size_t len;
166
167 pos = tmp + 1;
168 end = os_strchr(pos, ' ');
169 if (end)
170 len = end - pos;
171 else
172 len = os_strlen(pos);
173 if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
174 wpa_printf(MSG_DEBUG,
175 "Not enough room for neighbor subelements");
176 return -1;
177 }
178 if (len & 0x01 ||
179 hexstr2bin(pos, nei_pos, len / 2) < 0) {
180 wpa_printf(MSG_DEBUG,
181 "Invalid neighbor subelement info");
182 return -1;
183 }
184 nei_pos += len / 2;
185 pos = end;
186 }
187
188 nei_start[1] = nei_pos - nei_start - 2;
189 }
190
191 return nei_pos - nei_rep;
192 }
193
194 #ifdef CONFIG_SAE_PK
ieee802_11_parse_vendor_specific(struct wpa_supplicant * wpa_s,const struct element * elem,const u8 * pos)195 static int ieee802_11_parse_vendor_specific(struct wpa_supplicant *wpa_s, const struct element *elem, const u8* pos)
196 {
197 u32 oui;
198 oui = WPA_GET_BE24(pos);
199 switch (oui) {
200 case OUI_WFA:
201 switch (pos[3]) {
202 case SAE_PK_OUI_TYPE:
203 wpa_s->sae_pk_elems.sae_pk_len = elem->datalen - 4;
204 wpa_s->sae_pk_elems.sae_pk = (u8*)os_zalloc(sizeof(u8)*(elem->datalen-4));
205 os_memcpy(wpa_s->sae_pk_elems.sae_pk, pos+4, elem->datalen-4);
206 break;
207 default:
208 wpa_printf(MSG_EXCESSIVE, "Unknown WFA "
209 "information element ignored "
210 "(type=%d len=%lu)",
211 pos[3], (unsigned long) elem->datalen);
212 return -1;
213 }
214 break;
215 default:
216 wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
217 "information element ignored (vendor OUI "
218 "%02x:%02x:%02x len=%lu)",
219 pos[0], pos[1], pos[2], (unsigned long) elem->datalen);
220 return -1;
221 }
222
223 return 0;
224 }
225
ieee802_11_parse_extension(struct wpa_supplicant * wpa_s,const struct element * elem,const u8 * pos)226 static int ieee802_11_parse_extension(struct wpa_supplicant *wpa_s, const struct element *elem, const u8* pos){
227 // do not consider extension_id element len in datalen
228 if (elem->datalen < 1) {
229 wpa_printf(MSG_DEBUG,
230 "short information element (Ext)");
231 return -1;
232 }
233 u8 ext_id;
234 ext_id = *pos++;
235 switch (ext_id) {
236 case WLAN_EID_EXT_FILS_KEY_CONFIRM:
237 wpa_s->sae_pk_elems.fils_key_confirm_len = elem->datalen - 1;
238 wpa_s->sae_pk_elems.fils_key_confirm = (u8*)os_zalloc(sizeof(u8)*(elem->datalen - 1));
239 os_memcpy(wpa_s->sae_pk_elems.fils_key_confirm, pos, elem->datalen - 1);
240 break;
241 case WLAN_EID_EXT_FILS_PUBLIC_KEY:
242 wpa_s->sae_pk_elems.fils_pk_len = elem->datalen - 1;
243 wpa_s->sae_pk_elems.fils_pk = (u8*)os_zalloc(sizeof(u8)*(elem->datalen - 1));
244 os_memcpy(wpa_s->sae_pk_elems.fils_pk, pos, elem->datalen - 1);
245 break;
246 default:
247 wpa_printf(MSG_EXCESSIVE,
248 "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)",
249 ext_id, (unsigned int) elem->datalen-1);
250 return -1;
251 }
252
253 return 0;
254 }
255 #endif /* CONFIG_SAE_PK */
256
257 /**
258 * ieee802_11_parse_elems - Parse information elements in management frames
259 * @start: Pointer to the start of IEs
260 * @len: Length of IE buffer in octets
261 * @elems: Data structure for parsed elements
262 * @show_errors: Whether to show parsing errors in debug log
263 * Returns: Parsing result
264 */
ieee802_11_parse_elems(struct wpa_supplicant * wpa_s,const u8 * start,size_t len)265 int ieee802_11_parse_elems(struct wpa_supplicant *wpa_s, const u8 *start, size_t len)
266 {
267 #if defined(CONFIG_RRM) || defined(CONFIG_SAE_PK)
268 const struct element *elem;
269 u8 unknown = 0;
270
271 if (!start)
272 return 0;
273
274 for_each_element(elem, start, len) {
275 u8 id = elem->id;
276 const u8 *pos = elem->data;
277 switch (id) {
278 #ifdef CONFIG_RRM
279 case WLAN_EID_RRM_ENABLED_CAPABILITIES:
280 os_memcpy(wpa_s->rrm_ie, pos, 5);
281 wpa_s->rrm.rrm_used = true;
282 break;
283 #endif
284 #ifdef CONFIG_SAE_PK
285 case WLAN_EID_EXTENSION:
286 if(ieee802_11_parse_extension(wpa_s, elem, pos) != 0){
287 unknown++;
288 }
289 break;
290 case WLAN_EID_VENDOR_SPECIFIC:
291 if(ieee802_11_parse_vendor_specific(wpa_s, elem, pos) != 0){
292 unknown++;
293 }
294 break;
295 #endif /*CONFIG_SAE_PK*/
296 #ifdef CONFIG_RRM
297 case WLAN_EID_EXT_CAPAB:
298 /* extended caps can go beyond 8 octacts but we aren't using them now */
299 os_memcpy(wpa_s->extend_caps, pos, 5);
300 break;
301 #endif
302 default:
303 break;
304
305 }
306 }
307 if (unknown)
308 return -1;
309
310 #endif /* defined(CONFIG_RRM) || defined(CONFIG_SAE_PK) */
311 return 0;
312 }
313
ieee802_11_vendor_ie_concat(const u8 * ies,size_t ies_len,u32 oui_type)314 struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
315 u32 oui_type)
316 {
317 struct wpabuf *buf;
318 const struct element *elem, *found = NULL;
319
320 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
321 if (elem->datalen >= 4 &&
322 WPA_GET_BE32(elem->data) == oui_type) {
323 found = elem;
324 break;
325 }
326 }
327
328 if (!found)
329 return NULL; /* No specified vendor IE found */
330
331 buf = wpabuf_alloc(ies_len);
332 if (buf == NULL)
333 return NULL;
334
335 /*
336 * There may be multiple vendor IEs in the message, so need to
337 * concatenate their data fields.
338 */
339 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
340 if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type)
341 wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4);
342 }
343
344 return buf;
345 }
346
ieee802_11_ext_capab(const u8 * ie,unsigned int capab)347 int ieee802_11_ext_capab(const u8 *ie, unsigned int capab)
348 {
349 if (!ie || ie[1] <= capab / 8)
350 return 0;
351 return !!(ie[2 + capab / 8] & BIT(capab % 8));
352 }
353
get_operating_class(u8 chan,int sec_channel)354 u8 get_operating_class(u8 chan, int sec_channel)
355 {
356 u8 op_class;
357
358 if (chan < 1 || chan > 14)
359 return 0;
360 if (sec_channel == 1)
361 op_class = 83;
362 else if (sec_channel == -1)
363 op_class = 84;
364 else
365 op_class = 81;
366
367 return op_class;
368 }
369