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