Branch data Line data Source code
1 : : /*
2 : : * hostapd / EAP-SAKE (RFC 4763) server
3 : : * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
4 : : *
5 : : * This software may be distributed under the terms of the BSD license.
6 : : * See README for more details.
7 : : */
8 : :
9 : : #include "includes.h"
10 : :
11 : : #include "common.h"
12 : : #include "crypto/random.h"
13 : : #include "eap_server/eap_i.h"
14 : : #include "eap_common/eap_sake_common.h"
15 : :
16 : :
17 : : struct eap_sake_data {
18 : : enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
19 : : u8 rand_s[EAP_SAKE_RAND_LEN];
20 : : u8 rand_p[EAP_SAKE_RAND_LEN];
21 : : struct {
22 : : u8 auth[EAP_SAKE_TEK_AUTH_LEN];
23 : : u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
24 : : } tek;
25 : : u8 msk[EAP_MSK_LEN];
26 : : u8 emsk[EAP_EMSK_LEN];
27 : : u8 session_id;
28 : : u8 *peerid;
29 : : size_t peerid_len;
30 : : };
31 : :
32 : :
33 : 8 : static const char * eap_sake_state_txt(int state)
34 : : {
35 [ - + + + : 8 : switch (state) {
- - ]
36 : : case IDENTITY:
37 : 0 : return "IDENTITY";
38 : : case CHALLENGE:
39 : 2 : return "CHALLENGE";
40 : : case CONFIRM:
41 : 4 : return "CONFIRM";
42 : : case SUCCESS:
43 : 2 : return "SUCCESS";
44 : : case FAILURE:
45 : 0 : return "FAILURE";
46 : : default:
47 : 8 : return "?";
48 : : }
49 : : }
50 : :
51 : :
52 : 4 : static void eap_sake_state(struct eap_sake_data *data, int state)
53 : : {
54 : 4 : wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
55 : 4 : eap_sake_state_txt(data->state),
56 : : eap_sake_state_txt(state));
57 : 4 : data->state = state;
58 : 4 : }
59 : :
60 : :
61 : 2 : static void * eap_sake_init(struct eap_sm *sm)
62 : : {
63 : : struct eap_sake_data *data;
64 : :
65 : 2 : data = os_zalloc(sizeof(*data));
66 [ - + ]: 2 : if (data == NULL)
67 : 0 : return NULL;
68 : 2 : data->state = CHALLENGE;
69 : :
70 [ - + ]: 2 : if (os_get_random(&data->session_id, 1)) {
71 : 0 : wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
72 : 0 : os_free(data);
73 : 0 : return NULL;
74 : : }
75 : 2 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
76 : 2 : data->session_id);
77 : :
78 : 2 : return data;
79 : : }
80 : :
81 : :
82 : 2 : static void eap_sake_reset(struct eap_sm *sm, void *priv)
83 : : {
84 : 2 : struct eap_sake_data *data = priv;
85 : 2 : os_free(data->peerid);
86 : 2 : os_free(data);
87 : 2 : }
88 : :
89 : :
90 : 4 : static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
91 : : u8 id, size_t length, u8 subtype)
92 : : {
93 : : struct eap_sake_hdr *sake;
94 : : struct wpabuf *msg;
95 : : size_t plen;
96 : :
97 : 4 : plen = sizeof(struct eap_sake_hdr) + length;
98 : :
99 : 4 : msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
100 : : EAP_CODE_REQUEST, id);
101 [ - + ]: 4 : if (msg == NULL) {
102 : 0 : wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
103 : : "request");
104 : 0 : return NULL;
105 : : }
106 : :
107 : 4 : sake = wpabuf_put(msg, sizeof(*sake));
108 : 4 : sake->version = EAP_SAKE_VERSION;
109 : 4 : sake->session_id = data->session_id;
110 : 4 : sake->subtype = subtype;
111 : :
112 : 4 : return msg;
113 : : }
114 : :
115 : :
116 : 0 : static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm,
117 : : struct eap_sake_data *data,
118 : : u8 id)
119 : : {
120 : : struct wpabuf *msg;
121 : : size_t plen;
122 : :
123 : 0 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
124 : :
125 : 0 : plen = 4;
126 : 0 : plen += 2 + sm->server_id_len;
127 : 0 : msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY);
128 [ # # ]: 0 : if (msg == NULL) {
129 : 0 : data->state = FAILURE;
130 : 0 : return NULL;
131 : : }
132 : :
133 : 0 : wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ");
134 : 0 : eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2);
135 : :
136 : 0 : wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
137 : 0 : eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
138 : : sm->server_id, sm->server_id_len);
139 : :
140 : 0 : return msg;
141 : : }
142 : :
143 : :
144 : 2 : static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
145 : : struct eap_sake_data *data,
146 : : u8 id)
147 : : {
148 : : struct wpabuf *msg;
149 : : size_t plen;
150 : :
151 : 2 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
152 : :
153 [ - + ]: 2 : if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) {
154 : 0 : wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
155 : 0 : data->state = FAILURE;
156 : 0 : return NULL;
157 : : }
158 : 2 : wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
159 : 2 : data->rand_s, EAP_SAKE_RAND_LEN);
160 : :
161 : 2 : plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len;
162 : 2 : msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
163 [ - + ]: 2 : if (msg == NULL) {
164 : 0 : data->state = FAILURE;
165 : 0 : return NULL;
166 : : }
167 : :
168 : 2 : wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S");
169 : 2 : eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S,
170 : 2 : data->rand_s, EAP_SAKE_RAND_LEN);
171 : :
172 : 2 : wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
173 : 2 : eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
174 : : sm->server_id, sm->server_id_len);
175 : :
176 : 2 : return msg;
177 : : }
178 : :
179 : :
180 : 2 : static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm,
181 : : struct eap_sake_data *data,
182 : : u8 id)
183 : : {
184 : : struct wpabuf *msg;
185 : : u8 *mic;
186 : :
187 : 2 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm");
188 : :
189 : 2 : msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
190 : : EAP_SAKE_SUBTYPE_CONFIRM);
191 [ - + ]: 2 : if (msg == NULL) {
192 : 0 : data->state = FAILURE;
193 : 0 : return NULL;
194 : : }
195 : :
196 : 2 : wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S");
197 : 2 : wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S);
198 : 2 : wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN);
199 : 2 : mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN);
200 [ - + ]: 4 : if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
201 : : sm->server_id, sm->server_id_len,
202 : 2 : data->peerid, data->peerid_len, 0,
203 : 2 : wpabuf_head(msg), wpabuf_len(msg), mic, mic))
204 : : {
205 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
206 : 0 : data->state = FAILURE;
207 : 0 : os_free(msg);
208 : 0 : return NULL;
209 : : }
210 : :
211 : 2 : return msg;
212 : : }
213 : :
214 : :
215 : 4 : static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id)
216 : : {
217 : 4 : struct eap_sake_data *data = priv;
218 : :
219 [ - + + - ]: 4 : switch (data->state) {
220 : : case IDENTITY:
221 : 0 : return eap_sake_build_identity(sm, data, id);
222 : : case CHALLENGE:
223 : 2 : return eap_sake_build_challenge(sm, data, id);
224 : : case CONFIRM:
225 : 2 : return eap_sake_build_confirm(sm, data, id);
226 : : default:
227 : 0 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq",
228 : 0 : data->state);
229 : 0 : break;
230 : : }
231 : 4 : return NULL;
232 : : }
233 : :
234 : :
235 : 4 : static Boolean eap_sake_check(struct eap_sm *sm, void *priv,
236 : : struct wpabuf *respData)
237 : : {
238 : 4 : struct eap_sake_data *data = priv;
239 : : struct eap_sake_hdr *resp;
240 : : size_t len;
241 : : u8 version, session_id, subtype;
242 : : const u8 *pos;
243 : :
244 : 4 : pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
245 [ - + ][ + - ]: 4 : if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
246 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame");
247 : 0 : return TRUE;
248 : : }
249 : :
250 : 4 : resp = (struct eap_sake_hdr *) pos;
251 : 4 : version = resp->version;
252 : 4 : session_id = resp->session_id;
253 : 4 : subtype = resp->subtype;
254 : :
255 [ - + ]: 4 : if (version != EAP_SAKE_VERSION) {
256 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version);
257 : 0 : return TRUE;
258 : : }
259 : :
260 [ - + ]: 4 : if (session_id != data->session_id) {
261 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
262 : 0 : session_id, data->session_id);
263 : 0 : return TRUE;
264 : : }
265 : :
266 : 4 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype);
267 : :
268 [ # # ][ - + ]: 4 : if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY)
269 : 0 : return FALSE;
270 : :
271 [ + + ][ + - ]: 4 : if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE)
272 : 2 : return FALSE;
273 : :
274 [ + - ][ + - ]: 2 : if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM)
275 : 2 : return FALSE;
276 : :
277 [ # # ]: 0 : if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT)
278 : 0 : return FALSE;
279 : :
280 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d",
281 : 0 : subtype, data->state);
282 : :
283 : 4 : return TRUE;
284 : : }
285 : :
286 : :
287 : 0 : static void eap_sake_process_identity(struct eap_sm *sm,
288 : : struct eap_sake_data *data,
289 : : const struct wpabuf *respData,
290 : : const u8 *payload, size_t payloadlen)
291 : : {
292 [ # # ]: 0 : if (data->state != IDENTITY)
293 : 0 : return;
294 : :
295 : 0 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity");
296 : : /* TODO: update identity and select new user data */
297 : 0 : eap_sake_state(data, CHALLENGE);
298 : : }
299 : :
300 : :
301 : 2 : static void eap_sake_process_challenge(struct eap_sm *sm,
302 : : struct eap_sake_data *data,
303 : : const struct wpabuf *respData,
304 : : const u8 *payload, size_t payloadlen)
305 : : {
306 : : struct eap_sake_parse_attr attr;
307 : : u8 mic_p[EAP_SAKE_MIC_LEN];
308 : :
309 [ - + ]: 2 : if (data->state != CHALLENGE)
310 : 0 : return;
311 : :
312 : 2 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge");
313 : :
314 [ - + ]: 2 : if (eap_sake_parse_attributes(payload, payloadlen, &attr))
315 : 0 : return;
316 : :
317 [ + - ][ - + ]: 2 : if (!attr.rand_p || !attr.mic_p) {
318 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not "
319 : : "include AT_RAND_P or AT_MIC_P");
320 : 0 : return;
321 : : }
322 : :
323 : 2 : os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN);
324 : :
325 : 2 : os_free(data->peerid);
326 : 2 : data->peerid = NULL;
327 : 2 : data->peerid_len = 0;
328 [ + - ]: 2 : if (attr.peerid) {
329 : 2 : data->peerid = os_malloc(attr.peerid_len);
330 [ - + ]: 2 : if (data->peerid == NULL)
331 : 0 : return;
332 : 2 : os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
333 : 2 : data->peerid_len = attr.peerid_len;
334 : : }
335 : :
336 [ + - ][ + - ]: 2 : if (sm->user == NULL || sm->user->password == NULL ||
[ - + ]
337 : 2 : sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
338 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with "
339 : : "%d-byte key not configured",
340 : : 2 * EAP_SAKE_ROOT_SECRET_LEN);
341 : 0 : data->state = FAILURE;
342 : 0 : return;
343 : : }
344 : 2 : eap_sake_derive_keys(sm->user->password,
345 : 2 : sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
346 : 2 : data->rand_s, data->rand_p,
347 : 2 : (u8 *) &data->tek, data->msk, data->emsk);
348 : :
349 : 4 : eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
350 : : sm->server_id, sm->server_id_len,
351 : 2 : data->peerid, data->peerid_len, 1,
352 : 2 : wpabuf_head(respData), wpabuf_len(respData),
353 : : attr.mic_p, mic_p);
354 [ - + ]: 2 : if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
355 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
356 : 0 : eap_sake_state(data, FAILURE);
357 : 0 : return;
358 : : }
359 : :
360 : 2 : eap_sake_state(data, CONFIRM);
361 : : }
362 : :
363 : :
364 : 2 : static void eap_sake_process_confirm(struct eap_sm *sm,
365 : : struct eap_sake_data *data,
366 : : const struct wpabuf *respData,
367 : : const u8 *payload, size_t payloadlen)
368 : : {
369 : : struct eap_sake_parse_attr attr;
370 : : u8 mic_p[EAP_SAKE_MIC_LEN];
371 : :
372 [ - + ]: 2 : if (data->state != CONFIRM)
373 : 0 : return;
374 : :
375 : 2 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
376 : :
377 [ - + ]: 2 : if (eap_sake_parse_attributes(payload, payloadlen, &attr))
378 : 0 : return;
379 : :
380 [ - + ]: 2 : if (!attr.mic_p) {
381 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
382 : : "include AT_MIC_P");
383 : 0 : return;
384 : : }
385 : :
386 : 4 : eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
387 : : sm->server_id, sm->server_id_len,
388 : 2 : data->peerid, data->peerid_len, 1,
389 : 2 : wpabuf_head(respData), wpabuf_len(respData),
390 : : attr.mic_p, mic_p);
391 [ - + ]: 2 : if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
392 : 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
393 : 0 : eap_sake_state(data, FAILURE);
394 : : } else
395 : 2 : eap_sake_state(data, SUCCESS);
396 : : }
397 : :
398 : :
399 : 0 : static void eap_sake_process_auth_reject(struct eap_sm *sm,
400 : : struct eap_sake_data *data,
401 : : const struct wpabuf *respData,
402 : : const u8 *payload, size_t payloadlen)
403 : : {
404 : 0 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
405 : 0 : eap_sake_state(data, FAILURE);
406 : 0 : }
407 : :
408 : :
409 : 4 : static void eap_sake_process(struct eap_sm *sm, void *priv,
410 : : struct wpabuf *respData)
411 : : {
412 : 4 : struct eap_sake_data *data = priv;
413 : : struct eap_sake_hdr *resp;
414 : : u8 subtype;
415 : : size_t len;
416 : : const u8 *pos, *end;
417 : :
418 : 4 : pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
419 [ - + ][ + - ]: 4 : if (pos == NULL || len < sizeof(struct eap_sake_hdr))
420 : 4 : return;
421 : :
422 : 4 : resp = (struct eap_sake_hdr *) pos;
423 : 4 : end = pos + len;
424 : 4 : subtype = resp->subtype;
425 : 4 : pos = (u8 *) (resp + 1);
426 : :
427 : 4 : wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
428 : 4 : pos, end - pos);
429 : :
430 [ - + + - : 4 : switch (subtype) {
- ]
431 : : case EAP_SAKE_SUBTYPE_IDENTITY:
432 : 0 : eap_sake_process_identity(sm, data, respData, pos, end - pos);
433 : 0 : break;
434 : : case EAP_SAKE_SUBTYPE_CHALLENGE:
435 : 2 : eap_sake_process_challenge(sm, data, respData, pos, end - pos);
436 : 2 : break;
437 : : case EAP_SAKE_SUBTYPE_CONFIRM:
438 : 2 : eap_sake_process_confirm(sm, data, respData, pos, end - pos);
439 : 2 : break;
440 : : case EAP_SAKE_SUBTYPE_AUTH_REJECT:
441 : 0 : eap_sake_process_auth_reject(sm, data, respData, pos,
442 : 0 : end - pos);
443 : 0 : break;
444 : : }
445 : : }
446 : :
447 : :
448 : 4 : static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
449 : : {
450 : 4 : struct eap_sake_data *data = priv;
451 [ + + ][ - + ]: 4 : return data->state == SUCCESS || data->state == FAILURE;
452 : : }
453 : :
454 : :
455 : 2 : static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
456 : : {
457 : 2 : struct eap_sake_data *data = priv;
458 : : u8 *key;
459 : :
460 [ - + ]: 2 : if (data->state != SUCCESS)
461 : 0 : return NULL;
462 : :
463 : 2 : key = os_malloc(EAP_MSK_LEN);
464 [ - + ]: 2 : if (key == NULL)
465 : 0 : return NULL;
466 : 2 : os_memcpy(key, data->msk, EAP_MSK_LEN);
467 : 2 : *len = EAP_MSK_LEN;
468 : :
469 : 2 : return key;
470 : : }
471 : :
472 : :
473 : 0 : static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
474 : : {
475 : 0 : struct eap_sake_data *data = priv;
476 : : u8 *key;
477 : :
478 [ # # ]: 0 : if (data->state != SUCCESS)
479 : 0 : return NULL;
480 : :
481 : 0 : key = os_malloc(EAP_EMSK_LEN);
482 [ # # ]: 0 : if (key == NULL)
483 : 0 : return NULL;
484 : 0 : os_memcpy(key, data->emsk, EAP_EMSK_LEN);
485 : 0 : *len = EAP_EMSK_LEN;
486 : :
487 : 0 : return key;
488 : : }
489 : :
490 : :
491 : 2 : static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
492 : : {
493 : 2 : struct eap_sake_data *data = priv;
494 : 2 : return data->state == SUCCESS;
495 : : }
496 : :
497 : :
498 : 2 : int eap_server_sake_register(void)
499 : : {
500 : : struct eap_method *eap;
501 : : int ret;
502 : :
503 : 2 : eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
504 : : EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
505 [ - + ]: 2 : if (eap == NULL)
506 : 0 : return -1;
507 : :
508 : 2 : eap->init = eap_sake_init;
509 : 2 : eap->reset = eap_sake_reset;
510 : 2 : eap->buildReq = eap_sake_buildReq;
511 : 2 : eap->check = eap_sake_check;
512 : 2 : eap->process = eap_sake_process;
513 : 2 : eap->isDone = eap_sake_isDone;
514 : 2 : eap->getKey = eap_sake_getKey;
515 : 2 : eap->isSuccess = eap_sake_isSuccess;
516 : 2 : eap->get_emsk = eap_sake_get_emsk;
517 : :
518 : 2 : ret = eap_server_method_register(eap);
519 [ - + ]: 2 : if (ret)
520 : 0 : eap_server_method_free(eap);
521 : 2 : return ret;
522 : : }
|