]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * wpa_supplicant - WPA/RSN IE and KDE processing | |
c22bb5bb | 3 | * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> |
6fc6879b | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
12 | #include "wpa.h" | |
13 | #include "pmksa_cache.h" | |
90973fb2 | 14 | #include "common/ieee802_11_defs.h" |
6fc6879b JM |
15 | #include "wpa_i.h" |
16 | #include "wpa_ie.h" | |
17 | ||
18 | ||
6fc6879b JM |
19 | /** |
20 | * wpa_parse_wpa_ie - Parse WPA/RSN IE | |
21 | * @wpa_ie: Pointer to WPA or RSN IE | |
22 | * @wpa_ie_len: Length of the WPA/RSN IE | |
23 | * @data: Pointer to data area for parsing results | |
24 | * Returns: 0 on success, -1 on failure | |
25 | * | |
26 | * Parse the contents of WPA or RSN IE and write the parsed data into data. | |
27 | */ | |
28 | int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, | |
29 | struct wpa_ie_data *data) | |
30 | { | |
31 | if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) | |
32 | return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); | |
0f8385e6 BG |
33 | if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC && |
34 | wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE) | |
35 | return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); | |
6fc6879b JM |
36 | else |
37 | return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); | |
38 | } | |
39 | ||
40 | ||
41 | static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, | |
42 | int pairwise_cipher, int group_cipher, | |
43 | int key_mgmt) | |
44 | { | |
45 | u8 *pos; | |
46 | struct wpa_ie_hdr *hdr; | |
c3550295 | 47 | u32 suite; |
6fc6879b JM |
48 | |
49 | if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + | |
50 | 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) | |
51 | return -1; | |
52 | ||
53 | hdr = (struct wpa_ie_hdr *) wpa_ie; | |
54 | hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; | |
55 | RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); | |
56 | WPA_PUT_LE16(hdr->version, WPA_VERSION); | |
57 | pos = (u8 *) (hdr + 1); | |
58 | ||
c3550295 JM |
59 | suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher); |
60 | if (suite == 0) { | |
6fc6879b JM |
61 | wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", |
62 | group_cipher); | |
63 | return -1; | |
64 | } | |
c3550295 | 65 | RSN_SELECTOR_PUT(pos, suite); |
6fc6879b JM |
66 | pos += WPA_SELECTOR_LEN; |
67 | ||
68 | *pos++ = 1; | |
69 | *pos++ = 0; | |
c3550295 JM |
70 | suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher); |
71 | if (suite == 0 || | |
72 | (!wpa_cipher_valid_pairwise(pairwise_cipher) && | |
73 | pairwise_cipher != WPA_CIPHER_NONE)) { | |
6fc6879b JM |
74 | wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", |
75 | pairwise_cipher); | |
76 | return -1; | |
77 | } | |
c3550295 | 78 | RSN_SELECTOR_PUT(pos, suite); |
6fc6879b JM |
79 | pos += WPA_SELECTOR_LEN; |
80 | ||
81 | *pos++ = 1; | |
82 | *pos++ = 0; | |
83 | if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { | |
84 | RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); | |
85 | } else if (key_mgmt == WPA_KEY_MGMT_PSK) { | |
86 | RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); | |
87 | } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { | |
88 | RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE); | |
369c8d7b JM |
89 | } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { |
90 | RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM); | |
6fc6879b JM |
91 | } else { |
92 | wpa_printf(MSG_WARNING, "Invalid key management type (%d).", | |
93 | key_mgmt); | |
94 | return -1; | |
95 | } | |
96 | pos += WPA_SELECTOR_LEN; | |
97 | ||
98 | /* WPA Capabilities; use defaults, so no need to include it */ | |
99 | ||
100 | hdr->len = (pos - wpa_ie) - 2; | |
101 | ||
102 | WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); | |
103 | ||
104 | return pos - wpa_ie; | |
105 | } | |
106 | ||
107 | ||
108 | static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, | |
109 | int pairwise_cipher, int group_cipher, | |
110 | int key_mgmt, int mgmt_group_cipher, | |
111 | struct wpa_sm *sm) | |
112 | { | |
6fc6879b JM |
113 | u8 *pos; |
114 | struct rsn_ie_hdr *hdr; | |
115 | u16 capab; | |
c3550295 | 116 | u32 suite; |
6fc6879b JM |
117 | |
118 | if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + | |
119 | 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + | |
120 | (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) { | |
121 | wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)", | |
122 | (unsigned long) rsn_ie_len); | |
123 | return -1; | |
124 | } | |
125 | ||
126 | hdr = (struct rsn_ie_hdr *) rsn_ie; | |
127 | hdr->elem_id = WLAN_EID_RSN; | |
128 | WPA_PUT_LE16(hdr->version, RSN_VERSION); | |
129 | pos = (u8 *) (hdr + 1); | |
130 | ||
c3550295 JM |
131 | suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); |
132 | if (suite == 0) { | |
6fc6879b JM |
133 | wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", |
134 | group_cipher); | |
135 | return -1; | |
136 | } | |
c3550295 | 137 | RSN_SELECTOR_PUT(pos, suite); |
6fc6879b JM |
138 | pos += RSN_SELECTOR_LEN; |
139 | ||
140 | *pos++ = 1; | |
141 | *pos++ = 0; | |
c3550295 JM |
142 | suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); |
143 | if (suite == 0 || | |
144 | (!wpa_cipher_valid_pairwise(pairwise_cipher) && | |
145 | pairwise_cipher != WPA_CIPHER_NONE)) { | |
6fc6879b JM |
146 | wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", |
147 | pairwise_cipher); | |
148 | return -1; | |
149 | } | |
c3550295 | 150 | RSN_SELECTOR_PUT(pos, suite); |
6fc6879b JM |
151 | pos += RSN_SELECTOR_LEN; |
152 | ||
153 | *pos++ = 1; | |
154 | *pos++ = 0; | |
155 | if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { | |
156 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); | |
157 | } else if (key_mgmt == WPA_KEY_MGMT_PSK) { | |
158 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); | |
369c8d7b JM |
159 | } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { |
160 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM); | |
6fc6879b JM |
161 | #ifdef CONFIG_IEEE80211R |
162 | } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { | |
163 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); | |
c22bb5bb JM |
164 | #ifdef CONFIG_SHA384 |
165 | } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { | |
166 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); | |
167 | #endif /* CONFIG_SHA384 */ | |
6fc6879b JM |
168 | } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { |
169 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); | |
170 | #endif /* CONFIG_IEEE80211R */ | |
56586197 JM |
171 | } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) { |
172 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); | |
173 | } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { | |
174 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); | |
c10347f2 JM |
175 | #ifdef CONFIG_SAE |
176 | } else if (key_mgmt == WPA_KEY_MGMT_SAE) { | |
177 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); | |
178 | } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { | |
179 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); | |
180 | #endif /* CONFIG_SAE */ | |
5e3b5197 JM |
181 | } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { |
182 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192); | |
666497c8 JM |
183 | } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) { |
184 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); | |
94318a0d JM |
185 | #ifdef CONFIG_FILS |
186 | } else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { | |
187 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); | |
188 | } else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { | |
189 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); | |
190 | #ifdef CONFIG_IEEE80211R | |
191 | } else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { | |
192 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); | |
193 | } else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { | |
194 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); | |
195 | #endif /* CONFIG_IEEE80211R */ | |
196 | #endif /* CONFIG_FILS */ | |
a1ea1b45 JM |
197 | #ifdef CONFIG_OWE |
198 | } else if (key_mgmt & WPA_KEY_MGMT_OWE) { | |
199 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE); | |
200 | #endif /* CONFIG_OWE */ | |
567da5bb JM |
201 | #ifdef CONFIG_DPP |
202 | } else if (key_mgmt & WPA_KEY_MGMT_DPP) { | |
203 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP); | |
204 | #endif /* CONFIG_DPP */ | |
f5a60216 JM |
205 | #ifdef CONFIG_HS20 |
206 | } else if (key_mgmt & WPA_KEY_MGMT_OSEN) { | |
207 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); | |
208 | #endif /* CONFIG_HS20 */ | |
6fc6879b JM |
209 | } else { |
210 | wpa_printf(MSG_WARNING, "Invalid key management type (%d).", | |
211 | key_mgmt); | |
212 | return -1; | |
213 | } | |
214 | pos += RSN_SELECTOR_LEN; | |
215 | ||
216 | /* RSN Capabilities */ | |
217 | capab = 0; | |
e820cf95 | 218 | if (sm->mfp) |
0b60b0aa | 219 | capab |= WPA_CAPABILITY_MFPC; |
e820cf95 JM |
220 | if (sm->mfp == 2) |
221 | capab |= WPA_CAPABILITY_MFPR; | |
55c12f5d MV |
222 | if (sm->ocv) |
223 | capab |= WPA_CAPABILITY_OCVC; | |
b17b7a8e AW |
224 | if (sm->ext_key_id) |
225 | capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST; | |
6fc6879b JM |
226 | WPA_PUT_LE16(pos, capab); |
227 | pos += 2; | |
228 | ||
229 | if (sm->cur_pmksa) { | |
230 | /* PMKID Count (2 octets, little endian) */ | |
231 | *pos++ = 1; | |
232 | *pos++ = 0; | |
233 | /* PMKID */ | |
234 | os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN); | |
235 | pos += PMKID_LEN; | |
236 | } | |
237 | ||
8dd9f9cd | 238 | if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) { |
6fc6879b JM |
239 | if (!sm->cur_pmksa) { |
240 | /* PMKID Count */ | |
241 | WPA_PUT_LE16(pos, 0); | |
242 | pos += 2; | |
243 | } | |
244 | ||
245 | /* Management Group Cipher Suite */ | |
8dd9f9cd JM |
246 | RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, |
247 | mgmt_group_cipher)); | |
6fc6879b JM |
248 | pos += RSN_SELECTOR_LEN; |
249 | } | |
6fc6879b JM |
250 | |
251 | hdr->len = (pos - rsn_ie) - 2; | |
252 | ||
253 | WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); | |
254 | ||
255 | return pos - rsn_ie; | |
6fc6879b JM |
256 | } |
257 | ||
258 | ||
df0f01d9 JM |
259 | #ifdef CONFIG_HS20 |
260 | static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len, | |
261 | int pairwise_cipher, int group_cipher, | |
262 | int key_mgmt) | |
263 | { | |
264 | u8 *pos, *len; | |
265 | u32 suite; | |
266 | ||
267 | if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN + | |
268 | 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN) | |
269 | return -1; | |
270 | ||
271 | pos = wpa_ie; | |
272 | *pos++ = WLAN_EID_VENDOR_SPECIFIC; | |
273 | len = pos++; /* to be filled */ | |
274 | WPA_PUT_BE24(pos, OUI_WFA); | |
275 | pos += 3; | |
276 | *pos++ = HS20_OSEN_OUI_TYPE; | |
277 | ||
278 | /* Group Data Cipher Suite */ | |
279 | suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); | |
280 | if (suite == 0) { | |
281 | wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", | |
282 | group_cipher); | |
283 | return -1; | |
284 | } | |
285 | RSN_SELECTOR_PUT(pos, suite); | |
286 | pos += RSN_SELECTOR_LEN; | |
287 | ||
288 | /* Pairwise Cipher Suite Count and List */ | |
289 | WPA_PUT_LE16(pos, 1); | |
290 | pos += 2; | |
291 | suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); | |
292 | if (suite == 0 || | |
293 | (!wpa_cipher_valid_pairwise(pairwise_cipher) && | |
294 | pairwise_cipher != WPA_CIPHER_NONE)) { | |
295 | wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", | |
296 | pairwise_cipher); | |
297 | return -1; | |
298 | } | |
299 | RSN_SELECTOR_PUT(pos, suite); | |
300 | pos += RSN_SELECTOR_LEN; | |
301 | ||
302 | /* AKM Suite Count and List */ | |
303 | WPA_PUT_LE16(pos, 1); | |
304 | pos += 2; | |
305 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); | |
306 | pos += RSN_SELECTOR_LEN; | |
307 | ||
308 | *len = pos - len - 1; | |
309 | ||
310 | WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); | |
311 | ||
312 | return pos - wpa_ie; | |
313 | } | |
314 | #endif /* CONFIG_HS20 */ | |
315 | ||
316 | ||
6fc6879b JM |
317 | /** |
318 | * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy | |
319 | * @sm: Pointer to WPA state machine data from wpa_sm_init() | |
320 | * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE | |
321 | * @wpa_ie_len: Maximum length of the generated WPA/RSN IE | |
322 | * Returns: Length of the generated WPA/RSN IE or -1 on failure | |
323 | */ | |
324 | int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) | |
325 | { | |
326 | if (sm->proto == WPA_PROTO_RSN) | |
327 | return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len, | |
328 | sm->pairwise_cipher, | |
329 | sm->group_cipher, | |
330 | sm->key_mgmt, sm->mgmt_group_cipher, | |
331 | sm); | |
df0f01d9 JM |
332 | #ifdef CONFIG_HS20 |
333 | else if (sm->proto == WPA_PROTO_OSEN) | |
334 | return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len, | |
335 | sm->pairwise_cipher, | |
336 | sm->group_cipher, | |
337 | sm->key_mgmt); | |
338 | #endif /* CONFIG_HS20 */ | |
6fc6879b JM |
339 | else |
340 | return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, | |
341 | sm->pairwise_cipher, | |
342 | sm->group_cipher, | |
343 | sm->key_mgmt); | |
344 | } | |
345 | ||
346 | ||
6d6c8877 JM |
347 | int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len) |
348 | { | |
349 | u8 *pos = rsnxe; | |
350 | ||
351 | if (!wpa_key_mgmt_sae(sm->key_mgmt)) | |
352 | return 0; /* SAE not in use */ | |
353 | if (sm->sae_pwe != 1 && sm->sae_pwe != 2) | |
354 | return 0; /* no supported extended RSN capabilities */ | |
355 | ||
356 | if (rsnxe_len < 3) | |
357 | return -1; | |
358 | ||
359 | *pos++ = WLAN_EID_RSNX; | |
360 | *pos++ = 1; | |
361 | /* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is | |
362 | * used for now */ | |
363 | *pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E); | |
364 | ||
365 | return pos - rsnxe; | |
366 | } |