]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * hostapd - PeerKey for Direct Link Setup (DLS) | |
56586197 | 3 | * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> |
6fc6879b JM |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * Alternatively, this software may be distributed under the terms of BSD | |
10 | * license. | |
11 | * | |
12 | * See README and COPYING for more details. | |
13 | */ | |
14 | ||
15 | #include "includes.h" | |
16 | ||
17 | #include "common.h" | |
18 | #include "eloop.h" | |
19 | #include "sha1.h" | |
56586197 | 20 | #include "sha256.h" |
6fc6879b | 21 | #include "wpa.h" |
6fc6879b JM |
22 | #include "wpa_auth_i.h" |
23 | #include "wpa_auth_ie.h" | |
24 | ||
25 | #ifdef CONFIG_PEERKEY | |
26 | ||
27 | static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) | |
28 | { | |
29 | #if 0 | |
30 | struct wpa_authenticator *wpa_auth = eloop_ctx; | |
31 | struct wpa_stsl_negotiation *neg = timeout_ctx; | |
32 | #endif | |
33 | ||
34 | /* TODO: ? */ | |
35 | } | |
36 | ||
37 | ||
38 | struct wpa_stsl_search { | |
39 | const u8 *addr; | |
40 | struct wpa_state_machine *sm; | |
41 | }; | |
42 | ||
43 | ||
44 | static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) | |
45 | { | |
46 | struct wpa_stsl_search *search = ctx; | |
47 | if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { | |
48 | search->sm = sm; | |
49 | return 1; | |
50 | } | |
51 | return 0; | |
52 | } | |
53 | ||
54 | ||
55 | static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, | |
56 | struct wpa_state_machine *sm, const u8 *peer, | |
57 | u16 mui, u16 error_type) | |
58 | { | |
59 | u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + | |
60 | 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; | |
61 | u8 *pos; | |
62 | struct rsn_error_kde error; | |
63 | ||
64 | wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, | |
65 | "Sending SMK Error"); | |
66 | ||
67 | pos = kde; | |
68 | ||
69 | if (peer) { | |
70 | pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, | |
71 | NULL, 0); | |
72 | } | |
73 | ||
74 | error.mui = host_to_be16(mui); | |
75 | error.error_type = host_to_be16(error_type); | |
76 | pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, | |
77 | (u8 *) &error, sizeof(error), NULL, 0); | |
78 | ||
79 | __wpa_send_eapol(wpa_auth, sm, | |
80 | WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | | |
81 | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, | |
82 | NULL, NULL, kde, pos - kde, 0, 0, 0); | |
83 | } | |
84 | ||
85 | ||
86 | void wpa_smk_m1(struct wpa_authenticator *wpa_auth, | |
87 | struct wpa_state_machine *sm, struct wpa_eapol_key *key) | |
88 | { | |
89 | struct wpa_eapol_ie_parse kde; | |
90 | struct wpa_stsl_search search; | |
91 | u8 *buf, *pos; | |
92 | size_t buf_len; | |
93 | ||
94 | if (wpa_parse_kde_ies((const u8 *) (key + 1), | |
95 | WPA_GET_BE16(key->key_data_length), &kde) < 0) { | |
96 | wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); | |
97 | return; | |
98 | } | |
99 | ||
100 | if (kde.rsn_ie == NULL || kde.mac_addr == NULL || | |
101 | kde.mac_addr_len < ETH_ALEN) { | |
102 | wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " | |
103 | "SMK M1"); | |
104 | return; | |
105 | } | |
106 | ||
107 | /* Initiator = sm->addr; Peer = kde.mac_addr */ | |
108 | ||
109 | search.addr = kde.mac_addr; | |
110 | search.sm = NULL; | |
111 | if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == | |
112 | 0 || search.sm == NULL) { | |
113 | wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR | |
114 | " aborted - STA not associated anymore", | |
115 | MAC2STR(kde.mac_addr)); | |
116 | wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, | |
117 | STK_ERR_STA_NR); | |
118 | /* FIX: wpa_stsl_remove(wpa_auth, neg); */ | |
119 | return; | |
120 | } | |
121 | ||
122 | buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; | |
123 | buf = os_malloc(buf_len); | |
124 | if (buf == NULL) | |
125 | return; | |
126 | /* Initiator RSN IE */ | |
127 | os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); | |
128 | pos = buf + kde.rsn_ie_len; | |
129 | /* Initiator MAC Address */ | |
130 | pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, | |
131 | NULL, 0); | |
132 | ||
133 | /* SMK M2: | |
134 | * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, | |
135 | * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) | |
136 | */ | |
137 | ||
138 | wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, | |
139 | "Sending SMK M2"); | |
140 | ||
141 | __wpa_send_eapol(wpa_auth, search.sm, | |
142 | WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | | |
143 | WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, | |
144 | NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); | |
145 | ||
146 | os_free(buf); | |
147 | } | |
148 | ||
149 | ||
150 | static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, | |
151 | struct wpa_state_machine *sm, | |
152 | struct wpa_eapol_key *key, | |
153 | struct wpa_eapol_ie_parse *kde, | |
154 | const u8 *smk) | |
155 | { | |
156 | u8 *buf, *pos; | |
157 | size_t buf_len; | |
158 | u32 lifetime; | |
159 | ||
160 | /* SMK M4: | |
161 | * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, | |
162 | * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, | |
163 | * Lifetime KDE) | |
164 | */ | |
165 | ||
166 | buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + | |
167 | 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + | |
168 | 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + | |
169 | 2 + RSN_SELECTOR_LEN + sizeof(lifetime); | |
170 | pos = buf = os_malloc(buf_len); | |
171 | if (buf == NULL) | |
172 | return; | |
173 | ||
174 | /* Initiator MAC Address */ | |
175 | pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, | |
176 | NULL, 0); | |
177 | ||
178 | /* Initiator Nonce */ | |
179 | pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, | |
180 | NULL, 0); | |
181 | ||
182 | /* SMK with PNonce */ | |
183 | pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, | |
184 | key->key_nonce, WPA_NONCE_LEN); | |
185 | ||
186 | /* Lifetime */ | |
187 | lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ | |
188 | pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, | |
189 | (u8 *) &lifetime, sizeof(lifetime), NULL, 0); | |
190 | ||
191 | wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, | |
192 | "Sending SMK M4"); | |
193 | ||
194 | __wpa_send_eapol(wpa_auth, sm, | |
195 | WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | | |
196 | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, | |
197 | NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); | |
198 | ||
199 | os_free(buf); | |
200 | } | |
201 | ||
202 | ||
203 | static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, | |
204 | struct wpa_state_machine *sm, | |
205 | struct wpa_eapol_key *key, | |
206 | struct wpa_eapol_ie_parse *kde, | |
207 | const u8 *smk, const u8 *peer) | |
208 | { | |
209 | u8 *buf, *pos; | |
210 | size_t buf_len; | |
211 | u32 lifetime; | |
212 | ||
213 | /* SMK M5: | |
214 | * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, | |
215 | * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, | |
216 | * Lifetime KDE)) | |
217 | */ | |
218 | ||
219 | buf_len = kde->rsn_ie_len + | |
220 | 2 + RSN_SELECTOR_LEN + ETH_ALEN + | |
221 | 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + | |
222 | 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + | |
223 | 2 + RSN_SELECTOR_LEN + sizeof(lifetime); | |
224 | pos = buf = os_malloc(buf_len); | |
225 | if (buf == NULL) | |
226 | return; | |
227 | ||
228 | /* Peer RSN IE */ | |
229 | os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); | |
230 | pos = buf + kde->rsn_ie_len; | |
231 | ||
232 | /* Peer MAC Address */ | |
233 | pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); | |
234 | ||
235 | /* PNonce */ | |
236 | pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, | |
237 | WPA_NONCE_LEN, NULL, 0); | |
238 | ||
239 | /* SMK and INonce */ | |
240 | pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, | |
241 | kde->nonce, WPA_NONCE_LEN); | |
242 | ||
243 | /* Lifetime */ | |
244 | lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ | |
245 | pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, | |
246 | (u8 *) &lifetime, sizeof(lifetime), NULL, 0); | |
247 | ||
248 | wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, | |
249 | "Sending SMK M5"); | |
250 | ||
251 | __wpa_send_eapol(wpa_auth, sm, | |
252 | WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | | |
253 | WPA_KEY_INFO_SMK_MESSAGE, | |
254 | NULL, kde->nonce, buf, pos - buf, 0, 1, 0); | |
255 | ||
256 | os_free(buf); | |
257 | } | |
258 | ||
259 | ||
260 | void wpa_smk_m3(struct wpa_authenticator *wpa_auth, | |
261 | struct wpa_state_machine *sm, struct wpa_eapol_key *key) | |
262 | { | |
263 | struct wpa_eapol_ie_parse kde; | |
264 | struct wpa_stsl_search search; | |
265 | u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; | |
266 | ||
267 | if (wpa_parse_kde_ies((const u8 *) (key + 1), | |
268 | WPA_GET_BE16(key->key_data_length), &kde) < 0) { | |
269 | wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); | |
270 | return; | |
271 | } | |
272 | ||
273 | if (kde.rsn_ie == NULL || | |
274 | kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || | |
275 | kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { | |
276 | wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " | |
277 | "Nonce KDE in SMK M3"); | |
278 | return; | |
279 | } | |
280 | ||
281 | /* Peer = sm->addr; Initiator = kde.mac_addr; | |
282 | * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ | |
283 | ||
284 | search.addr = kde.mac_addr; | |
285 | search.sm = NULL; | |
286 | if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == | |
287 | 0 || search.sm == NULL) { | |
288 | wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR | |
289 | " aborted - STA not associated anymore", | |
290 | MAC2STR(kde.mac_addr)); | |
291 | wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, | |
292 | STK_ERR_STA_NR); | |
293 | /* FIX: wpa_stsl_remove(wpa_auth, neg); */ | |
294 | return; | |
295 | } | |
296 | ||
297 | if (os_get_random(smk, PMK_LEN)) { | |
298 | wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); | |
299 | return; | |
300 | } | |
301 | ||
302 | /* SMK = PRF-256(Random number, "SMK Derivation", | |
303 | * AA || Time || INonce || PNonce) | |
304 | */ | |
305 | os_memcpy(buf, wpa_auth->addr, ETH_ALEN); | |
306 | pos = buf + ETH_ALEN; | |
307 | wpa_get_ntp_timestamp(pos); | |
308 | pos += 8; | |
309 | os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); | |
310 | pos += WPA_NONCE_LEN; | |
311 | os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); | |
56586197 JM |
312 | #ifdef CONFIG_IEEE80211W |
313 | sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), | |
314 | smk, PMK_LEN); | |
315 | #else /* CONFIG_IEEE80211W */ | |
6fc6879b JM |
316 | sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), |
317 | smk, PMK_LEN); | |
56586197 | 318 | #endif /* CONFIG_IEEE80211W */ |
6fc6879b JM |
319 | |
320 | wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); | |
321 | ||
322 | wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); | |
323 | wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); | |
324 | ||
325 | /* Authenticator does not need SMK anymore and it is required to forget | |
326 | * it. */ | |
327 | os_memset(smk, 0, sizeof(*smk)); | |
328 | } | |
329 | ||
330 | ||
331 | void wpa_smk_error(struct wpa_authenticator *wpa_auth, | |
332 | struct wpa_state_machine *sm, struct wpa_eapol_key *key) | |
333 | { | |
334 | struct wpa_eapol_ie_parse kde; | |
335 | struct wpa_stsl_search search; | |
336 | struct rsn_error_kde error; | |
337 | u16 mui, error_type; | |
338 | ||
339 | if (wpa_parse_kde_ies((const u8 *) (key + 1), | |
340 | WPA_GET_BE16(key->key_data_length), &kde) < 0) { | |
341 | wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); | |
342 | return; | |
343 | } | |
344 | ||
345 | if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || | |
346 | kde.error == NULL || kde.error_len < sizeof(error)) { | |
347 | wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " | |
348 | "SMK Error"); | |
349 | return; | |
350 | } | |
351 | ||
352 | search.addr = kde.mac_addr; | |
353 | search.sm = NULL; | |
354 | if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == | |
355 | 0 || search.sm == NULL) { | |
356 | wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " | |
357 | "associated for SMK Error message from " MACSTR, | |
358 | MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); | |
359 | return; | |
360 | } | |
361 | ||
362 | os_memcpy(&error, kde.error, sizeof(error)); | |
363 | mui = be_to_host16(error.mui); | |
364 | error_type = be_to_host16(error.error_type); | |
365 | wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, | |
366 | "STA reported SMK Error: Peer " MACSTR | |
367 | " MUI %d Error Type %d", | |
368 | MAC2STR(kde.mac_addr), mui, error_type); | |
369 | ||
370 | wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); | |
371 | } | |
372 | ||
373 | ||
374 | int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, | |
375 | struct wpa_stsl_negotiation *neg) | |
376 | { | |
377 | struct wpa_stsl_negotiation *pos, *prev; | |
378 | ||
379 | if (wpa_auth == NULL) | |
380 | return -1; | |
381 | pos = wpa_auth->stsl_negotiations; | |
382 | prev = NULL; | |
383 | while (pos) { | |
384 | if (pos == neg) { | |
385 | if (prev) | |
386 | prev->next = pos->next; | |
387 | else | |
388 | wpa_auth->stsl_negotiations = pos->next; | |
389 | ||
390 | eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); | |
391 | os_free(pos); | |
392 | return 0; | |
393 | } | |
394 | prev = pos; | |
395 | pos = pos->next; | |
396 | } | |
397 | ||
398 | return -1; | |
399 | } | |
400 | ||
401 | #endif /* CONFIG_PEERKEY */ |