]>
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 | */ | |
8 | #include <openssl/ssl.h> | |
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 | ||
dd10abcc HW |
95 | static unsigned int conf_offset_val(enum confidentiality_offset co) |
96 | { | |
97 | switch (co) { | |
98 | case CONFIDENTIALITY_OFFSET_30: | |
99 | return 30; | |
100 | break; | |
101 | case CONFIDENTIALITY_OFFSET_50: | |
102 | return 50; | |
103 | default: | |
104 | return 0; | |
105 | } | |
106 | } | |
107 | ||
108 | ||
5f5ca284 | 109 | static int wpas_create_receive_sc(void *wpa_s, struct receive_sc *sc, |
dd10abcc HW |
110 | enum validate_frames vf, |
111 | enum confidentiality_offset co) | |
112 | { | |
5f5ca284 | 113 | return wpa_drv_create_receive_sc(wpa_s, sc, conf_offset_val(co), vf); |
dd10abcc HW |
114 | } |
115 | ||
116 | ||
5f5ca284 | 117 | static int wpas_delete_receive_sc(void *wpa_s, struct receive_sc *sc) |
dd10abcc | 118 | { |
5f5ca284 | 119 | return wpa_drv_delete_receive_sc(wpa_s, sc); |
dd10abcc HW |
120 | } |
121 | ||
122 | ||
cecdecdb | 123 | static int wpas_create_receive_sa(void *wpa_s, struct receive_sa *sa) |
dd10abcc | 124 | { |
cecdecdb | 125 | return wpa_drv_create_receive_sa(wpa_s, sa); |
dd10abcc HW |
126 | } |
127 | ||
128 | ||
23c3528a SD |
129 | static int wpas_delete_receive_sa(void *wpa_s, struct receive_sa *sa) |
130 | { | |
131 | return wpa_drv_delete_receive_sa(wpa_s, sa); | |
132 | } | |
133 | ||
134 | ||
cecdecdb | 135 | static int wpas_enable_receive_sa(void *wpa_s, struct receive_sa *sa) |
dd10abcc | 136 | { |
cecdecdb | 137 | return wpa_drv_enable_receive_sa(wpa_s, sa); |
dd10abcc HW |
138 | } |
139 | ||
140 | ||
cecdecdb | 141 | static int wpas_disable_receive_sa(void *wpa_s, struct receive_sa *sa) |
dd10abcc | 142 | { |
cecdecdb | 143 | return wpa_drv_disable_receive_sa(wpa_s, sa); |
dd10abcc HW |
144 | } |
145 | ||
146 | ||
dd10abcc | 147 | static int |
8ebfc7c2 | 148 | wpas_create_transmit_sc(void *wpa_s, struct transmit_sc *sc, |
dd10abcc HW |
149 | enum confidentiality_offset co) |
150 | { | |
8ebfc7c2 | 151 | return wpa_drv_create_transmit_sc(wpa_s, sc, conf_offset_val(co)); |
dd10abcc HW |
152 | } |
153 | ||
154 | ||
8ebfc7c2 | 155 | static int wpas_delete_transmit_sc(void *wpa_s, struct transmit_sc *sc) |
dd10abcc | 156 | { |
8ebfc7c2 | 157 | return wpa_drv_delete_transmit_sc(wpa_s, sc); |
dd10abcc HW |
158 | } |
159 | ||
160 | ||
909c1b98 | 161 | static int wpas_create_transmit_sa(void *wpa_s, struct transmit_sa *sa) |
dd10abcc | 162 | { |
909c1b98 | 163 | return wpa_drv_create_transmit_sa(wpa_s, sa); |
dd10abcc HW |
164 | } |
165 | ||
166 | ||
23c3528a SD |
167 | static int wpas_delete_transmit_sa(void *wpa_s, struct transmit_sa *sa) |
168 | { | |
169 | return wpa_drv_delete_transmit_sa(wpa_s, sa); | |
170 | } | |
171 | ||
172 | ||
909c1b98 | 173 | static int wpas_enable_transmit_sa(void *wpa_s, struct transmit_sa *sa) |
dd10abcc | 174 | { |
909c1b98 | 175 | return wpa_drv_enable_transmit_sa(wpa_s, sa); |
dd10abcc HW |
176 | } |
177 | ||
178 | ||
909c1b98 | 179 | static int wpas_disable_transmit_sa(void *wpa_s, struct transmit_sa *sa) |
dd10abcc | 180 | { |
909c1b98 | 181 | return wpa_drv_disable_transmit_sa(wpa_s, sa); |
dd10abcc HW |
182 | } |
183 | ||
184 | ||
185 | int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) | |
186 | { | |
187 | struct ieee802_1x_kay_ctx *kay_ctx; | |
188 | struct ieee802_1x_kay *res = NULL; | |
189 | enum macsec_policy policy; | |
190 | ||
191 | ieee802_1x_dealloc_kay_sm(wpa_s); | |
192 | ||
193 | if (!ssid || ssid->macsec_policy == 0) | |
194 | return 0; | |
195 | ||
7b4d546e SD |
196 | if (ssid->macsec_policy == 1) { |
197 | if (ssid->macsec_integ_only == 1) | |
198 | policy = SHOULD_SECURE; | |
199 | else | |
200 | policy = SHOULD_ENCRYPT; | |
201 | } else { | |
202 | policy = DO_NOT_SECURE; | |
203 | } | |
dd10abcc HW |
204 | |
205 | kay_ctx = os_zalloc(sizeof(*kay_ctx)); | |
206 | if (!kay_ctx) | |
207 | return -1; | |
208 | ||
209 | kay_ctx->ctx = wpa_s; | |
210 | ||
211 | kay_ctx->macsec_init = wpas_macsec_init; | |
212 | kay_ctx->macsec_deinit = wpas_macsec_deinit; | |
a25e4efc | 213 | kay_ctx->macsec_get_capability = wpas_macsec_get_capability; |
dd10abcc | 214 | kay_ctx->enable_protect_frames = wpas_enable_protect_frames; |
1d3d0666 | 215 | kay_ctx->enable_encrypt = wpas_enable_encrypt; |
dd10abcc HW |
216 | kay_ctx->set_replay_protect = wpas_set_replay_protect; |
217 | kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite; | |
218 | kay_ctx->enable_controlled_port = wpas_enable_controlled_port; | |
219 | kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn; | |
220 | kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn; | |
221 | kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn; | |
dd10abcc HW |
222 | kay_ctx->create_receive_sc = wpas_create_receive_sc; |
223 | kay_ctx->delete_receive_sc = wpas_delete_receive_sc; | |
224 | kay_ctx->create_receive_sa = wpas_create_receive_sa; | |
23c3528a | 225 | kay_ctx->delete_receive_sa = wpas_delete_receive_sa; |
dd10abcc HW |
226 | kay_ctx->enable_receive_sa = wpas_enable_receive_sa; |
227 | kay_ctx->disable_receive_sa = wpas_disable_receive_sa; | |
dd10abcc HW |
228 | kay_ctx->create_transmit_sc = wpas_create_transmit_sc; |
229 | kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc; | |
230 | kay_ctx->create_transmit_sa = wpas_create_transmit_sa; | |
23c3528a | 231 | kay_ctx->delete_transmit_sa = wpas_delete_transmit_sa; |
dd10abcc HW |
232 | kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa; |
233 | kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa; | |
234 | ||
e0d9fd34 | 235 | res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_port, |
65dfa872 BA |
236 | ssid->mka_priority, wpa_s->ifname, |
237 | wpa_s->own_addr); | |
dd10abcc HW |
238 | if (res == NULL) { |
239 | os_free(kay_ctx); | |
240 | return -1; | |
241 | } | |
242 | ||
243 | wpa_s->kay = res; | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
248 | ||
249 | void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s) | |
250 | { | |
251 | if (!wpa_s->kay) | |
252 | return; | |
253 | ||
254 | ieee802_1x_kay_deinit(wpa_s->kay); | |
255 | wpa_s->kay = NULL; | |
256 | } | |
257 | ||
258 | ||
259 | static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s, | |
260 | const u8 *addr, u8 *sid, size_t *len) | |
261 | { | |
262 | const u8 *session_id; | |
263 | size_t id_len, need_len; | |
264 | ||
265 | session_id = eapol_sm_get_session_id(wpa_s->eapol, &id_len); | |
266 | if (session_id == NULL) { | |
267 | wpa_printf(MSG_DEBUG, | |
268 | "Failed to get SessionID from EAPOL state machines"); | |
269 | return -1; | |
270 | } | |
271 | ||
272 | need_len = 1 + 2 * SSL3_RANDOM_SIZE; | |
273 | if (need_len > id_len) { | |
274 | wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough"); | |
275 | return -1; | |
276 | } | |
277 | ||
278 | os_memcpy(sid, session_id, need_len); | |
279 | *len = need_len; | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
284 | ||
285 | static int ieee802_1x_auth_get_msk(struct wpa_supplicant *wpa_s, const u8 *addr, | |
286 | u8 *msk, size_t *len) | |
287 | { | |
288 | u8 key[EAP_MSK_LEN]; | |
289 | size_t keylen; | |
290 | struct eapol_sm *sm; | |
291 | int res; | |
292 | ||
293 | sm = wpa_s->eapol; | |
294 | if (sm == NULL) | |
295 | return -1; | |
296 | ||
297 | keylen = EAP_MSK_LEN; | |
298 | res = eapol_sm_get_key(sm, key, keylen); | |
299 | if (res) { | |
300 | wpa_printf(MSG_DEBUG, | |
301 | "Failed to get MSK from EAPOL state machines"); | |
302 | return -1; | |
303 | } | |
304 | ||
305 | if (keylen > *len) | |
306 | keylen = *len; | |
307 | os_memcpy(msk, key, keylen); | |
308 | *len = keylen; | |
309 | ||
310 | return 0; | |
311 | } | |
312 | ||
313 | ||
314 | void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s, | |
315 | const u8 *peer_addr) | |
316 | { | |
317 | u8 *sid; | |
318 | size_t sid_len = 128; | |
319 | struct mka_key_name *ckn; | |
320 | struct mka_key *cak; | |
321 | struct mka_key *msk; | |
322 | void *res = NULL; | |
323 | ||
324 | if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE) | |
325 | return NULL; | |
326 | ||
327 | wpa_printf(MSG_DEBUG, | |
328 | "IEEE 802.1X: External notification - Create MKA for " | |
329 | MACSTR, MAC2STR(peer_addr)); | |
330 | ||
331 | msk = os_zalloc(sizeof(*msk)); | |
332 | sid = os_zalloc(sid_len); | |
333 | ckn = os_zalloc(sizeof(*ckn)); | |
334 | cak = os_zalloc(sizeof(*cak)); | |
335 | if (!msk || !sid || !ckn || !cak) | |
336 | goto fail; | |
337 | ||
338 | msk->len = DEFAULT_KEY_LEN; | |
339 | if (ieee802_1x_auth_get_msk(wpa_s, wpa_s->bssid, msk->key, &msk->len)) { | |
340 | wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK"); | |
341 | goto fail; | |
342 | } | |
343 | ||
344 | if (ieee802_1x_auth_get_session_id(wpa_s, wpa_s->bssid, sid, &sid_len)) | |
345 | { | |
346 | wpa_printf(MSG_ERROR, | |
347 | "IEEE 802.1X: Could not get EAP Session Id"); | |
348 | goto fail; | |
349 | } | |
350 | ||
351 | /* Derive CAK from MSK */ | |
352 | cak->len = DEFAULT_KEY_LEN; | |
353 | if (ieee802_1x_cak_128bits_aes_cmac(msk->key, wpa_s->own_addr, | |
354 | peer_addr, cak->key)) { | |
355 | wpa_printf(MSG_ERROR, | |
356 | "IEEE 802.1X: Deriving CAK failed"); | |
357 | goto fail; | |
358 | } | |
359 | wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len); | |
360 | ||
361 | /* Derive CKN from MSK */ | |
362 | ckn->len = DEFAULT_CKN_LEN; | |
363 | if (ieee802_1x_ckn_128bits_aes_cmac(msk->key, wpa_s->own_addr, | |
364 | peer_addr, sid, sid_len, | |
365 | ckn->name)) { | |
366 | wpa_printf(MSG_ERROR, | |
367 | "IEEE 802.1X: Deriving CKN failed"); | |
368 | goto fail; | |
369 | } | |
370 | wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len); | |
371 | ||
372 | res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, | |
373 | EAP_EXCHANGE, FALSE); | |
374 | ||
375 | fail: | |
376 | if (msk) { | |
377 | os_memset(msk, 0, sizeof(*msk)); | |
378 | os_free(msk); | |
379 | } | |
380 | os_free(sid); | |
381 | os_free(ckn); | |
382 | if (cak) { | |
383 | os_memset(cak, 0, sizeof(*cak)); | |
384 | os_free(cak); | |
385 | } | |
386 | ||
387 | return res; | |
388 | } | |
ad51731a SD |
389 | |
390 | ||
391 | void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s, | |
392 | struct wpa_ssid *ssid) | |
393 | { | |
394 | struct mka_key *cak; | |
395 | struct mka_key_name *ckn; | |
396 | void *res; | |
397 | ||
398 | if ((ssid->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET) | |
399 | return NULL; | |
400 | ||
401 | if (ieee802_1x_alloc_kay_sm(wpa_s, ssid) < 0) | |
402 | return NULL; | |
403 | ||
404 | if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE) | |
405 | return NULL; | |
406 | ||
407 | ckn = os_zalloc(sizeof(*ckn)); | |
408 | if (!ckn) | |
409 | goto dealloc; | |
410 | ||
411 | cak = os_zalloc(sizeof(*cak)); | |
412 | if (!cak) | |
413 | goto free_ckn; | |
414 | ||
415 | cak->len = MACSEC_CAK_LEN; | |
416 | os_memcpy(cak->key, ssid->mka_cak, cak->len); | |
417 | ||
418 | ckn->len = MACSEC_CKN_LEN; | |
419 | os_memcpy(ckn->name, ssid->mka_ckn, ckn->len); | |
420 | ||
421 | res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, PSK, FALSE); | |
422 | if (res) | |
423 | return res; | |
424 | ||
425 | /* Failed to create MKA */ | |
426 | os_free(cak); | |
427 | ||
428 | /* fallthrough */ | |
429 | ||
430 | free_ckn: | |
431 | os_free(ckn); | |
432 | dealloc: | |
433 | ieee802_1x_dealloc_kay_sm(wpa_s); | |
434 | ||
435 | return NULL; | |
436 | } |