Line data Source code
1 : /*
2 : * EAP peer method: EAP-pwd (RFC 5931)
3 : * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
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/sha256.h"
13 : #include "crypto/ms_funcs.h"
14 : #include "eap_peer/eap_i.h"
15 : #include "eap_common/eap_pwd_common.h"
16 :
17 :
18 : struct eap_pwd_data {
19 : enum {
20 : PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req,
21 : SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE
22 : } state;
23 : u8 *id_peer;
24 : size_t id_peer_len;
25 : u8 *id_server;
26 : size_t id_server_len;
27 : u8 *password;
28 : size_t password_len;
29 : int password_hash;
30 : u16 group_num;
31 : EAP_PWD_group *grp;
32 :
33 : struct wpabuf *inbuf;
34 : size_t in_frag_pos;
35 : struct wpabuf *outbuf;
36 : size_t out_frag_pos;
37 : size_t mtu;
38 :
39 : BIGNUM *k;
40 : BIGNUM *private_value;
41 : BIGNUM *server_scalar;
42 : BIGNUM *my_scalar;
43 : EC_POINT *my_element;
44 : EC_POINT *server_element;
45 :
46 : u8 msk[EAP_MSK_LEN];
47 : u8 emsk[EAP_EMSK_LEN];
48 : u8 session_id[1 + SHA256_MAC_LEN];
49 :
50 : BN_CTX *bnctx;
51 : };
52 :
53 :
54 : #ifndef CONFIG_NO_STDOUT_DEBUG
55 114 : static const char * eap_pwd_state_txt(int state)
56 : {
57 114 : switch (state) {
58 : case PWD_ID_Req:
59 16 : return "PWD-ID-Req";
60 : case PWD_Commit_Req:
61 28 : return "PWD-Commit-Req";
62 : case PWD_Confirm_Req:
63 28 : return "PWD-Confirm-Req";
64 : case SUCCESS_ON_FRAG_COMPLETION:
65 26 : return "SUCCESS_ON_FRAG_COMPLETION";
66 : case SUCCESS:
67 13 : return "SUCCESS";
68 : case FAILURE:
69 3 : return "FAILURE";
70 : default:
71 0 : return "PWD-UNK";
72 : }
73 : }
74 : #endif /* CONFIG_NO_STDOUT_DEBUG */
75 :
76 :
77 57 : static void eap_pwd_state(struct eap_pwd_data *data, int state)
78 : {
79 114 : wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s",
80 57 : eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
81 57 : data->state = state;
82 57 : }
83 :
84 :
85 16 : static void * eap_pwd_init(struct eap_sm *sm)
86 : {
87 : struct eap_pwd_data *data;
88 : const u8 *identity, *password;
89 : size_t identity_len, password_len;
90 : int fragment_size;
91 : int pwhash;
92 :
93 16 : password = eap_get_config_password2(sm, &password_len, &pwhash);
94 16 : if (password == NULL) {
95 0 : wpa_printf(MSG_INFO, "EAP-PWD: No password configured!");
96 0 : return NULL;
97 : }
98 :
99 16 : identity = eap_get_config_identity(sm, &identity_len);
100 16 : if (identity == NULL) {
101 0 : wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!");
102 0 : return NULL;
103 : }
104 :
105 16 : if ((data = os_zalloc(sizeof(*data))) == NULL) {
106 0 : wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail");
107 0 : return NULL;
108 : }
109 :
110 16 : if ((data->bnctx = BN_CTX_new()) == NULL) {
111 0 : wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
112 0 : os_free(data);
113 0 : return NULL;
114 : }
115 :
116 16 : if ((data->id_peer = os_malloc(identity_len)) == NULL) {
117 0 : wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
118 0 : BN_CTX_free(data->bnctx);
119 0 : os_free(data);
120 0 : return NULL;
121 : }
122 :
123 16 : os_memcpy(data->id_peer, identity, identity_len);
124 16 : data->id_peer_len = identity_len;
125 :
126 16 : if ((data->password = os_malloc(password_len)) == NULL) {
127 0 : wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
128 0 : BN_CTX_free(data->bnctx);
129 0 : bin_clear_free(data->id_peer, data->id_peer_len);
130 0 : os_free(data);
131 0 : return NULL;
132 : }
133 16 : os_memcpy(data->password, password, password_len);
134 16 : data->password_len = password_len;
135 16 : data->password_hash = pwhash;
136 :
137 16 : data->out_frag_pos = data->in_frag_pos = 0;
138 16 : data->inbuf = data->outbuf = NULL;
139 16 : fragment_size = eap_get_config_fragment_size(sm);
140 16 : if (fragment_size <= 0)
141 0 : data->mtu = 1020; /* default from RFC 5931 */
142 : else
143 16 : data->mtu = fragment_size;
144 :
145 16 : data->state = PWD_ID_Req;
146 :
147 16 : return data;
148 : }
149 :
150 :
151 16 : static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
152 : {
153 16 : struct eap_pwd_data *data = priv;
154 :
155 16 : BN_clear_free(data->private_value);
156 16 : BN_clear_free(data->server_scalar);
157 16 : BN_clear_free(data->my_scalar);
158 16 : BN_clear_free(data->k);
159 16 : BN_CTX_free(data->bnctx);
160 16 : EC_POINT_clear_free(data->my_element);
161 16 : EC_POINT_clear_free(data->server_element);
162 16 : bin_clear_free(data->id_peer, data->id_peer_len);
163 16 : bin_clear_free(data->id_server, data->id_server_len);
164 16 : bin_clear_free(data->password, data->password_len);
165 16 : if (data->grp) {
166 15 : EC_GROUP_free(data->grp->group);
167 15 : EC_POINT_clear_free(data->grp->pwe);
168 15 : BN_clear_free(data->grp->order);
169 15 : BN_clear_free(data->grp->prime);
170 15 : os_free(data->grp);
171 : }
172 16 : wpabuf_free(data->inbuf);
173 16 : wpabuf_free(data->outbuf);
174 16 : bin_clear_free(data, sizeof(*data));
175 16 : }
176 :
177 :
178 13 : static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
179 : {
180 13 : struct eap_pwd_data *data = priv;
181 : u8 *key;
182 :
183 13 : if (data->state != SUCCESS)
184 0 : return NULL;
185 :
186 13 : key = os_malloc(EAP_MSK_LEN);
187 13 : if (key == NULL)
188 0 : return NULL;
189 :
190 13 : os_memcpy(key, data->msk, EAP_MSK_LEN);
191 13 : *len = EAP_MSK_LEN;
192 :
193 13 : return key;
194 : }
195 :
196 :
197 13 : static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
198 : {
199 13 : struct eap_pwd_data *data = priv;
200 : u8 *id;
201 :
202 13 : if (data->state != SUCCESS)
203 0 : return NULL;
204 :
205 13 : id = os_malloc(1 + SHA256_MAC_LEN);
206 13 : if (id == NULL)
207 0 : return NULL;
208 :
209 13 : os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
210 13 : *len = 1 + SHA256_MAC_LEN;
211 :
212 13 : return id;
213 : }
214 :
215 :
216 : static void
217 16 : eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
218 : struct eap_method_ret *ret,
219 : const struct wpabuf *reqData,
220 : const u8 *payload, size_t payload_len)
221 : {
222 : struct eap_pwd_id *id;
223 : const u8 *password;
224 : size_t password_len;
225 : u8 pwhashhash[16];
226 : int res;
227 :
228 16 : if (data->state != PWD_ID_Req) {
229 0 : ret->ignore = TRUE;
230 0 : eap_pwd_state(data, FAILURE);
231 0 : return;
232 : }
233 :
234 16 : if (payload_len < sizeof(struct eap_pwd_id)) {
235 0 : ret->ignore = TRUE;
236 0 : eap_pwd_state(data, FAILURE);
237 0 : return;
238 : }
239 :
240 16 : id = (struct eap_pwd_id *) payload;
241 16 : data->group_num = be_to_host16(id->group_num);
242 64 : wpa_printf(MSG_DEBUG,
243 : "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u",
244 64 : data->group_num, id->random_function, id->prf, id->prep);
245 32 : if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
246 16 : (id->prf != EAP_PWD_DEFAULT_PRF)) {
247 0 : ret->ignore = TRUE;
248 0 : eap_pwd_state(data, FAILURE);
249 0 : return;
250 : }
251 :
252 18 : if (id->prep != EAP_PWD_PREP_NONE &&
253 2 : id->prep != EAP_PWD_PREP_MS) {
254 0 : wpa_printf(MSG_DEBUG,
255 : "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)",
256 0 : id->prep);
257 0 : eap_pwd_state(data, FAILURE);
258 0 : return;
259 : }
260 :
261 16 : if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) {
262 1 : wpa_printf(MSG_DEBUG,
263 : "EAP-PWD: Unhashed password not available");
264 1 : eap_pwd_state(data, FAILURE);
265 1 : return;
266 : }
267 :
268 15 : wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d",
269 15 : data->group_num);
270 :
271 15 : data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
272 15 : if (data->id_server == NULL) {
273 0 : wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
274 0 : eap_pwd_state(data, FAILURE);
275 0 : return;
276 : }
277 15 : data->id_server_len = payload_len - sizeof(struct eap_pwd_id);
278 15 : os_memcpy(data->id_server, id->identity, data->id_server_len);
279 30 : wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
280 15 : data->id_server, data->id_server_len);
281 :
282 15 : data->grp = os_zalloc(sizeof(EAP_PWD_group));
283 15 : if (data->grp == NULL) {
284 0 : wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
285 : "group");
286 0 : eap_pwd_state(data, FAILURE);
287 0 : return;
288 : }
289 :
290 15 : if (id->prep == EAP_PWD_PREP_MS) {
291 : #ifdef CONFIG_FIPS
292 : wpa_printf(MSG_ERROR,
293 : "EAP-PWD (peer): MS password hash not supported in FIPS mode");
294 : eap_pwd_state(data, FAILURE);
295 : return;
296 : #else /* CONFIG_FIPS */
297 2 : if (data->password_hash) {
298 1 : res = hash_nt_password_hash(data->password, pwhashhash);
299 : } else {
300 : u8 pwhash[16];
301 :
302 1 : res = nt_password_hash(data->password,
303 : data->password_len, pwhash);
304 1 : if (res == 0)
305 1 : res = hash_nt_password_hash(pwhash, pwhashhash);
306 1 : os_memset(pwhash, 0, sizeof(pwhash));
307 : }
308 :
309 2 : if (res) {
310 0 : eap_pwd_state(data, FAILURE);
311 0 : return;
312 : }
313 :
314 2 : password = pwhashhash;
315 2 : password_len = sizeof(pwhashhash);
316 : #endif /* CONFIG_FIPS */
317 : } else {
318 13 : password = data->password;
319 13 : password_len = data->password_len;
320 : }
321 :
322 : /* compute PWE */
323 45 : res = compute_password_element(data->grp, data->group_num,
324 : password, password_len,
325 15 : data->id_server, data->id_server_len,
326 15 : data->id_peer, data->id_peer_len,
327 15 : id->token);
328 15 : os_memset(pwhashhash, 0, sizeof(pwhashhash));
329 15 : if (res) {
330 1 : wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
331 1 : eap_pwd_state(data, FAILURE);
332 1 : return;
333 : }
334 :
335 14 : wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
336 14 : BN_num_bits(data->grp->prime));
337 :
338 14 : data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
339 14 : data->id_peer_len);
340 14 : if (data->outbuf == NULL) {
341 0 : eap_pwd_state(data, FAILURE);
342 0 : return;
343 : }
344 14 : wpabuf_put_be16(data->outbuf, data->group_num);
345 14 : wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
346 14 : wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
347 14 : wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
348 14 : wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
349 14 : wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
350 :
351 14 : eap_pwd_state(data, PWD_Commit_Req);
352 : }
353 :
354 :
355 : static void
356 14 : eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
357 : struct eap_method_ret *ret,
358 : const struct wpabuf *reqData,
359 : const u8 *payload, size_t payload_len)
360 : {
361 14 : EC_POINT *K = NULL, *point = NULL;
362 14 : BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
363 : u16 offset;
364 14 : u8 *ptr, *scalar = NULL, *element = NULL;
365 : size_t prime_len, order_len;
366 :
367 14 : if (data->state != PWD_Commit_Req) {
368 0 : ret->ignore = TRUE;
369 0 : goto fin;
370 : }
371 :
372 14 : prime_len = BN_num_bytes(data->grp->prime);
373 14 : order_len = BN_num_bytes(data->grp->order);
374 :
375 14 : if (payload_len != 2 * prime_len + order_len) {
376 0 : wpa_printf(MSG_INFO,
377 : "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
378 : (unsigned int) payload_len,
379 : (unsigned int) (2 * prime_len + order_len));
380 0 : goto fin;
381 : }
382 :
383 28 : if (((data->private_value = BN_new()) == NULL) ||
384 28 : ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
385 14 : ((cofactor = BN_new()) == NULL) ||
386 28 : ((data->my_scalar = BN_new()) == NULL) ||
387 : ((mask = BN_new()) == NULL)) {
388 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
389 0 : goto fin;
390 : }
391 :
392 14 : if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
393 0 : wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
394 : "for curve");
395 0 : goto fin;
396 : }
397 :
398 28 : if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
399 28 : BN_rand_range(mask, data->grp->order) != 1 ||
400 28 : BN_add(data->my_scalar, data->private_value, mask) != 1 ||
401 14 : BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
402 : data->bnctx) != 1) {
403 0 : wpa_printf(MSG_INFO,
404 : "EAP-pwd (peer): unable to get randomness");
405 0 : goto fin;
406 : }
407 :
408 28 : if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
409 14 : data->grp->pwe, mask, data->bnctx)) {
410 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
411 : "fail");
412 0 : eap_pwd_state(data, FAILURE);
413 0 : goto fin;
414 : }
415 :
416 14 : if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
417 : {
418 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
419 0 : goto fin;
420 : }
421 14 : BN_clear_free(mask);
422 :
423 14 : if (((x = BN_new()) == NULL) ||
424 : ((y = BN_new()) == NULL)) {
425 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
426 0 : goto fin;
427 : }
428 :
429 : /* process the request */
430 28 : if (((data->server_scalar = BN_new()) == NULL) ||
431 28 : ((data->k = BN_new()) == NULL) ||
432 28 : ((K = EC_POINT_new(data->grp->group)) == NULL) ||
433 28 : ((point = EC_POINT_new(data->grp->group)) == NULL) ||
434 14 : ((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
435 : {
436 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
437 : "fail");
438 0 : goto fin;
439 : }
440 :
441 : /* element, x then y, followed by scalar */
442 14 : ptr = (u8 *) payload;
443 14 : BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
444 14 : ptr += BN_num_bytes(data->grp->prime);
445 14 : BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
446 14 : ptr += BN_num_bytes(data->grp->prime);
447 14 : BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
448 14 : if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
449 : data->server_element, x, y,
450 : data->bnctx)) {
451 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
452 : "fail");
453 0 : goto fin;
454 : }
455 :
456 : /* check to ensure server's element is not in a small sub-group */
457 14 : if (BN_cmp(cofactor, BN_value_one())) {
458 0 : if (!EC_POINT_mul(data->grp->group, point, NULL,
459 0 : data->server_element, cofactor, NULL)) {
460 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
461 : "server element by order!\n");
462 0 : goto fin;
463 : }
464 0 : if (EC_POINT_is_at_infinity(data->grp->group, point)) {
465 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
466 : "is at infinity!\n");
467 0 : goto fin;
468 : }
469 : }
470 :
471 : /* compute the shared key, k */
472 28 : if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
473 28 : data->server_scalar, data->bnctx)) ||
474 14 : (!EC_POINT_add(data->grp->group, K, K, data->server_element,
475 14 : data->bnctx)) ||
476 14 : (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
477 : data->bnctx))) {
478 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
479 : "fail");
480 0 : goto fin;
481 : }
482 :
483 : /* ensure that the shared key isn't in a small sub-group */
484 14 : if (BN_cmp(cofactor, BN_value_one())) {
485 0 : if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
486 : NULL)) {
487 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
488 : "shared key point by order");
489 0 : goto fin;
490 : }
491 : }
492 :
493 : /*
494 : * This check is strictly speaking just for the case above where
495 : * co-factor > 1 but it was suggested that even though this is probably
496 : * never going to happen it is a simple and safe check "just to be
497 : * sure" so let's be safe.
498 : */
499 14 : if (EC_POINT_is_at_infinity(data->grp->group, K)) {
500 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
501 : "infinity!\n");
502 0 : goto fin;
503 : }
504 :
505 14 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
506 : NULL, data->bnctx)) {
507 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
508 : "shared secret from point");
509 0 : goto fin;
510 : }
511 :
512 : /* now do the response */
513 28 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
514 14 : data->my_element, x, y,
515 : data->bnctx)) {
516 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
517 0 : goto fin;
518 : }
519 :
520 28 : if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
521 14 : ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
522 : NULL)) {
523 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
524 0 : goto fin;
525 : }
526 :
527 : /*
528 : * bignums occupy as little memory as possible so one that is
529 : * sufficiently smaller than the prime or order might need pre-pending
530 : * with zeros.
531 : */
532 14 : os_memset(scalar, 0, BN_num_bytes(data->grp->order));
533 14 : os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
534 28 : offset = BN_num_bytes(data->grp->order) -
535 14 : BN_num_bytes(data->my_scalar);
536 14 : BN_bn2bin(data->my_scalar, scalar + offset);
537 :
538 14 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
539 14 : BN_bn2bin(x, element + offset);
540 14 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
541 14 : BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
542 :
543 28 : data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) +
544 14 : 2 * BN_num_bytes(data->grp->prime));
545 14 : if (data->outbuf == NULL)
546 0 : goto fin;
547 :
548 : /* we send the element as (x,y) follwed by the scalar */
549 14 : wpabuf_put_data(data->outbuf, element,
550 14 : 2 * BN_num_bytes(data->grp->prime));
551 14 : wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
552 :
553 : fin:
554 14 : os_free(scalar);
555 14 : os_free(element);
556 14 : BN_clear_free(x);
557 14 : BN_clear_free(y);
558 14 : BN_clear_free(cofactor);
559 14 : EC_POINT_clear_free(K);
560 14 : EC_POINT_clear_free(point);
561 14 : if (data->outbuf == NULL)
562 0 : eap_pwd_state(data, FAILURE);
563 : else
564 14 : eap_pwd_state(data, PWD_Confirm_Req);
565 14 : }
566 :
567 :
568 : static void
569 14 : eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
570 : struct eap_method_ret *ret,
571 : const struct wpabuf *reqData,
572 : const u8 *payload, size_t payload_len)
573 : {
574 14 : BIGNUM *x = NULL, *y = NULL;
575 : struct crypto_hash *hash;
576 : u32 cs;
577 : u16 grp;
578 14 : u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
579 : int offset;
580 :
581 14 : if (data->state != PWD_Confirm_Req) {
582 0 : ret->ignore = TRUE;
583 0 : goto fin;
584 : }
585 :
586 14 : if (payload_len != SHA256_MAC_LEN) {
587 0 : wpa_printf(MSG_INFO,
588 : "EAP-pwd: Unexpected Confirm payload length %u (expected %u)",
589 : (unsigned int) payload_len, SHA256_MAC_LEN);
590 0 : goto fin;
591 : }
592 :
593 : /*
594 : * first build up the ciphersuite which is group | random_function |
595 : * prf
596 : */
597 14 : grp = htons(data->group_num);
598 14 : ptr = (u8 *) &cs;
599 14 : os_memcpy(ptr, &grp, sizeof(u16));
600 14 : ptr += sizeof(u16);
601 14 : *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
602 14 : ptr += sizeof(u8);
603 14 : *ptr = EAP_PWD_DEFAULT_PRF;
604 :
605 : /* each component of the cruft will be at most as big as the prime */
606 14 : if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
607 14 : ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
608 0 : wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation "
609 : "fail");
610 0 : goto fin;
611 : }
612 :
613 : /*
614 : * server's commit is H(k | server_element | server_scalar |
615 : * peer_element | peer_scalar | ciphersuite)
616 : */
617 14 : hash = eap_pwd_h_init();
618 14 : if (hash == NULL)
619 0 : goto fin;
620 :
621 : /*
622 : * zero the memory each time because this is mod prime math and some
623 : * value may start with a few zeros and the previous one did not.
624 : */
625 14 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
626 14 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
627 14 : BN_bn2bin(data->k, cruft + offset);
628 14 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
629 :
630 : /* server element: x, y */
631 28 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
632 14 : data->server_element, x, y,
633 : data->bnctx)) {
634 0 : wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
635 : "assignment fail");
636 0 : goto fin;
637 : }
638 14 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
639 14 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
640 14 : BN_bn2bin(x, cruft + offset);
641 14 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
642 14 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
643 14 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
644 14 : BN_bn2bin(y, cruft + offset);
645 14 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
646 :
647 : /* server scalar */
648 14 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
649 28 : offset = BN_num_bytes(data->grp->order) -
650 14 : BN_num_bytes(data->server_scalar);
651 14 : BN_bn2bin(data->server_scalar, cruft + offset);
652 14 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
653 :
654 : /* my element: x, y */
655 28 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
656 14 : data->my_element, x, y,
657 : data->bnctx)) {
658 0 : wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
659 : "assignment fail");
660 0 : goto fin;
661 : }
662 :
663 14 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
664 14 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
665 14 : BN_bn2bin(x, cruft + offset);
666 14 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
667 14 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
668 14 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
669 14 : BN_bn2bin(y, cruft + offset);
670 14 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
671 :
672 : /* my scalar */
673 14 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
674 28 : offset = BN_num_bytes(data->grp->order) -
675 14 : BN_num_bytes(data->my_scalar);
676 14 : BN_bn2bin(data->my_scalar, cruft + offset);
677 14 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
678 :
679 : /* the ciphersuite */
680 14 : eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
681 :
682 : /* random function fin */
683 14 : eap_pwd_h_final(hash, conf);
684 :
685 14 : ptr = (u8 *) payload;
686 14 : if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
687 1 : wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
688 1 : goto fin;
689 : }
690 :
691 13 : wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified");
692 :
693 : /*
694 : * compute confirm:
695 : * H(k | peer_element | peer_scalar | server_element | server_scalar |
696 : * ciphersuite)
697 : */
698 13 : hash = eap_pwd_h_init();
699 13 : if (hash == NULL)
700 0 : goto fin;
701 :
702 : /* k */
703 13 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
704 13 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
705 13 : BN_bn2bin(data->k, cruft + offset);
706 13 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
707 :
708 : /* my element */
709 26 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
710 13 : data->my_element, x, y,
711 : data->bnctx)) {
712 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
713 : "assignment fail");
714 0 : goto fin;
715 : }
716 13 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
717 13 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
718 13 : BN_bn2bin(x, cruft + offset);
719 13 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
720 13 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
721 13 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
722 13 : BN_bn2bin(y, cruft + offset);
723 13 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
724 :
725 : /* my scalar */
726 13 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
727 26 : offset = BN_num_bytes(data->grp->order) -
728 13 : BN_num_bytes(data->my_scalar);
729 13 : BN_bn2bin(data->my_scalar, cruft + offset);
730 13 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
731 :
732 : /* server element: x, y */
733 26 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
734 13 : data->server_element, x, y,
735 : data->bnctx)) {
736 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
737 : "assignment fail");
738 0 : goto fin;
739 : }
740 13 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
741 13 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
742 13 : BN_bn2bin(x, cruft + offset);
743 13 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
744 13 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
745 13 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
746 13 : BN_bn2bin(y, cruft + offset);
747 13 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
748 :
749 : /* server scalar */
750 13 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
751 26 : offset = BN_num_bytes(data->grp->order) -
752 13 : BN_num_bytes(data->server_scalar);
753 13 : BN_bn2bin(data->server_scalar, cruft + offset);
754 13 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
755 :
756 : /* the ciphersuite */
757 13 : eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
758 :
759 : /* all done */
760 13 : eap_pwd_h_final(hash, conf);
761 :
762 13 : if (compute_keys(data->grp, data->bnctx, data->k,
763 13 : data->my_scalar, data->server_scalar, conf, ptr,
764 13 : &cs, data->msk, data->emsk, data->session_id) < 0) {
765 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
766 : "EMSK");
767 0 : goto fin;
768 : }
769 :
770 13 : data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
771 13 : if (data->outbuf == NULL)
772 0 : goto fin;
773 :
774 13 : wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
775 :
776 : fin:
777 14 : bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
778 14 : BN_clear_free(x);
779 14 : BN_clear_free(y);
780 14 : if (data->outbuf == NULL) {
781 1 : ret->methodState = METHOD_DONE;
782 1 : ret->decision = DECISION_FAIL;
783 1 : eap_pwd_state(data, FAILURE);
784 : } else {
785 13 : eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
786 : }
787 14 : }
788 :
789 :
790 : static struct wpabuf *
791 56 : eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
792 : const struct wpabuf *reqData)
793 : {
794 56 : struct eap_pwd_data *data = priv;
795 56 : struct wpabuf *resp = NULL;
796 : const u8 *pos, *buf;
797 : size_t len;
798 56 : u16 tot_len = 0;
799 : u8 lm_exch;
800 :
801 56 : pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
802 56 : if ((pos == NULL) || (len < 1)) {
803 0 : wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and "
804 : "len is %d",
805 : pos == NULL ? "NULL" : "not NULL", (int) len);
806 0 : ret->ignore = TRUE;
807 0 : return NULL;
808 : }
809 :
810 56 : ret->ignore = FALSE;
811 56 : ret->methodState = METHOD_MAY_CONT;
812 56 : ret->decision = DECISION_FAIL;
813 56 : ret->allowNotifications = FALSE;
814 :
815 56 : lm_exch = *pos;
816 56 : pos++; /* skip over the bits and the exch */
817 56 : len--;
818 :
819 : /*
820 : * we're fragmenting so send out the next fragment
821 : */
822 56 : if (data->out_frag_pos) {
823 : /*
824 : * this should be an ACK
825 : */
826 10 : if (len)
827 0 : wpa_printf(MSG_INFO, "Bad Response! Fragmenting but "
828 : "not an ACK");
829 :
830 10 : wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment");
831 : /*
832 : * check if there are going to be more fragments
833 : */
834 10 : len = wpabuf_len(data->outbuf) - data->out_frag_pos;
835 10 : if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
836 5 : len = data->mtu - EAP_PWD_HDR_SIZE;
837 5 : EAP_PWD_SET_MORE_BIT(lm_exch);
838 : }
839 10 : resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
840 : EAP_PWD_HDR_SIZE + len,
841 10 : EAP_CODE_RESPONSE, eap_get_id(reqData));
842 10 : if (resp == NULL) {
843 0 : wpa_printf(MSG_INFO, "Unable to allocate memory for "
844 : "next fragment!");
845 0 : return NULL;
846 : }
847 10 : wpabuf_put_u8(resp, lm_exch);
848 10 : buf = wpabuf_head_u8(data->outbuf);
849 10 : wpabuf_put_data(resp, buf + data->out_frag_pos, len);
850 10 : data->out_frag_pos += len;
851 : /*
852 : * this is the last fragment so get rid of the out buffer
853 : */
854 10 : if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
855 5 : wpabuf_free(data->outbuf);
856 5 : data->outbuf = NULL;
857 5 : data->out_frag_pos = 0;
858 : }
859 20 : wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes",
860 10 : data->out_frag_pos == 0 ? "last" : "next",
861 : (int) len);
862 10 : if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
863 1 : ret->methodState = METHOD_DONE;
864 1 : ret->decision = DECISION_UNCOND_SUCC;
865 1 : eap_pwd_state(data, SUCCESS);
866 : }
867 10 : return resp;
868 : }
869 :
870 : /*
871 : * see if this is a fragment that needs buffering
872 : *
873 : * if it's the first fragment there'll be a length field
874 : */
875 46 : if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
876 1 : if (len < 2) {
877 0 : wpa_printf(MSG_DEBUG,
878 : "EAP-pwd: Frame too short to contain Total-Length field");
879 0 : ret->ignore = TRUE;
880 0 : return NULL;
881 : }
882 1 : tot_len = WPA_GET_BE16(pos);
883 1 : wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose "
884 : "total length = %d", tot_len);
885 1 : if (tot_len > 15000)
886 0 : return NULL;
887 1 : if (data->inbuf) {
888 0 : wpa_printf(MSG_DEBUG,
889 : "EAP-pwd: Unexpected new fragment start when previous fragment is still in use");
890 0 : ret->ignore = TRUE;
891 0 : return NULL;
892 : }
893 1 : data->inbuf = wpabuf_alloc(tot_len);
894 1 : if (data->inbuf == NULL) {
895 0 : wpa_printf(MSG_INFO, "Out of memory to buffer "
896 : "fragments!");
897 0 : return NULL;
898 : }
899 1 : data->in_frag_pos = 0;
900 1 : pos += sizeof(u16);
901 1 : len -= sizeof(u16);
902 : }
903 : /*
904 : * buffer and ACK the fragment
905 : */
906 46 : if (EAP_PWD_GET_MORE_BIT(lm_exch)) {
907 2 : data->in_frag_pos += len;
908 2 : if (data->in_frag_pos > wpabuf_size(data->inbuf)) {
909 0 : wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack "
910 : "detected (%d vs. %d)!",
911 0 : (int) data->in_frag_pos,
912 0 : (int) wpabuf_len(data->inbuf));
913 0 : wpabuf_free(data->inbuf);
914 0 : data->inbuf = NULL;
915 0 : data->in_frag_pos = 0;
916 0 : return NULL;
917 : }
918 2 : wpabuf_put_data(data->inbuf, pos, len);
919 :
920 2 : resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
921 : EAP_PWD_HDR_SIZE,
922 2 : EAP_CODE_RESPONSE, eap_get_id(reqData));
923 2 : if (resp != NULL)
924 2 : wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch)));
925 2 : wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment",
926 : (int) len);
927 2 : return resp;
928 : }
929 : /*
930 : * we're buffering and this is the last fragment
931 : */
932 44 : if (data->in_frag_pos) {
933 1 : wpabuf_put_data(data->inbuf, pos, len);
934 1 : wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
935 : (int) len);
936 1 : data->in_frag_pos += len;
937 1 : pos = wpabuf_head_u8(data->inbuf);
938 1 : len = data->in_frag_pos;
939 : }
940 44 : wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d",
941 : EAP_PWD_GET_EXCHANGE(lm_exch), (int) len);
942 :
943 44 : switch (EAP_PWD_GET_EXCHANGE(lm_exch)) {
944 : case EAP_PWD_OPCODE_ID_EXCH:
945 16 : eap_pwd_perform_id_exchange(sm, data, ret, reqData,
946 : pos, len);
947 16 : break;
948 : case EAP_PWD_OPCODE_COMMIT_EXCH:
949 14 : eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
950 : pos, len);
951 14 : break;
952 : case EAP_PWD_OPCODE_CONFIRM_EXCH:
953 14 : eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
954 : pos, len);
955 14 : break;
956 : default:
957 0 : wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
958 : "opcode %d", lm_exch);
959 0 : break;
960 : }
961 : /*
962 : * if we buffered the just processed input now's the time to free it
963 : */
964 44 : if (data->in_frag_pos) {
965 1 : wpabuf_free(data->inbuf);
966 1 : data->inbuf = NULL;
967 1 : data->in_frag_pos = 0;
968 : }
969 :
970 44 : if (data->outbuf == NULL) {
971 3 : ret->methodState = METHOD_DONE;
972 3 : ret->decision = DECISION_FAIL;
973 3 : return NULL; /* generic failure */
974 : }
975 :
976 : /*
977 : * we have output! Do we need to fragment it?
978 : */
979 41 : lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch);
980 41 : len = wpabuf_len(data->outbuf);
981 41 : if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
982 5 : resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu,
983 5 : EAP_CODE_RESPONSE, eap_get_id(reqData));
984 : /*
985 : * if so it's the first so include a length field
986 : */
987 5 : EAP_PWD_SET_LENGTH_BIT(lm_exch);
988 5 : EAP_PWD_SET_MORE_BIT(lm_exch);
989 5 : tot_len = len;
990 : /*
991 : * keep the packet at the MTU
992 : */
993 5 : len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16);
994 5 : wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total "
995 : "length = %d", tot_len);
996 : } else {
997 36 : resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
998 : EAP_PWD_HDR_SIZE + len,
999 36 : EAP_CODE_RESPONSE, eap_get_id(reqData));
1000 : }
1001 41 : if (resp == NULL)
1002 0 : return NULL;
1003 :
1004 41 : wpabuf_put_u8(resp, lm_exch);
1005 41 : if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
1006 5 : wpabuf_put_be16(resp, tot_len);
1007 5 : data->out_frag_pos += len;
1008 : }
1009 41 : buf = wpabuf_head_u8(data->outbuf);
1010 41 : wpabuf_put_data(resp, buf, len);
1011 : /*
1012 : * if we're not fragmenting then there's no need to carry this around
1013 : */
1014 41 : if (data->out_frag_pos == 0) {
1015 36 : wpabuf_free(data->outbuf);
1016 36 : data->outbuf = NULL;
1017 36 : data->out_frag_pos = 0;
1018 36 : if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
1019 12 : ret->methodState = METHOD_DONE;
1020 12 : ret->decision = DECISION_UNCOND_SUCC;
1021 12 : eap_pwd_state(data, SUCCESS);
1022 : }
1023 : }
1024 :
1025 41 : return resp;
1026 : }
1027 :
1028 :
1029 56 : static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv)
1030 : {
1031 56 : struct eap_pwd_data *data = priv;
1032 56 : return data->state == SUCCESS;
1033 : }
1034 :
1035 :
1036 1 : static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
1037 : {
1038 1 : struct eap_pwd_data *data = priv;
1039 : u8 *key;
1040 :
1041 1 : if (data->state != SUCCESS)
1042 0 : return NULL;
1043 :
1044 1 : if ((key = os_malloc(EAP_EMSK_LEN)) == NULL)
1045 0 : return NULL;
1046 :
1047 1 : os_memcpy(key, data->emsk, EAP_EMSK_LEN);
1048 1 : *len = EAP_EMSK_LEN;
1049 :
1050 1 : return key;
1051 : }
1052 :
1053 :
1054 49 : int eap_peer_pwd_register(void)
1055 : {
1056 : struct eap_method *eap;
1057 : int ret;
1058 :
1059 49 : eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
1060 : EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
1061 49 : if (eap == NULL)
1062 0 : return -1;
1063 :
1064 49 : eap->init = eap_pwd_init;
1065 49 : eap->deinit = eap_pwd_deinit;
1066 49 : eap->process = eap_pwd_process;
1067 49 : eap->isKeyAvailable = eap_pwd_key_available;
1068 49 : eap->getKey = eap_pwd_getkey;
1069 49 : eap->getSessionId = eap_pwd_get_session_id;
1070 49 : eap->get_emsk = eap_pwd_get_emsk;
1071 :
1072 49 : ret = eap_peer_method_register(eap);
1073 49 : if (ret)
1074 0 : eap_peer_method_free(eap);
1075 49 : return ret;
1076 : }
|