]>
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 | ||
41 | static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled) | |
42 | { | |
43 | return wpa_drv_enable_protect_frames(wpa_s, enabled); | |
44 | } | |
45 | ||
46 | ||
47 | static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window) | |
48 | { | |
49 | return wpa_drv_set_replay_protect(wpa_s, enabled, window); | |
50 | } | |
51 | ||
52 | ||
53 | static int wpas_set_current_cipher_suite(void *wpa_s, const u8 *cs, | |
54 | size_t cs_len) | |
55 | { | |
56 | return wpa_drv_set_current_cipher_suite(wpa_s, cs, cs_len); | |
57 | } | |
58 | ||
59 | ||
60 | static int wpas_enable_controlled_port(void *wpa_s, Boolean enabled) | |
61 | { | |
62 | return wpa_drv_enable_controlled_port(wpa_s, enabled); | |
63 | } | |
64 | ||
65 | ||
66 | static int wpas_get_receive_lowest_pn(void *wpa_s, u32 channel, | |
67 | u8 an, u32 *lowest_pn) | |
68 | { | |
69 | return wpa_drv_get_receive_lowest_pn(wpa_s, channel, an, lowest_pn); | |
70 | } | |
71 | ||
72 | ||
73 | static int wpas_get_transmit_next_pn(void *wpa_s, u32 channel, | |
74 | u8 an, u32 *next_pn) | |
75 | { | |
76 | return wpa_drv_get_transmit_next_pn(wpa_s, channel, an, next_pn); | |
77 | } | |
78 | ||
79 | ||
80 | static int wpas_set_transmit_next_pn(void *wpa_s, u32 channel, | |
81 | u8 an, u32 next_pn) | |
82 | { | |
83 | return wpa_drv_set_transmit_next_pn(wpa_s, channel, an, next_pn); | |
84 | } | |
85 | ||
86 | ||
87 | static int wpas_get_available_receive_sc(void *wpa_s, u32 *channel) | |
88 | { | |
89 | return wpa_drv_get_available_receive_sc(wpa_s, channel); | |
90 | } | |
91 | ||
92 | ||
93 | static unsigned int conf_offset_val(enum confidentiality_offset co) | |
94 | { | |
95 | switch (co) { | |
96 | case CONFIDENTIALITY_OFFSET_30: | |
97 | return 30; | |
98 | break; | |
99 | case CONFIDENTIALITY_OFFSET_50: | |
100 | return 50; | |
101 | default: | |
102 | return 0; | |
103 | } | |
104 | } | |
105 | ||
106 | ||
107 | static int wpas_create_receive_sc(void *wpa_s, u32 channel, | |
108 | struct ieee802_1x_mka_sci *sci, | |
109 | enum validate_frames vf, | |
110 | enum confidentiality_offset co) | |
111 | { | |
112 | return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, sci->port, | |
113 | conf_offset_val(co), vf); | |
114 | } | |
115 | ||
116 | ||
117 | static int wpas_delete_receive_sc(void *wpa_s, u32 channel) | |
118 | { | |
119 | return wpa_drv_delete_receive_sc(wpa_s, channel); | |
120 | } | |
121 | ||
122 | ||
123 | static int wpas_create_receive_sa(void *wpa_s, u32 channel, u8 an, | |
124 | u32 lowest_pn, const u8 *sak) | |
125 | { | |
126 | return wpa_drv_create_receive_sa(wpa_s, channel, an, lowest_pn, sak); | |
127 | } | |
128 | ||
129 | ||
130 | static int wpas_enable_receive_sa(void *wpa_s, u32 channel, u8 an) | |
131 | { | |
132 | return wpa_drv_enable_receive_sa(wpa_s, channel, an); | |
133 | } | |
134 | ||
135 | ||
136 | static int wpas_disable_receive_sa(void *wpa_s, u32 channel, u8 an) | |
137 | { | |
138 | return wpa_drv_disable_receive_sa(wpa_s, channel, an); | |
139 | } | |
140 | ||
141 | ||
142 | static int wpas_get_available_transmit_sc(void *wpa_s, u32 *channel) | |
143 | { | |
144 | return wpa_drv_get_available_transmit_sc(wpa_s, channel); | |
145 | } | |
146 | ||
147 | ||
148 | static int | |
149 | wpas_create_transmit_sc(void *wpa_s, u32 channel, | |
150 | const struct ieee802_1x_mka_sci *sci, | |
151 | enum confidentiality_offset co) | |
152 | { | |
153 | return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, sci->port, | |
154 | conf_offset_val(co)); | |
155 | } | |
156 | ||
157 | ||
158 | static int wpas_delete_transmit_sc(void *wpa_s, u32 channel) | |
159 | { | |
160 | return wpa_drv_delete_transmit_sc(wpa_s, channel); | |
161 | } | |
162 | ||
163 | ||
164 | static int wpas_create_transmit_sa(void *wpa_s, u32 channel, u8 an, | |
165 | u32 next_pn, Boolean confidentiality, | |
166 | const u8 *sak) | |
167 | { | |
168 | return wpa_drv_create_transmit_sa(wpa_s, channel, an, next_pn, | |
169 | confidentiality, sak); | |
170 | } | |
171 | ||
172 | ||
173 | static int wpas_enable_transmit_sa(void *wpa_s, u32 channel, u8 an) | |
174 | { | |
175 | return wpa_drv_enable_transmit_sa(wpa_s, channel, an); | |
176 | } | |
177 | ||
178 | ||
179 | static int wpas_disable_transmit_sa(void *wpa_s, u32 channel, u8 an) | |
180 | { | |
181 | return wpa_drv_disable_transmit_sa(wpa_s, channel, an); | |
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 | ||
196 | policy = ssid->macsec_policy == 1 ? SHOULD_SECURE : DO_NOT_SECURE; | |
197 | ||
198 | kay_ctx = os_zalloc(sizeof(*kay_ctx)); | |
199 | if (!kay_ctx) | |
200 | return -1; | |
201 | ||
202 | kay_ctx->ctx = wpa_s; | |
203 | ||
204 | kay_ctx->macsec_init = wpas_macsec_init; | |
205 | kay_ctx->macsec_deinit = wpas_macsec_deinit; | |
206 | kay_ctx->enable_protect_frames = wpas_enable_protect_frames; | |
207 | kay_ctx->set_replay_protect = wpas_set_replay_protect; | |
208 | kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite; | |
209 | kay_ctx->enable_controlled_port = wpas_enable_controlled_port; | |
210 | kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn; | |
211 | kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn; | |
212 | kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn; | |
213 | kay_ctx->get_available_receive_sc = wpas_get_available_receive_sc; | |
214 | kay_ctx->create_receive_sc = wpas_create_receive_sc; | |
215 | kay_ctx->delete_receive_sc = wpas_delete_receive_sc; | |
216 | kay_ctx->create_receive_sa = wpas_create_receive_sa; | |
217 | kay_ctx->enable_receive_sa = wpas_enable_receive_sa; | |
218 | kay_ctx->disable_receive_sa = wpas_disable_receive_sa; | |
219 | kay_ctx->get_available_transmit_sc = wpas_get_available_transmit_sc; | |
220 | kay_ctx->create_transmit_sc = wpas_create_transmit_sc; | |
221 | kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc; | |
222 | kay_ctx->create_transmit_sa = wpas_create_transmit_sa; | |
223 | kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa; | |
224 | kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa; | |
225 | ||
226 | res = ieee802_1x_kay_init(kay_ctx, policy, wpa_s->ifname, | |
227 | wpa_s->own_addr); | |
228 | if (res == NULL) { | |
229 | os_free(kay_ctx); | |
230 | return -1; | |
231 | } | |
232 | ||
233 | wpa_s->kay = res; | |
234 | ||
235 | return 0; | |
236 | } | |
237 | ||
238 | ||
239 | void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s) | |
240 | { | |
241 | if (!wpa_s->kay) | |
242 | return; | |
243 | ||
244 | ieee802_1x_kay_deinit(wpa_s->kay); | |
245 | wpa_s->kay = NULL; | |
246 | } | |
247 | ||
248 | ||
249 | static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s, | |
250 | const u8 *addr, u8 *sid, size_t *len) | |
251 | { | |
252 | const u8 *session_id; | |
253 | size_t id_len, need_len; | |
254 | ||
255 | session_id = eapol_sm_get_session_id(wpa_s->eapol, &id_len); | |
256 | if (session_id == NULL) { | |
257 | wpa_printf(MSG_DEBUG, | |
258 | "Failed to get SessionID from EAPOL state machines"); | |
259 | return -1; | |
260 | } | |
261 | ||
262 | need_len = 1 + 2 * SSL3_RANDOM_SIZE; | |
263 | if (need_len > id_len) { | |
264 | wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough"); | |
265 | return -1; | |
266 | } | |
267 | ||
268 | os_memcpy(sid, session_id, need_len); | |
269 | *len = need_len; | |
270 | ||
271 | return 0; | |
272 | } | |
273 | ||
274 | ||
275 | static int ieee802_1x_auth_get_msk(struct wpa_supplicant *wpa_s, const u8 *addr, | |
276 | u8 *msk, size_t *len) | |
277 | { | |
278 | u8 key[EAP_MSK_LEN]; | |
279 | size_t keylen; | |
280 | struct eapol_sm *sm; | |
281 | int res; | |
282 | ||
283 | sm = wpa_s->eapol; | |
284 | if (sm == NULL) | |
285 | return -1; | |
286 | ||
287 | keylen = EAP_MSK_LEN; | |
288 | res = eapol_sm_get_key(sm, key, keylen); | |
289 | if (res) { | |
290 | wpa_printf(MSG_DEBUG, | |
291 | "Failed to get MSK from EAPOL state machines"); | |
292 | return -1; | |
293 | } | |
294 | ||
295 | if (keylen > *len) | |
296 | keylen = *len; | |
297 | os_memcpy(msk, key, keylen); | |
298 | *len = keylen; | |
299 | ||
300 | return 0; | |
301 | } | |
302 | ||
303 | ||
304 | void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s, | |
305 | const u8 *peer_addr) | |
306 | { | |
307 | u8 *sid; | |
308 | size_t sid_len = 128; | |
309 | struct mka_key_name *ckn; | |
310 | struct mka_key *cak; | |
311 | struct mka_key *msk; | |
312 | void *res = NULL; | |
313 | ||
314 | if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE) | |
315 | return NULL; | |
316 | ||
317 | wpa_printf(MSG_DEBUG, | |
318 | "IEEE 802.1X: External notification - Create MKA for " | |
319 | MACSTR, MAC2STR(peer_addr)); | |
320 | ||
321 | msk = os_zalloc(sizeof(*msk)); | |
322 | sid = os_zalloc(sid_len); | |
323 | ckn = os_zalloc(sizeof(*ckn)); | |
324 | cak = os_zalloc(sizeof(*cak)); | |
325 | if (!msk || !sid || !ckn || !cak) | |
326 | goto fail; | |
327 | ||
328 | msk->len = DEFAULT_KEY_LEN; | |
329 | if (ieee802_1x_auth_get_msk(wpa_s, wpa_s->bssid, msk->key, &msk->len)) { | |
330 | wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK"); | |
331 | goto fail; | |
332 | } | |
333 | ||
334 | if (ieee802_1x_auth_get_session_id(wpa_s, wpa_s->bssid, sid, &sid_len)) | |
335 | { | |
336 | wpa_printf(MSG_ERROR, | |
337 | "IEEE 802.1X: Could not get EAP Session Id"); | |
338 | goto fail; | |
339 | } | |
340 | ||
341 | /* Derive CAK from MSK */ | |
342 | cak->len = DEFAULT_KEY_LEN; | |
343 | if (ieee802_1x_cak_128bits_aes_cmac(msk->key, wpa_s->own_addr, | |
344 | peer_addr, cak->key)) { | |
345 | wpa_printf(MSG_ERROR, | |
346 | "IEEE 802.1X: Deriving CAK failed"); | |
347 | goto fail; | |
348 | } | |
349 | wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len); | |
350 | ||
351 | /* Derive CKN from MSK */ | |
352 | ckn->len = DEFAULT_CKN_LEN; | |
353 | if (ieee802_1x_ckn_128bits_aes_cmac(msk->key, wpa_s->own_addr, | |
354 | peer_addr, sid, sid_len, | |
355 | ckn->name)) { | |
356 | wpa_printf(MSG_ERROR, | |
357 | "IEEE 802.1X: Deriving CKN failed"); | |
358 | goto fail; | |
359 | } | |
360 | wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len); | |
361 | ||
362 | res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, | |
363 | EAP_EXCHANGE, FALSE); | |
364 | ||
365 | fail: | |
366 | if (msk) { | |
367 | os_memset(msk, 0, sizeof(*msk)); | |
368 | os_free(msk); | |
369 | } | |
370 | os_free(sid); | |
371 | os_free(ckn); | |
372 | if (cak) { | |
373 | os_memset(cak, 0, sizeof(*cak)); | |
374 | os_free(cak); | |
375 | } | |
376 | ||
377 | return res; | |
378 | } |