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