]>
Commit | Line | Data |
---|---|---|
dd10abcc HW |
1 | /* |
2 | * IEEE 802.1X-2010 KaY Interface | |
3 | * Copyright (c) 2013-2014, Qualcomm Atheros, Inc. | |
4 | * | |
5 | * This software may be distributed under the terms of the BSD license. | |
6 | * See README for more details. | |
7 | */ | |
9596a756 | 8 | |
dd10abcc HW |
9 | #include "utils/includes.h" |
10 | ||
11 | #include "utils/common.h" | |
12 | #include "eap_peer/eap.h" | |
13 | #include "eap_peer/eap_i.h" | |
14 | #include "eapol_supp/eapol_supp_sm.h" | |
15 | #include "pae/ieee802_1x_key.h" | |
16 | #include "pae/ieee802_1x_kay.h" | |
17 | #include "wpa_supplicant_i.h" | |
18 | #include "config.h" | |
19 | #include "config_ssid.h" | |
20 | #include "driver_i.h" | |
21 | #include "wpas_kay.h" | |
22 | ||
23 | ||
24 | #define DEFAULT_KEY_LEN 16 | |
25 | /* secure Connectivity Association Key Name (CKN) */ | |
26 | #define DEFAULT_CKN_LEN 16 | |
27 | ||
28 | ||
29 | static int wpas_macsec_init(void *priv, struct macsec_init_params *params) | |
30 | { | |
31 | return wpa_drv_macsec_init(priv, params); | |
32 | } | |
33 | ||
34 | ||
35 | static int wpas_macsec_deinit(void *priv) | |
36 | { | |
37 | return wpa_drv_macsec_deinit(priv); | |
38 | } | |
39 | ||
40 | ||
a25e4efc SD |
41 | static int wpas_macsec_get_capability(void *priv, enum macsec_cap *cap) |
42 | { | |
43 | return wpa_drv_macsec_get_capability(priv, cap); | |
44 | } | |
45 | ||
46 | ||
dd10abcc HW |
47 | static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled) |
48 | { | |
49 | return wpa_drv_enable_protect_frames(wpa_s, enabled); | |
50 | } | |
51 | ||
52 | ||
1d3d0666 SD |
53 | static int wpas_enable_encrypt(void *wpa_s, Boolean enabled) |
54 | { | |
55 | return wpa_drv_enable_encrypt(wpa_s, enabled); | |
56 | } | |
57 | ||
58 | ||
dd10abcc HW |
59 | static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window) |
60 | { | |
61 | return wpa_drv_set_replay_protect(wpa_s, enabled, window); | |
62 | } | |
63 | ||
64 | ||
07a6bfe1 | 65 | static int wpas_set_current_cipher_suite(void *wpa_s, u64 cs) |
dd10abcc | 66 | { |
ec958aee | 67 | return wpa_drv_set_current_cipher_suite(wpa_s, cs); |
dd10abcc HW |
68 | } |
69 | ||
70 | ||
71 | static int wpas_enable_controlled_port(void *wpa_s, Boolean enabled) | |
72 | { | |
73 | return wpa_drv_enable_controlled_port(wpa_s, enabled); | |
74 | } | |
75 | ||
76 | ||
7fa5eff8 | 77 | static int wpas_get_receive_lowest_pn(void *wpa_s, struct receive_sa *sa) |
dd10abcc | 78 | { |
7fa5eff8 | 79 | return wpa_drv_get_receive_lowest_pn(wpa_s, sa); |
dd10abcc HW |
80 | } |
81 | ||
82 | ||
7fa5eff8 | 83 | static int wpas_get_transmit_next_pn(void *wpa_s, struct transmit_sa *sa) |
dd10abcc | 84 | { |
7fa5eff8 | 85 | return wpa_drv_get_transmit_next_pn(wpa_s, sa); |
dd10abcc HW |
86 | } |
87 | ||
88 | ||
7fa5eff8 | 89 | static int wpas_set_transmit_next_pn(void *wpa_s, struct transmit_sa *sa) |
dd10abcc | 90 | { |
7fa5eff8 | 91 | return wpa_drv_set_transmit_next_pn(wpa_s, sa); |
dd10abcc HW |
92 | } |
93 | ||
94 | ||
2fc06756 MS |
95 | static int wpas_set_receive_lowest_pn(void *wpa_s, struct receive_sa *sa) |
96 | { | |
97 | return wpa_drv_set_receive_lowest_pn(wpa_s, sa); | |
98 | } | |
99 | ||
100 | ||
dd10abcc HW |
101 | static unsigned int conf_offset_val(enum confidentiality_offset co) |
102 | { | |
103 | switch (co) { | |
104 | case CONFIDENTIALITY_OFFSET_30: | |
105 | return 30; | |
106 | break; | |
107 | case CONFIDENTIALITY_OFFSET_50: | |
108 | return 50; | |
109 | default: | |
110 | return 0; | |
111 | } | |
112 | } | |
113 | ||
114 | ||
5f5ca284 | 115 | static int wpas_create_receive_sc(void *wpa_s, struct receive_sc *sc, |
dd10abcc HW |
116 | enum validate_frames vf, |
117 | enum confidentiality_offset co) | |
118 | { | |
5f5ca284 | 119 | return wpa_drv_create_receive_sc(wpa_s, sc, conf_offset_val(co), vf); |
dd10abcc HW |
120 | } |
121 | ||
122 | ||
5f5ca284 | 123 | static int wpas_delete_receive_sc(void *wpa_s, struct receive_sc *sc) |
dd10abcc | 124 | { |
5f5ca284 | 125 | return wpa_drv_delete_receive_sc(wpa_s, sc); |
dd10abcc HW |
126 | } |
127 | ||
128 | ||
cecdecdb | 129 | static int wpas_create_receive_sa(void *wpa_s, struct receive_sa *sa) |
dd10abcc | 130 | { |
cecdecdb | 131 | return wpa_drv_create_receive_sa(wpa_s, sa); |
dd10abcc HW |
132 | } |
133 | ||
134 | ||
23c3528a SD |
135 | static int wpas_delete_receive_sa(void *wpa_s, struct receive_sa *sa) |
136 | { | |
137 | return wpa_drv_delete_receive_sa(wpa_s, sa); | |
138 | } | |
139 | ||
140 | ||
cecdecdb | 141 | static int wpas_enable_receive_sa(void *wpa_s, struct receive_sa *sa) |
dd10abcc | 142 | { |
cecdecdb | 143 | return wpa_drv_enable_receive_sa(wpa_s, sa); |
dd10abcc HW |
144 | } |
145 | ||
146 | ||
cecdecdb | 147 | static int wpas_disable_receive_sa(void *wpa_s, struct receive_sa *sa) |
dd10abcc | 148 | { |
cecdecdb | 149 | return wpa_drv_disable_receive_sa(wpa_s, sa); |
dd10abcc HW |
150 | } |
151 | ||
152 | ||
dd10abcc | 153 | static int |
8ebfc7c2 | 154 | wpas_create_transmit_sc(void *wpa_s, struct transmit_sc *sc, |
dd10abcc HW |
155 | enum confidentiality_offset co) |
156 | { | |
8ebfc7c2 | 157 | return wpa_drv_create_transmit_sc(wpa_s, sc, conf_offset_val(co)); |
dd10abcc HW |
158 | } |
159 | ||
160 | ||
8ebfc7c2 | 161 | static int wpas_delete_transmit_sc(void *wpa_s, struct transmit_sc *sc) |
dd10abcc | 162 | { |
8ebfc7c2 | 163 | return wpa_drv_delete_transmit_sc(wpa_s, sc); |
dd10abcc HW |
164 | } |
165 | ||
166 | ||
909c1b98 | 167 | static int wpas_create_transmit_sa(void *wpa_s, struct transmit_sa *sa) |
dd10abcc | 168 | { |
909c1b98 | 169 | return wpa_drv_create_transmit_sa(wpa_s, sa); |
dd10abcc HW |
170 | } |
171 | ||
172 | ||
23c3528a SD |
173 | static int wpas_delete_transmit_sa(void *wpa_s, struct transmit_sa *sa) |
174 | { | |
175 | return wpa_drv_delete_transmit_sa(wpa_s, sa); | |
176 | } | |
177 | ||
178 | ||
909c1b98 | 179 | static int wpas_enable_transmit_sa(void *wpa_s, struct transmit_sa *sa) |
dd10abcc | 180 | { |
909c1b98 | 181 | return wpa_drv_enable_transmit_sa(wpa_s, sa); |
dd10abcc HW |
182 | } |
183 | ||
184 | ||
909c1b98 | 185 | static int wpas_disable_transmit_sa(void *wpa_s, struct transmit_sa *sa) |
dd10abcc | 186 | { |
909c1b98 | 187 | return wpa_drv_disable_transmit_sa(wpa_s, sa); |
dd10abcc HW |
188 | } |
189 | ||
190 | ||
191 | int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) | |
192 | { | |
193 | struct ieee802_1x_kay_ctx *kay_ctx; | |
194 | struct ieee802_1x_kay *res = NULL; | |
195 | enum macsec_policy policy; | |
196 | ||
197 | ieee802_1x_dealloc_kay_sm(wpa_s); | |
198 | ||
199 | if (!ssid || ssid->macsec_policy == 0) | |
200 | return 0; | |
201 | ||
7b4d546e SD |
202 | if (ssid->macsec_policy == 1) { |
203 | if (ssid->macsec_integ_only == 1) | |
204 | policy = SHOULD_SECURE; | |
205 | else | |
206 | policy = SHOULD_ENCRYPT; | |
207 | } else { | |
208 | policy = DO_NOT_SECURE; | |
209 | } | |
dd10abcc HW |
210 | |
211 | kay_ctx = os_zalloc(sizeof(*kay_ctx)); | |
212 | if (!kay_ctx) | |
213 | return -1; | |
214 | ||
215 | kay_ctx->ctx = wpa_s; | |
216 | ||
217 | kay_ctx->macsec_init = wpas_macsec_init; | |
218 | kay_ctx->macsec_deinit = wpas_macsec_deinit; | |
a25e4efc | 219 | kay_ctx->macsec_get_capability = wpas_macsec_get_capability; |
dd10abcc | 220 | kay_ctx->enable_protect_frames = wpas_enable_protect_frames; |
1d3d0666 | 221 | kay_ctx->enable_encrypt = wpas_enable_encrypt; |
dd10abcc HW |
222 | kay_ctx->set_replay_protect = wpas_set_replay_protect; |
223 | kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite; | |
224 | kay_ctx->enable_controlled_port = wpas_enable_controlled_port; | |
225 | kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn; | |
226 | kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn; | |
227 | kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn; | |
2fc06756 | 228 | kay_ctx->set_receive_lowest_pn = wpas_set_receive_lowest_pn; |
dd10abcc HW |
229 | kay_ctx->create_receive_sc = wpas_create_receive_sc; |
230 | kay_ctx->delete_receive_sc = wpas_delete_receive_sc; | |
231 | kay_ctx->create_receive_sa = wpas_create_receive_sa; | |
23c3528a | 232 | kay_ctx->delete_receive_sa = wpas_delete_receive_sa; |
dd10abcc HW |
233 | kay_ctx->enable_receive_sa = wpas_enable_receive_sa; |
234 | kay_ctx->disable_receive_sa = wpas_disable_receive_sa; | |
dd10abcc HW |
235 | kay_ctx->create_transmit_sc = wpas_create_transmit_sc; |
236 | kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc; | |
237 | kay_ctx->create_transmit_sa = wpas_create_transmit_sa; | |
23c3528a | 238 | kay_ctx->delete_transmit_sa = wpas_delete_transmit_sa; |
dd10abcc HW |
239 | kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa; |
240 | kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa; | |
241 | ||
e49b78c0 AK |
242 | res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_replay_protect, |
243 | ssid->macsec_replay_window, ssid->macsec_port, | |
65dfa872 BA |
244 | ssid->mka_priority, wpa_s->ifname, |
245 | wpa_s->own_addr); | |
7612e65b SD |
246 | /* ieee802_1x_kay_init() frees kay_ctx on failure */ |
247 | if (res == NULL) | |
dd10abcc | 248 | return -1; |
dd10abcc HW |
249 | |
250 | wpa_s->kay = res; | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | ||
256 | void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s) | |
257 | { | |
258 | if (!wpa_s->kay) | |
259 | return; | |
260 | ||
261 | ieee802_1x_kay_deinit(wpa_s->kay); | |
262 | wpa_s->kay = NULL; | |
263 | } | |
264 | ||
265 | ||
266 | static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s, | |
267 | const u8 *addr, u8 *sid, size_t *len) | |
268 | { | |
269 | const u8 *session_id; | |
270 | size_t id_len, need_len; | |
271 | ||
272 | session_id = eapol_sm_get_session_id(wpa_s->eapol, &id_len); | |
273 | if (session_id == NULL) { | |
274 | wpa_printf(MSG_DEBUG, | |
275 | "Failed to get SessionID from EAPOL state machines"); | |
276 | return -1; | |
277 | } | |
278 | ||
9596a756 | 279 | need_len = 1 + 2 * 32 /* random size */; |
dd10abcc HW |
280 | if (need_len > id_len) { |
281 | wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough"); | |
282 | return -1; | |
283 | } | |
284 | ||
285 | os_memcpy(sid, session_id, need_len); | |
286 | *len = need_len; | |
287 | ||
288 | return 0; | |
289 | } | |
290 | ||
291 | ||
292 | static int ieee802_1x_auth_get_msk(struct wpa_supplicant *wpa_s, const u8 *addr, | |
293 | u8 *msk, size_t *len) | |
294 | { | |
295 | u8 key[EAP_MSK_LEN]; | |
296 | size_t keylen; | |
297 | struct eapol_sm *sm; | |
298 | int res; | |
299 | ||
300 | sm = wpa_s->eapol; | |
301 | if (sm == NULL) | |
302 | return -1; | |
303 | ||
304 | keylen = EAP_MSK_LEN; | |
305 | res = eapol_sm_get_key(sm, key, keylen); | |
306 | if (res) { | |
307 | wpa_printf(MSG_DEBUG, | |
308 | "Failed to get MSK from EAPOL state machines"); | |
309 | return -1; | |
310 | } | |
311 | ||
312 | if (keylen > *len) | |
313 | keylen = *len; | |
314 | os_memcpy(msk, key, keylen); | |
315 | *len = keylen; | |
316 | ||
317 | return 0; | |
318 | } | |
319 | ||
320 | ||
321 | void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s, | |
322 | const u8 *peer_addr) | |
323 | { | |
324 | u8 *sid; | |
325 | size_t sid_len = 128; | |
326 | struct mka_key_name *ckn; | |
327 | struct mka_key *cak; | |
328 | struct mka_key *msk; | |
329 | void *res = NULL; | |
330 | ||
331 | if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE) | |
332 | return NULL; | |
333 | ||
334 | wpa_printf(MSG_DEBUG, | |
335 | "IEEE 802.1X: External notification - Create MKA for " | |
336 | MACSTR, MAC2STR(peer_addr)); | |
337 | ||
338 | msk = os_zalloc(sizeof(*msk)); | |
339 | sid = os_zalloc(sid_len); | |
340 | ckn = os_zalloc(sizeof(*ckn)); | |
341 | cak = os_zalloc(sizeof(*cak)); | |
342 | if (!msk || !sid || !ckn || !cak) | |
343 | goto fail; | |
344 | ||
345 | msk->len = DEFAULT_KEY_LEN; | |
346 | if (ieee802_1x_auth_get_msk(wpa_s, wpa_s->bssid, msk->key, &msk->len)) { | |
347 | wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK"); | |
348 | goto fail; | |
349 | } | |
350 | ||
351 | if (ieee802_1x_auth_get_session_id(wpa_s, wpa_s->bssid, sid, &sid_len)) | |
352 | { | |
353 | wpa_printf(MSG_ERROR, | |
354 | "IEEE 802.1X: Could not get EAP Session Id"); | |
355 | goto fail; | |
356 | } | |
357 | ||
358 | /* Derive CAK from MSK */ | |
359 | cak->len = DEFAULT_KEY_LEN; | |
7251f0ba JM |
360 | if (ieee802_1x_cak_aes_cmac(msk->key, msk->len, wpa_s->own_addr, |
361 | peer_addr, cak->key, cak->len)) { | |
dd10abcc HW |
362 | wpa_printf(MSG_ERROR, |
363 | "IEEE 802.1X: Deriving CAK failed"); | |
364 | goto fail; | |
365 | } | |
366 | wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len); | |
367 | ||
368 | /* Derive CKN from MSK */ | |
369 | ckn->len = DEFAULT_CKN_LEN; | |
7251f0ba JM |
370 | if (ieee802_1x_ckn_aes_cmac(msk->key, msk->len, wpa_s->own_addr, |
371 | peer_addr, sid, sid_len, ckn->name)) { | |
dd10abcc HW |
372 | wpa_printf(MSG_ERROR, |
373 | "IEEE 802.1X: Deriving CKN failed"); | |
374 | goto fail; | |
375 | } | |
376 | wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len); | |
377 | ||
378 | res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, | |
379 | EAP_EXCHANGE, FALSE); | |
380 | ||
381 | fail: | |
382 | if (msk) { | |
383 | os_memset(msk, 0, sizeof(*msk)); | |
384 | os_free(msk); | |
385 | } | |
386 | os_free(sid); | |
387 | os_free(ckn); | |
388 | if (cak) { | |
389 | os_memset(cak, 0, sizeof(*cak)); | |
390 | os_free(cak); | |
391 | } | |
392 | ||
393 | return res; | |
394 | } | |
ad51731a SD |
395 | |
396 | ||
397 | void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s, | |
398 | struct wpa_ssid *ssid) | |
399 | { | |
400 | struct mka_key *cak; | |
401 | struct mka_key_name *ckn; | |
22151b11 | 402 | void *res = NULL; |
ad51731a SD |
403 | |
404 | if ((ssid->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET) | |
22151b11 | 405 | goto end; |
ad51731a SD |
406 | |
407 | ckn = os_zalloc(sizeof(*ckn)); | |
408 | if (!ckn) | |
22151b11 | 409 | goto end; |
ad51731a SD |
410 | |
411 | cak = os_zalloc(sizeof(*cak)); | |
412 | if (!cak) | |
413 | goto free_ckn; | |
414 | ||
22151b11 DC |
415 | if (ieee802_1x_alloc_kay_sm(wpa_s, ssid) < 0 || !wpa_s->kay) |
416 | goto free_cak; | |
417 | ||
418 | if (wpa_s->kay->policy == DO_NOT_SECURE) | |
419 | goto dealloc; | |
420 | ||
871439b5 | 421 | cak->len = ssid->mka_cak_len; |
ad51731a SD |
422 | os_memcpy(cak->key, ssid->mka_cak, cak->len); |
423 | ||
b678ed1e | 424 | ckn->len = ssid->mka_ckn_len; |
ad51731a SD |
425 | os_memcpy(ckn->name, ssid->mka_ckn, ckn->len); |
426 | ||
427 | res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, PSK, FALSE); | |
428 | if (res) | |
22151b11 | 429 | goto free_cak; |
ad51731a | 430 | |
22151b11 | 431 | dealloc: |
ad51731a | 432 | /* Failed to create MKA */ |
22151b11 DC |
433 | ieee802_1x_dealloc_kay_sm(wpa_s); |
434 | free_cak: | |
ad51731a | 435 | os_free(cak); |
ad51731a SD |
436 | free_ckn: |
437 | os_free(ckn); | |
22151b11 DC |
438 | end: |
439 | return res; | |
ad51731a | 440 | } |