7ae4db0d6a7ece7e60f30002171b9bead7ceaa5a
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_pwd / rlm_eap_pwd.c
1 /*
2  * Copyright (c) Dan Harkins, 2012
3  *
4  *  Copyright holder grants permission for redistribution and use in source 
5  *  and binary forms, with or without modification, provided that the 
6  *  following conditions are met:
7  *     1. Redistribution of source code must retain the above copyright
8  *        notice, this list of conditions, and the following disclaimer
9  *        in all source files.
10  *     2. Redistribution in binary form must retain the above copyright
11  *        notice, this list of conditions, and the following disclaimer
12  *        in the documentation and/or other materials provided with the
13  *        distribution.
14  *
15  *  "DISCLAIMER OF LIABILITY
16  *  
17  *  THIS SOFTWARE IS PROVIDED BY DAN HARKINS ``AS IS'' AND
18  *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
19  *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
20  *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INDUSTRIAL LOUNGE BE LIABLE
21  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
23  *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  *  SUCH DAMAGE."
28  *
29  * This license and distribution terms cannot be changed. In other words,
30  * this code cannot simply be copied and put under a different distribution
31  * license (including the GNU public license).
32  */
33
34 #include <freeradius-devel/ident.h>
35 RCSID("$Id$")
36
37 #include "rlm_eap_pwd.h"
38
39 #include "eap_pwd.h"
40
41 #define MSK_EMSK_LEN    64
42 #define MPPE_KEY_LEN    32
43
44 static CONF_PARSER pwd_module_config[] = {
45     { "group", PW_TYPE_INTEGER,
46       offsetof(EAP_PWD_CONF, group), NULL, "19"},
47     { "fragment_size", PW_TYPE_INTEGER,
48       offsetof(EAP_PWD_CONF, fragment_size), NULL, "1020"},
49     { "server_id", PW_TYPE_STRING_PTR,
50       offsetof(EAP_PWD_CONF, server_id), NULL, NULL },
51     { "virtual_server", PW_TYPE_STRING_PTR,
52       offsetof(EAP_PWD_CONF, virtual_server), NULL, NULL },
53     { NULL, -1, 0, NULL, NULL }
54 };
55
56 /*
57  * stolen from rlm_eap_sim: Add value pair to reply
58  */
59 static void add_reply(VALUE_PAIR** vp,
60                       const char* name, const uint8_t *value, size_t len)
61 {
62     VALUE_PAIR *reply_attr;
63     reply_attr = pairmake(name, "", T_OP_EQ);
64     if (!reply_attr) {
65         DEBUG("rlm_eap_pwd: "
66               "add_reply failed to create attribute %s: %s\n",
67               name, fr_strerror());
68         return;
69     }
70
71     memcpy(reply_attr->vp_strvalue, value, len);
72     reply_attr->length = len;
73     pairadd(vp, reply_attr);
74 }
75
76 static int
77 eap_pwd_detach (void *arg)
78 {
79     EAP_PWD_CONF *conf;
80     eap_pwd_t *inst;
81
82     inst = (eap_pwd_t *) arg;
83
84     if (inst->bnctx) {
85         BN_CTX_free(inst->bnctx);
86     }
87
88     return 0;
89 }
90
91 static int
92 eap_pwd_attach (CONF_SECTION *cs, void **instance)
93 {
94     eap_pwd_t *inst;
95
96     *instance = inst = talloc_zero(cs, eap_pwd_t);
97     if (!inst) return -1;
98
99     inst->conf = talloc_zero(inst, EAP_PWD_CONF);
100     if (!inst->conf) return -1;
101
102     if (cf_section_parse(cs, inst->conf, pwd_module_config) < 0) {
103         return -1;
104     }
105
106     if ((inst->bnctx = BN_CTX_new()) == NULL) {
107         radlog(L_ERR, "rlm_eap_pwd: failed to get BN context!");
108         return -1;
109     }
110
111     return 0;
112 }
113
114 static void
115 free_session (void *data)
116 {
117     pwd_session_t *session = (pwd_session_t *)data;
118
119     if (session == NULL) {
120         radlog(L_ERR, "rlm_eap_pwd: freeing a NULL session...naughty, naughty");
121         return;
122     }
123     BN_free(session->private_value);
124     BN_free(session->peer_scalar);
125     BN_free(session->my_scalar);
126     BN_free(session->k);
127     EC_POINT_free(session->my_element);
128     EC_POINT_free(session->peer_element);
129     EC_GROUP_free(session->group);
130     EC_POINT_free(session->pwe);
131     BN_free(session->order);
132     BN_free(session->prime);
133
134     free(session);
135 }
136
137 static int
138 send_pwd_request (pwd_session_t *sess, EAP_DS *eap_ds)
139 {
140     int len;
141     uint16_t totlen;
142     pwd_hdr *hdr;
143
144     len = (sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr);
145     rad_assert(len > 0);
146     eap_ds->request->code = PW_EAP_REQUEST;
147     eap_ds->request->type.type = PW_EAP_PWD;
148     eap_ds->request->type.length = (len > sess->mtu) ? sess->mtu : len;
149     eap_ds->request->type.data = malloc(eap_ds->request->type.length);
150     memset(eap_ds->request->type.data, 0, eap_ds->request->type.length);
151
152     hdr = (pwd_hdr *)eap_ds->request->type.data;
153     switch (sess->state) {
154         case PWD_STATE_ID_REQ:
155             EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_ID);
156             break;
157         case PWD_STATE_COMMIT:
158             EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_COMMIT);
159             break;
160         case PWD_STATE_CONFIRM:
161             EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_CONFIRM);
162             break;
163         default:
164             DEBUG2("pwd state is messed up! Cannot send request");
165             return 0;
166     }
167     /*
168      * are we fragmenting?
169      */
170     if ((int)((sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr)) > sess->mtu) {
171         EAP_PWD_SET_MORE_BIT(hdr);
172         if (sess->out_buf_pos == 0) {
173             /*
174              * the first fragment, add the total length
175              */
176             EAP_PWD_SET_LENGTH_BIT(hdr);
177             totlen = ntohs(sess->out_buf_len);
178             memcpy(hdr->data, (char *)&totlen, sizeof(totlen));
179             memcpy(hdr->data + sizeof(uint16_t),
180                    sess->out_buf,
181                    sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
182             sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
183         } else {
184             /*
185              * an intermediate fragment
186              */
187             memcpy(hdr->data, sess->out_buf + sess->out_buf_pos, (sess->mtu - sizeof(pwd_hdr)));
188             sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr));
189         }
190     } else {
191         /*
192          * either it's not a fragment or it's the last fragment. 
193          * The out buffer isn't needed anymore though so get rid of it.
194          */
195         memcpy(hdr->data, sess->out_buf + sess->out_buf_pos, 
196                (sess->out_buf_len - sess->out_buf_pos));
197         free(sess->out_buf);
198         sess->out_buf = NULL;
199         sess->out_buf_pos = sess->out_buf_len = 0;
200     }
201     return 1;
202 }
203
204 static int
205 eap_pwd_initiate (void *type_data, EAP_HANDLER *handler)
206 {
207     pwd_session_t *pwd_session;
208     eap_pwd_t *inst = (eap_pwd_t *)type_data;
209     VALUE_PAIR *vp;
210     pwd_id_packet *pack;
211     
212     if (!inst || !handler) {
213         radlog(L_ERR, "rlm_eap_pwd: initiate, NULL data provided");
214         return -1;
215     }
216
217     /*
218      * make sure the server's been configured properly
219      */
220     if (inst->conf->server_id == NULL) {
221         radlog(L_ERR, "rlm_eap_pwd: server ID is not configured!");
222         return -1;
223     }
224     switch (inst->conf->group) {
225         case 19:
226         case 20:
227         case 21:
228         case 25:
229         case 26:
230             break;
231         default:
232             radlog(L_ERR, "rlm_eap_pwd: group is not supported!");
233             return -1;
234     }
235
236     if ((pwd_session = (pwd_session_t *)malloc(sizeof(*pwd_session))) == NULL) {
237         radlog(L_ERR, "rlm_eap_pwd: initiate, out of memory (1)");
238         return -1;
239     }
240     /*
241      * set things up so they can be free'd reliably
242      */
243     pwd_session->group_num = inst->conf->group;
244     pwd_session->private_value = NULL;
245     pwd_session->peer_scalar = NULL;
246     pwd_session->my_scalar = NULL;
247     pwd_session->k = NULL;
248     pwd_session->my_element = NULL;
249     pwd_session->peer_element = NULL;
250     pwd_session->group = NULL;
251     pwd_session->pwe = NULL;
252     pwd_session->order = NULL;
253     pwd_session->prime = NULL;
254
255     /*
256      * figure out the MTU (basically do what eap-tls does)
257      */
258     pwd_session->mtu = inst->conf->fragment_size;
259     vp = pairfind(handler->request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY);
260     if (vp && ((int)(vp->vp_integer - 9) < pwd_session->mtu)) {
261         /*
262          * 9 = 4 (EAPOL header) + 4 (EAP header) + 1 (EAP type)
263          *
264          * the fragmentation code deals with the included length
265          * so we don't need to subtract that here.
266          */
267         pwd_session->mtu = vp->vp_integer - 9;
268     }
269
270     pwd_session->state = PWD_STATE_ID_REQ;
271     pwd_session->in_buf = NULL;
272     pwd_session->out_buf_pos = 0;
273     handler->opaque = pwd_session;
274     handler->free_opaque = free_session;
275
276     /*
277      * construct an EAP-pwd-ID/Request
278      */
279     pwd_session->out_buf_len = sizeof(pwd_id_packet) + strlen(inst->conf->server_id);
280     if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) {
281         radlog(L_ERR, "rlm_eap_pwd: initiate, out of memory to send ID request");
282         return -1;
283     }
284     memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
285
286     pack = (pwd_id_packet *)pwd_session->out_buf;
287     pack->group_num = htons(pwd_session->group_num);
288     pack->random_function = EAP_PWD_DEF_RAND_FUN;
289     pack->prf = EAP_PWD_DEF_PRF;
290     pwd_session->token = random();
291     memcpy(pack->token, (char *)&pwd_session->token, 4);
292     pack->prep = EAP_PWD_PREP_NONE;
293     strcpy(pack->identity, inst->conf->server_id);
294
295     handler->stage = AUTHENTICATE;
296
297     return send_pwd_request(pwd_session, handler->eap_ds);
298 }
299
300 static int
301 eap_pwd_authenticate (void *arg, EAP_HANDLER *handler)
302 {
303     pwd_session_t *pwd_session;
304     pwd_hdr *hdr;
305     pwd_id_packet *id;
306     EAP_PACKET *response;
307     REQUEST *request, *fake;
308     VALUE_PAIR *pw, **outvps, *vp;
309     EAP_DS *eap_ds;
310     int len, ret = 0;
311     eap_pwd_t *inst = (eap_pwd_t *)arg;
312     uint16_t offset;
313     uint8_t exch, *buf, *ptr, msk[MSK_EMSK_LEN], emsk[MSK_EMSK_LEN];
314     uint8_t peer_confirm[SHA256_DIGEST_LENGTH];
315     BIGNUM *x = NULL, *y = NULL;
316
317     if ((handler == NULL) || 
318         ((eap_ds = handler->eap_ds) == NULL) ||
319         (inst == NULL)) {
320         return 0;
321     }
322     pwd_session = (pwd_session_t *)handler->opaque;
323     request = handler->request;
324     response = handler->eap_ds->response;
325     hdr = (pwd_hdr *)response->type.data;
326
327     buf = hdr->data;
328     len = response->type.length - sizeof(pwd_hdr);
329
330     /*
331      * see if we're fragmenting, if so continue until we're done
332      */
333     if (pwd_session->out_buf_pos) {
334         if (len) {
335             RDEBUG2("pwd got something more than an ACK for a fragment");
336         }
337         return send_pwd_request(pwd_session, eap_ds);
338     }
339
340     /*
341      * the first fragment will have a total length, make a
342      * buffer to hold all the fragments
343      */
344     if (EAP_PWD_GET_LENGTH_BIT(hdr)) {
345         if (pwd_session->in_buf) {
346             RDEBUG2("pwd already alloced buffer for fragments");
347             return 0;
348         }
349         pwd_session->in_buf_len = ntohs(buf[0] * 256 | buf[1]);
350         if ((pwd_session->in_buf = malloc(pwd_session->in_buf_len)) == NULL) {
351             RDEBUG2("pwd cannot malloc %d buffer to hold fragments",
352                     pwd_session->in_buf_len);
353             return 0;
354         }
355         memset(pwd_session->in_buf, 0, pwd_session->in_buf_len);
356         pwd_session->in_buf_pos = 0;
357         buf += sizeof(uint16_t);
358         len -= sizeof(uint16_t);
359     }
360
361     /*
362      * all fragments, including the 1st will have the M(ore) bit set,
363      * buffer those fragments!
364      */
365     if (EAP_PWD_GET_MORE_BIT(hdr)) {
366         rad_assert(pwd_session->in_buf != NULL);
367         if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) {
368             RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent.");
369             return 0;
370         }
371         memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len);
372         pwd_session->in_buf_pos += len;
373
374         /*
375          * send back an ACK for this fragment
376          */
377         exch = EAP_PWD_GET_EXCHANGE(hdr);
378         eap_ds->request->code = PW_EAP_REQUEST;
379         eap_ds->request->type.type = PW_EAP_PWD;
380         eap_ds->request->type.length = sizeof(pwd_hdr);
381         if ((eap_ds->request->type.data = malloc(sizeof(pwd_hdr))) == NULL) {
382             radlog(L_ERR, "rlm_eap_pwd: fragment ACK, out of memory");
383             return 0;
384         }
385         hdr = (pwd_hdr *)eap_ds->request->type.data;
386         EAP_PWD_SET_EXCHANGE(hdr, exch);
387         return 1;
388
389     }
390
391     if (pwd_session->in_buf) {
392         /*
393          * the last fragment...
394          */
395         if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) {
396             RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent.");
397             return 0;
398         }
399         memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len);
400         buf = pwd_session->in_buf;
401         len = pwd_session->in_buf_len;
402     }
403
404     switch (pwd_session->state) {
405         case PWD_STATE_ID_REQ:
406             if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_ID) {
407                 RDEBUG2("pwd exchange is incorrect: not ID");
408                 return 0;
409             }
410             id = (pwd_id_packet *)buf;
411             if ((id->prf != EAP_PWD_DEF_PRF) ||
412                 (id->random_function != EAP_PWD_DEF_RAND_FUN) ||
413                 (id->prep != EAP_PWD_PREP_NONE) ||
414                 (memcmp(id->token, (char *)&pwd_session->token, 4)) ||
415                 (id->group_num != ntohs(pwd_session->group_num))) {
416                 RDEBUG2("pwd id response is invalid");
417                 return 0;
418             }
419             /*
420              * we've agreed on the ciphersuite, record it...
421              */
422             ptr = (uint8_t *)&pwd_session->ciphersuite;
423             memcpy(ptr, (char *)&id->group_num, sizeof(uint16_t));
424             ptr += sizeof(uint16_t);
425             *ptr = EAP_PWD_DEF_RAND_FUN;
426             ptr += sizeof(uint8_t);
427             *ptr = EAP_PWD_DEF_PRF;
428             
429             pwd_session->peer_id_len = len - sizeof(pwd_id_packet);
430             if (pwd_session->peer_id_len >= sizeof(pwd_session->peer_id)) {
431                 RDEBUG2("pwd id response is malformed");
432                 return 0;
433             }
434             memcpy(pwd_session->peer_id, id->identity,
435                     pwd_session->peer_id_len);
436             pwd_session->peer_id[pwd_session->peer_id_len] = '\0';
437
438             /*
439              * make fake request to get the password for the usable ID
440              */
441             if ((fake = request_alloc_fake(handler->request)) == NULL) {
442                 RDEBUG("pwd unable to create fake request!");
443                 return 0;
444             }
445             if ((fake->username = pairmake("User-Name", "", T_OP_EQ)) == NULL) {
446                 RDEBUG("pwd unanable to create value pair for username!");
447                 request_free(&fake);
448                 return 0;
449             }
450             memcpy(fake->username->vp_strvalue, pwd_session->peer_id, 
451                    pwd_session->peer_id_len);
452             fake->username->length = pwd_session->peer_id_len;
453             fake->username->vp_strvalue[fake->username->length] = 0;
454
455             if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
456                     fake->server = vp->vp_strvalue;
457                     
458             } else if (inst->conf->virtual_server) {
459                     fake->server = inst->conf->virtual_server;
460                     
461             } /* else fake->server == request->server */
462             
463             if ((debug_flag > 0) && fr_log_fp) {
464                     RDEBUG("Sending tunneled request");
465                     
466                     debug_pair_list(fake->packet->vps);
467                     
468                     fprintf(fr_log_fp, "server %s {\n",
469                             (fake->server == NULL) ? "" : fake->server);
470             }
471             
472             /*
473              *  Call authorization recursively, which will
474              *  get the password.
475              */
476             module_authorize(0, fake);
477             
478             /*
479              *  Note that we don't do *anything* with the reply
480              *  attributes.
481              */
482             if ((debug_flag > 0) && fr_log_fp) {
483                     fprintf(fr_log_fp, "} # server %s\n",
484                             (fake->server == NULL) ? "" : fake->server);
485                     
486                     RDEBUG("Got tunneled reply code %d", fake->reply->code);
487                     
488                     debug_pair_list(fake->reply->vps);
489             }
490
491             if ((pw = pairfind(fake->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL) {
492                 DEBUG2("failed to find password for %s to do pwd authentication",
493                        pwd_session->peer_id);
494                 request_free(&fake);
495                 return 0;
496             }
497
498             if (compute_password_element(pwd_session, pwd_session->group_num,
499                                          pw->data.strvalue, strlen(pw->data.strvalue),
500                                          inst->conf->server_id, strlen(inst->conf->server_id),
501                                          pwd_session->peer_id, strlen(pwd_session->peer_id),
502                                          &pwd_session->token)) {
503                 DEBUG2("failed to obtain password element :-(");
504                 request_free(&fake);
505                 return 0;
506             }
507             request_free(&fake);
508
509             /*
510              * compute our scalar and element
511              */
512             if (compute_scalar_element(pwd_session, inst->bnctx)) {
513                 DEBUG2("failed to compute server's scalar and element");
514                 return 0;
515             }
516             if (((x = BN_new()) == NULL) ||
517                 ((y = BN_new()) == NULL)) {
518                 DEBUG2("server point allocation failed");
519                 return 0;
520             }
521             /*
522              * element is a point, get both coordinates: x and y
523              */
524             if (!EC_POINT_get_affine_coordinates_GFp(pwd_session->group,
525                                                      pwd_session->my_element, x, y,
526                                                      inst->bnctx)) {
527                 DEBUG2("server point assignment failed");
528                 BN_free(x);
529                 BN_free(y);
530                 return 0;
531             }
532             /*
533              * construct request
534              */
535             pwd_session->out_buf_len = BN_num_bytes(pwd_session->order) + 
536                 (2 * BN_num_bytes(pwd_session->prime));
537             if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) {
538                 radlog(L_ERR, "rlm_eap_pwd: out of memory to send commit");
539                 return 0;
540             }
541             memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
542
543             ptr = pwd_session->out_buf;
544             offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(x);
545             BN_bn2bin(x, ptr + offset);
546
547             ptr += BN_num_bytes(pwd_session->prime);
548             offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(y);
549             BN_bn2bin(y, ptr + offset);
550             
551             ptr += BN_num_bytes(pwd_session->prime);
552             offset = BN_num_bytes(pwd_session->order) - BN_num_bytes(pwd_session->my_scalar);
553             BN_bn2bin(pwd_session->my_scalar, ptr + offset);
554
555             pwd_session->state = PWD_STATE_COMMIT;
556             ret = send_pwd_request(pwd_session, eap_ds);
557             break;
558         case PWD_STATE_COMMIT:
559             if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_COMMIT) {
560                 RDEBUG2("pwd exchange is incorrect: not commit!");
561                 return 0;
562             }
563             /*
564              * process the peer's commit and generate the shared key, k
565              */
566             if (process_peer_commit(pwd_session, buf, inst->bnctx)) {
567                 RDEBUG2("failed to process peer's commit");
568                 return 0;
569             }
570
571             /*
572              * compute our confirm blob
573              */
574             if (compute_server_confirm(pwd_session, pwd_session->my_confirm, inst->bnctx)) {
575                 radlog(L_ERR, "rlm_eap_pwd: failed to compute confirm!");
576                 return 0;
577             }
578             /*
579              * construct a response...which is just our confirm blob
580              */
581             pwd_session->out_buf_len = SHA256_DIGEST_LENGTH;
582             if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) {
583                 radlog(L_ERR, "rlm_eap_pwd: out of memory to send confirm");
584                 return 0;
585             }
586             memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
587             memcpy(pwd_session->out_buf, pwd_session->my_confirm, SHA256_DIGEST_LENGTH);
588
589             pwd_session->state = PWD_STATE_CONFIRM;
590             ret = send_pwd_request(pwd_session, eap_ds);
591             break;
592         case PWD_STATE_CONFIRM:
593             if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_CONFIRM) {
594                 RDEBUG2("pwd exchange is incorrect: not commit!");
595                 return 0;
596             }
597             if (compute_peer_confirm(pwd_session, peer_confirm, inst->bnctx)) {
598                 RDEBUG2("pwd exchange cannot compute peer's confirm");
599                 return 0;
600             }
601             if (memcmp(peer_confirm, buf, SHA256_DIGEST_LENGTH)) {
602                 RDEBUG2("pwd exchange fails: peer confirm is incorrect!");
603                 return 0;
604             }
605             if (compute_keys(pwd_session, peer_confirm, msk, emsk)) {
606                 RDEBUG2("pwd exchange cannot generate (E)MSK!");
607                 return 0;
608             }
609             eap_ds->request->code = PW_EAP_SUCCESS;
610             /*
611              * return the MSK (in halves)
612              */
613             outvps = &handler->request->reply->vps;
614             add_reply(outvps, "MS-MPPE-Recv-Key", msk, MPPE_KEY_LEN);
615             add_reply(outvps, "MS-MPPE-Send-Key", msk+MPPE_KEY_LEN, MPPE_KEY_LEN);
616             ret = 1;
617             break;
618         default:
619             RDEBUG2("unknown PWD state");
620             return 0;
621     }
622
623     /*
624      * we processed the buffered fragments, get rid of them
625      */
626     if (pwd_session->in_buf) {
627         free(pwd_session->in_buf);
628         pwd_session->in_buf = NULL;
629     }
630
631     return ret;
632 }
633
634
635 EAP_TYPE rlm_eap_pwd = {
636     "eap_pwd",
637     eap_pwd_attach,                     /* attach */
638     eap_pwd_initiate,                   /* initiate to a client */
639     NULL,                               /* no authorization */
640     eap_pwd_authenticate,               /* pwd authentication */
641     eap_pwd_detach                      /* detach */
642 };
643