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 10 : static const char * eap_sake_state_txt(int state)
34 : {
35 10 : switch (state) {
36 : case IDENTITY:
37 0 : return "IDENTITY";
38 : case CHALLENGE:
39 3 : return "CHALLENGE";
40 : case CONFIRM:
41 4 : return "CONFIRM";
42 : case SUCCESS:
43 2 : return "SUCCESS";
44 : case FAILURE:
45 1 : return "FAILURE";
46 : default:
47 0 : return "?";
48 : }
49 : }
50 :
51 :
52 5 : static void eap_sake_state(struct eap_sake_data *data, int state)
53 : {
54 10 : wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
55 5 : eap_sake_state_txt(data->state),
56 : eap_sake_state_txt(state));
57 5 : data->state = state;
58 5 : }
59 :
60 :
61 3 : static void * eap_sake_init(struct eap_sm *sm)
62 : {
63 : struct eap_sake_data *data;
64 :
65 3 : data = os_zalloc(sizeof(*data));
66 3 : if (data == NULL)
67 0 : return NULL;
68 3 : data->state = CHALLENGE;
69 :
70 3 : 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 3 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
76 3 : data->session_id);
77 :
78 3 : return data;
79 : }
80 :
81 :
82 3 : static void eap_sake_reset(struct eap_sm *sm, void *priv)
83 : {
84 3 : struct eap_sake_data *data = priv;
85 3 : os_free(data->peerid);
86 3 : os_free(data);
87 3 : }
88 :
89 :
90 5 : 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 5 : plen = sizeof(struct eap_sake_hdr) + length;
98 :
99 5 : msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
100 : EAP_CODE_REQUEST, id);
101 5 : if (msg == NULL) {
102 0 : wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
103 : "request");
104 0 : return NULL;
105 : }
106 :
107 5 : sake = wpabuf_put(msg, sizeof(*sake));
108 5 : sake->version = EAP_SAKE_VERSION;
109 5 : sake->session_id = data->session_id;
110 5 : sake->subtype = subtype;
111 :
112 5 : 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 3 : 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 3 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
152 :
153 3 : 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 3 : wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
159 3 : data->rand_s, EAP_SAKE_RAND_LEN);
160 :
161 3 : plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len;
162 3 : msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
163 3 : if (msg == NULL) {
164 0 : data->state = FAILURE;
165 0 : return NULL;
166 : }
167 :
168 3 : wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S");
169 3 : eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S,
170 3 : data->rand_s, EAP_SAKE_RAND_LEN);
171 :
172 3 : wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
173 3 : eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
174 : sm->server_id, sm->server_id_len);
175 :
176 3 : 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 6 : 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 5 : static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id)
216 : {
217 5 : struct eap_sake_data *data = priv;
218 :
219 5 : switch (data->state) {
220 : case IDENTITY:
221 0 : return eap_sake_build_identity(sm, data, id);
222 : case CHALLENGE:
223 3 : 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 0 : return NULL;
232 : }
233 :
234 :
235 5 : static Boolean eap_sake_check(struct eap_sm *sm, void *priv,
236 : struct wpabuf *respData)
237 : {
238 5 : 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 5 : pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
245 5 : 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 5 : resp = (struct eap_sake_hdr *) pos;
251 5 : version = resp->version;
252 5 : session_id = resp->session_id;
253 5 : subtype = resp->subtype;
254 :
255 5 : if (version != EAP_SAKE_VERSION) {
256 0 : wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version);
257 0 : return TRUE;
258 : }
259 :
260 5 : 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 5 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype);
267 :
268 5 : if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY)
269 0 : return FALSE;
270 :
271 5 : if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE)
272 3 : 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 0 : 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 3 : 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 3 : if (data->state != CHALLENGE)
310 1 : return;
311 :
312 3 : wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge");
313 :
314 3 : if (eap_sake_parse_attributes(payload, payloadlen, &attr))
315 0 : return;
316 :
317 3 : 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 3 : os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN);
324 :
325 3 : os_free(data->peerid);
326 3 : data->peerid = NULL;
327 3 : data->peerid_len = 0;
328 3 : if (attr.peerid) {
329 3 : data->peerid = os_malloc(attr.peerid_len);
330 3 : if (data->peerid == NULL)
331 0 : return;
332 3 : os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
333 3 : data->peerid_len = attr.peerid_len;
334 : }
335 :
336 6 : if (sm->user == NULL || sm->user->password == NULL ||
337 3 : 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 3 : eap_sake_derive_keys(sm->user->password,
345 3 : sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
346 3 : data->rand_s, data->rand_p,
347 3 : (u8 *) &data->tek, data->msk, data->emsk);
348 :
349 9 : eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
350 : sm->server_id, sm->server_id_len,
351 3 : data->peerid, data->peerid_len, 1,
352 3 : wpabuf_head(respData), wpabuf_len(respData),
353 : attr.mic_p, mic_p);
354 3 : if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
355 1 : wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
356 1 : eap_sake_state(data, FAILURE);
357 1 : 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 6 : 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 5 : static void eap_sake_process(struct eap_sm *sm, void *priv,
410 : struct wpabuf *respData)
411 : {
412 5 : 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 5 : pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
419 5 : if (pos == NULL || len < sizeof(struct eap_sake_hdr))
420 5 : return;
421 :
422 5 : resp = (struct eap_sake_hdr *) pos;
423 5 : end = pos + len;
424 5 : subtype = resp->subtype;
425 5 : pos = (u8 *) (resp + 1);
426 :
427 5 : wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
428 5 : pos, end - pos);
429 :
430 5 : 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 3 : eap_sake_process_challenge(sm, data, respData, pos, end - pos);
436 3 : 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 6 : static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
449 : {
450 6 : struct eap_sake_data *data = priv;
451 6 : return data->state == SUCCESS || data->state == FAILURE;
452 : }
453 :
454 :
455 3 : static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
456 : {
457 3 : struct eap_sake_data *data = priv;
458 : u8 *key;
459 :
460 3 : if (data->state != SUCCESS)
461 1 : 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 4 : static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
492 : {
493 4 : struct eap_sake_data *data = priv;
494 4 : 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 : }
|