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