bdf4668d6855ccbf6edc55eaf56ecfab8ee1a180
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_mschapv2 / rlm_eap_mschapv2.c
1 /*
2  * rlm_eap_mschapv2.c    Handles that are called from eap
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2003,2006  The FreeRADIUS server project
21  */
22
23 #include <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include <freeradius-devel/autoconf.h>
27
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 #include "eap_mschapv2.h"
32
33 #include <freeradius-devel/rad_assert.h>
34
35 typedef struct rlm_eap_mschapv2_t {
36         int with_ntdomain_hack;
37 } rlm_eap_mschapv2_t;
38
39 static CONF_PARSER module_config[] = {
40         { "with_ntdomain_hack",     PW_TYPE_BOOLEAN,
41           offsetof(rlm_eap_mschapv2_t,with_ntdomain_hack), NULL, "no" },
42
43         { NULL, -1, 0, NULL, NULL }             /* end the list */
44 };
45
46
47 static void fix_mppe_keys(EAP_HANDLER *handler, mschapv2_opaque_t *data)
48 {
49         pairmove2(&data->mppe_keys, &handler->request->reply->vps, ((311 << 16) | 7));
50         pairmove2(&data->mppe_keys, &handler->request->reply->vps, ((311 << 16) | 8));
51         pairmove2(&data->mppe_keys, &handler->request->reply->vps, ((311 << 16) | 16));
52         pairmove2(&data->mppe_keys, &handler->request->reply->vps, ((311 << 16) | 17));
53   
54 }
55
56 static void free_data(void *ptr)
57 {
58         mschapv2_opaque_t *data = ptr;
59
60         pairfree(&data->mppe_keys);
61         free(data);
62 }
63
64 /*
65  *      Detach the module.
66  */
67 static int mschapv2_detach(void *arg)
68 {
69         rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg;
70
71         free(inst);
72
73         return 0;
74 }
75
76
77 /*
78  *      Attach the module.
79  */
80 static int mschapv2_attach(CONF_SECTION *cs, void **instance)
81 {
82         rlm_eap_mschapv2_t *inst;
83
84         inst = malloc(sizeof(*inst));
85         if (!inst) {
86                 radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
87                 return -1;
88         }
89         memset(inst, 0, sizeof(*inst));
90
91         /*
92          *      Parse the configuration attributes.
93          */
94         if (cf_section_parse(cs, inst, module_config) < 0) {
95                 mschapv2_detach(inst);
96                 return -1;
97         }
98
99         *instance = inst;
100
101         return 0;
102 }
103
104
105 /*
106  *      Compose the response.
107  */
108 static int eapmschapv2_compose(EAP_HANDLER *handler, VALUE_PAIR *reply)
109 {
110         uint8_t *ptr;
111         int16_t length;
112         mschapv2_header_t *hdr;
113         EAP_DS *eap_ds = handler->eap_ds;
114
115         eap_ds->request->code = PW_EAP_REQUEST;
116         eap_ds->request->type.type = PW_EAP_MSCHAPV2;
117
118         switch (reply->attribute) {
119         case PW_MSCHAP_CHALLENGE:
120                 /*
121                  *   0                   1                   2                   3
122                  *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
123                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
124                  *  |     Code      |   Identifier  |            Length             |
125                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
126                  *  |     Type      |   OpCode      |  MS-CHAPv2-ID |  MS-Length...
127                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
128                  *  |   MS-Length   |  Value-Size   |  Challenge...
129                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
130                  *  |                             Challenge...
131                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
132                  *  |                             Name...
133                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
134                  */
135                 length = MSCHAPV2_HEADER_LEN + MSCHAPV2_CHALLENGE_LEN + strlen(handler->identity);
136                 eap_ds->request->type.data = malloc(length);
137                 /*
138                  *      Allocate room for the EAP-MS-CHAPv2 data.
139                  */
140                 if (eap_ds->request->type.data == NULL) {
141                         radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
142                         return 0;
143                 }
144                 eap_ds->request->type.length = length;
145
146                 ptr = eap_ds->request->type.data;
147                 hdr = (mschapv2_header_t *) ptr;
148
149                 hdr->opcode = PW_EAP_MSCHAPV2_CHALLENGE;
150                 hdr->mschapv2_id = eap_ds->response->id + 1;
151                 length = htons(length);
152                 memcpy(hdr->ms_length, &length, sizeof(uint16_t));
153                 hdr->value_size = MSCHAPV2_CHALLENGE_LEN;
154
155                 ptr += MSCHAPV2_HEADER_LEN;
156
157                 /*
158                  *      Copy the Challenge, success, or error over.
159                  */
160                 memcpy(ptr, reply->vp_strvalue, reply->length);
161                 memcpy((ptr + reply->length), handler->identity, strlen(handler->identity));
162                 break;
163
164         case PW_MSCHAP2_SUCCESS:
165                 /*
166                  *   0                   1                   2                   3
167                  *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
168                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
169                  *  |     Code      |   Identifier  |            Length             |
170                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
171                  *  |     Type      |   OpCode      |  MS-CHAPv2-ID |  MS-Length...
172                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
173                  *  |   MS-Length   |                    Message...
174                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
175                  */
176                 DEBUG2("MSCHAP Success\n");
177                 length = 46;
178                 eap_ds->request->type.data = malloc(length);
179                 /*
180                  *      Allocate room for the EAP-MS-CHAPv2 data.
181                  */
182                 if (eap_ds->request->type.data == NULL) {
183                         radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
184                         return 0;
185                 }
186                 memset(eap_ds->request->type.data, 0, length);
187                 eap_ds->request->type.length = length;
188
189                 eap_ds->request->type.data[0] = PW_EAP_MSCHAPV2_SUCCESS;
190                 eap_ds->request->type.data[1] = eap_ds->response->id;
191                 length = htons(length);
192                 memcpy((eap_ds->request->type.data + 2), &length, sizeof(uint16_t));
193                 memcpy((eap_ds->request->type.data + 4), reply->vp_strvalue + 1, 42);
194                 break;
195
196         case PW_MSCHAP_ERROR:
197                 DEBUG2("MSCHAP Failure\n");
198                 length = 4 + MSCHAPV2_FAILURE_MESSAGE_LEN;
199                 eap_ds->request->type.data = malloc(length);
200
201                 /*
202                  *      Allocate room for the EAP-MS-CHAPv2 data.
203                  */
204                 if (eap_ds->request->type.data == NULL) {
205                         radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
206                         return 0;
207                 }
208                 memset(eap_ds->request->type.data, 0, length);
209                 eap_ds->request->type.length = length;
210
211                 eap_ds->request->type.data[0] = PW_EAP_MSCHAPV2_FAILURE;
212                 eap_ds->request->type.data[1] = eap_ds->response->id;
213                 length = htons(length);
214                 memcpy((eap_ds->request->type.data + 2), &length, sizeof(uint16_t));
215                 memcpy((eap_ds->request->type.data + 4), MSCHAPV2_FAILURE_MESSAGE, MSCHAPV2_FAILURE_MESSAGE_LEN);
216                 break;
217
218         default:
219                 radlog(L_ERR, "rlm_eap_mschapv2: Internal sanity check failed");
220                 return 0;
221                 break;
222         }
223
224         return 1;
225 }
226
227
228 /*
229  *      Initiate the EAP-MSCHAPV2 session by sending a challenge to the peer.
230  */
231 static int mschapv2_initiate(void *type_data, EAP_HANDLER *handler)
232 {
233         int             i;
234         VALUE_PAIR      *challenge;
235         mschapv2_opaque_t *data;
236
237         type_data = type_data;  /* -Wunused */
238
239         challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ);
240         if (!challenge) {
241                 radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
242                 return 0;
243         }
244
245         /*
246          *      Get a random challenge.
247          */
248         challenge->length = MSCHAPV2_CHALLENGE_LEN;
249         for (i = 0; i < MSCHAPV2_CHALLENGE_LEN; i++) {
250                 challenge->vp_strvalue[i] = fr_rand();
251         }
252         DEBUG2("rlm_eap_mschapv2: Issuing Challenge");
253
254         /*
255          *      Keep track of the challenge.
256          */
257         data = malloc(sizeof(mschapv2_opaque_t));
258         rad_assert(data != NULL);
259
260         /*
261          *      We're at the stage where we're challenging the user.
262          */
263         data->code = PW_EAP_MSCHAPV2_CHALLENGE;
264         memcpy(data->challenge, challenge->vp_strvalue, MSCHAPV2_CHALLENGE_LEN);
265         data->mppe_keys = NULL;
266
267         handler->opaque = data;
268         handler->free_opaque = free_data;
269
270         /*
271          *      Compose the EAP-MSCHAPV2 packet out of the data structure,
272          *      and free it.
273          */
274         eapmschapv2_compose(handler, challenge);
275         pairfree(&challenge);
276
277 #ifdef WITH_PROXY
278         /*
279          *      The EAP session doesn't have enough information to
280          *      proxy the "inside EAP" protocol.  Disable EAP proxying.
281          */
282         handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
283 #endif
284
285         /*
286          *      We don't need to authorize the user at this point.
287          *
288          *      We also don't need to keep the challenge, as it's
289          *      stored in 'handler->eap_ds', which will be given back
290          *      to us...
291          */
292         handler->stage = AUTHENTICATE;
293
294         return 1;
295 }
296
297 #ifdef WITH_PROXY
298 /*
299  *      Do post-proxy processing,
300  *      0 = fail
301  *      1 = OK.
302  *
303  *      Called from rlm_eap.c, eap_postproxy().
304  */
305 static int mschap_postproxy(EAP_HANDLER *handler, void *tunnel_data)
306 {
307         VALUE_PAIR *response = NULL;
308         mschapv2_opaque_t *data;
309
310         data = (mschapv2_opaque_t *) handler->opaque;
311         rad_assert(data != NULL);
312
313         tunnel_data = tunnel_data; /* -Wunused */
314
315         DEBUG2("  rlm_eap_mschapv2: Passing reply from proxy back into the tunnel %p %d.",
316                handler->request, handler->request->reply->code);
317
318         /*
319          *      There is only a limited number of possibilities.
320          */
321         switch (handler->request->reply->code) {
322         case PW_AUTHENTICATION_ACK:
323                 DEBUG("  rlm_eap_mschapv2: Authentication succeeded.");
324                 /*
325                  *      Move the attribute, so it doesn't go into
326                  *      the reply.
327                  */
328                 pairmove2(&response,
329                           &handler->request->reply->vps,
330                           PW_MSCHAP2_SUCCESS);
331                 break;
332
333         default:
334         case PW_AUTHENTICATION_REJECT:
335                 DEBUG("  rlm_eap_mschapv2: Authentication did not succeed.");
336                 return 0;
337         }
338
339         /*
340          *      No response, die.
341          */
342         if (!response) {
343                 radlog(L_ERR, "rlm_eap_mschapv2: No MS-CHAPv2-Success or MS-CHAP-Error was found.");
344                 return 0;
345         }
346
347         /*
348          *      Done doing EAP proxy stuff.
349          */
350         handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
351         eapmschapv2_compose(handler, response);
352         data->code = PW_EAP_MSCHAPV2_SUCCESS;
353
354         /*
355          *      Delete MPPE keys & encryption policy
356          *
357          *      FIXME: Use intelligent names...
358          */
359         fix_mppe_keys(handler, data);
360
361         /*
362          *      And we need to challenge the user, not ack/reject them,
363          *      so we re-write the ACK to a challenge.  Yuck.
364          */
365         handler->request->reply->code = PW_ACCESS_CHALLENGE;
366         pairfree(&response);
367
368         return 1;
369 }
370 #endif
371
372 /*
373  *      Authenticate a previously sent challenge.
374  */
375 static int mschapv2_authenticate(void *arg, EAP_HANDLER *handler)
376 {
377         int rcode;
378         mschapv2_opaque_t *data;
379         EAP_DS *eap_ds = handler->eap_ds;
380         VALUE_PAIR *challenge, *response, *name;
381
382         rad_assert(handler->request != NULL);
383         rad_assert(handler->stage == AUTHENTICATE);
384
385         data = (mschapv2_opaque_t *) handler->opaque;
386
387         /*
388          *      Sanity check the response.
389          */
390         if (eap_ds->response->length <= 4) {
391                 radlog(L_ERR, "rlm_eap_mschapv2: corrupted data");
392                 return 0;
393         }
394
395         /*
396          *      Switch over the MS-CHAP type.
397          */
398         switch (eap_ds->response->type.data[0]) {
399                 /*
400                  *      We should get an ACK from the client ONLY if we've
401                  *      sent them a SUCCESS packet.
402                  */
403                 case PW_EAP_MSCHAPV2_ACK:
404                 if (data->code != PW_EAP_MSCHAPV2_SUCCESS) {
405                         radlog(L_ERR, "rlm_eap_mschapv2: Unexpected ACK received");
406                         return 0;
407                 }
408
409                 /*
410                  *      It's a success.  Don't proxy it.
411                  */
412                 handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
413
414                 /*
415                  *      And upon receiving the clients ACK, we do nothing
416                  *      other than return EAP-Success, with no EAP-MS-CHAPv2
417                  *      data.
418                  */
419                 return 1;
420                 break;
421
422                 /*
423                  *      We should get a response ONLY after we've sent
424                  *      a challenge.
425                  */
426         case PW_EAP_MSCHAPV2_RESPONSE:
427                 if (data->code != PW_EAP_MSCHAPV2_CHALLENGE) {
428                         radlog(L_ERR, "rlm_eap_mschapv2: Unexpected response received");
429                         return 0;
430                 }
431
432                 /*
433                  *      Ensure that we have at least enough data
434                  *      to do the following checks.
435                  *
436                  *      EAP header (4), EAP type, MS-CHAP opcode,
437                  *      MS-CHAP ident, MS-CHAP data length (2),
438                  *      MS-CHAP value length.
439                  */
440                 if (eap_ds->response->length < (4 + 1 + 1 + 1 + 2 + 1)) {
441                         radlog(L_ERR, "rlm_eap_mschapv2: Response is too short");
442                         return 0;
443                 }
444
445                 /*
446                  *      The 'value_size' is the size of the response,
447                  *      which is supposed to be the response (48
448                  *      bytes) plus 1 byte of flags at the end.
449                  */
450                 if (eap_ds->response->type.data[4] != 49) {
451                         radlog(L_ERR, "rlm_eap_mschapv2: Response is of incorrect length %d", eap_ds->response->type.data[4]);
452                         return 0;
453                 }
454
455                 /*
456                  *      The MS-Length field is 5 + value_size + length
457                  *      of name, which is put after the response.
458                  */
459                 if (((eap_ds->response->type.data[2] << 8) |
460                      eap_ds->response->type.data[3]) < (5 + 49)) {
461                         radlog(L_ERR, "rlm_eap_mschapv2: Response contains contradictory length %d %d",
462                               (eap_ds->response->type.data[2] << 8) |
463                                eap_ds->response->type.data[3], 5 + 49);
464                         return 0;
465                 }
466                 break;
467
468         case PW_EAP_MSCHAPV2_SUCCESS:
469                 if (data->code != PW_EAP_MSCHAPV2_SUCCESS) {
470                         radlog(L_ERR, "rlm_eap_mschapv2: Unexpected success received");
471                         return 0;
472                 }
473
474 #ifdef WITH_PROXY
475                 /*
476                  *      It's a success.  Don't proxy it.
477                  */
478                 handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
479 #endif
480
481                 eap_ds->request->code = PW_EAP_SUCCESS;
482
483                 pairadd(&handler->request->reply->vps, data->mppe_keys);
484                 data->mppe_keys = NULL;
485                 return 1;
486                 break;
487
488                 /*
489                  *      Something else, we don't know what it is.
490                  */
491         default:
492                 radlog(L_ERR, "rlm_eap_mschapv2: Invalid response type %d",
493                        eap_ds->response->type.data[0]);
494                 return 0;
495         }
496
497         /*
498          *      We now know that the user has sent us a response
499          *      to the challenge.  Let's try to authenticate it.
500          *
501          *      We do this by taking the challenge from 'data',
502          *      the response from the EAP packet, and creating VALUE_PAIR's
503          *      to pass to the 'mschap' module.  This is a little wonky,
504          *      but it works.
505          */
506         challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ);
507         if (!challenge) {
508                 radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
509                 return 0;
510         }
511         challenge->length = MSCHAPV2_CHALLENGE_LEN;
512         memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN);
513
514         response = pairmake("MS-CHAP2-Response", "0x00", T_OP_EQ);
515         if (!response) {
516                 pairfree(&challenge);
517                 radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
518                 return 0;
519         }
520
521         response->length = MSCHAPV2_RESPONSE_LEN;
522         memcpy(response->vp_strvalue + 2, &eap_ds->response->type.data[5],
523                MSCHAPV2_RESPONSE_LEN - 2);
524         response->vp_strvalue[0] = eap_ds->response->type.data[1];
525         response->vp_strvalue[1] = eap_ds->response->type.data[5 + MSCHAPV2_RESPONSE_LEN];
526
527         name = pairmake("NTLM-User-Name", "", T_OP_EQ);
528         if (!name) {
529                 pairfree(&challenge);
530                 pairfree(&response);
531                 radlog(L_ERR, "rlm_eap_mschapv2: Failed creating NTLM-User-Name: %s", fr_strerror());
532                 return 0;
533         }
534         
535         /*
536          *      MS-Length - MS-Value - 5.
537          */
538         name->length = (((eap_ds->response->type.data[2] << 8) |
539                          eap_ds->response->type.data[3]) -
540                         eap_ds->response->type.data[4] - 5);
541         if (name->length >= sizeof(name->vp_strvalue)) {
542                 name->length = sizeof(name->vp_strvalue) - 1;
543         }
544
545         memcpy(name->vp_strvalue,
546                &eap_ds->response->type.data[4 + MSCHAPV2_RESPONSE_LEN],
547                name->length);
548         name->vp_strvalue[name->length] = '\0';
549
550         /*
551          *      Add the pairs to the request, and call the 'mschap'
552          *      module.
553          */
554         pairadd(&handler->request->packet->vps, challenge);
555         pairadd(&handler->request->packet->vps, response);
556         pairadd(&handler->request->packet->vps, name);
557
558 #ifdef WITH_PROXY
559         /*
560          *      If this options is set, then we do NOT authenticate the
561          *      user here.  Instead, now that we've added the MS-CHAP
562          *      attributes to the request, we STOP, and let the outer
563          *      tunnel code handle it.
564          *
565          *      This means that the outer tunnel code will DELETE the
566          *      EAP attributes, and proxy the MS-CHAP attributes to a
567          *      home server.
568          */
569         if (handler->request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
570                 char *username = NULL;
571                 eap_tunnel_data_t *tunnel;
572                 rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg;
573
574                 /*
575                  *      Set up the callbacks for the tunnel
576                  */
577                 tunnel = rad_malloc(sizeof(*tunnel));
578                 memset(tunnel, 0, sizeof(*tunnel));
579
580                 tunnel->tls_session = arg;
581                 tunnel->callback = mschap_postproxy;
582
583                 /*
584                  *      Associate the callback with the request.
585                  */
586                 rcode = request_data_add(handler->request,
587                                          handler->request->proxy,
588                                          REQUEST_DATA_EAP_TUNNEL_CALLBACK,
589                                          tunnel, free);
590                 rad_assert(rcode == 0);
591
592                 /*
593                  *      The State attribute is NOT supposed to
594                  *      go into the proxied packet, it will confuse
595                  *      other RADIUS servers, and they will discard
596                  *      the request.
597                  *
598                  *      The PEAP module will take care of adding
599                  *      the State attribute back, before passing
600                  *      the handler & request back into the tunnel.
601                  */
602                 pairdelete(&handler->request->packet->vps, PW_STATE);
603
604                 /*
605                  *      Fix the User-Name when proxying, to strip off
606                  *      the NT Domain, if we're told to, and a User-Name
607                  *      exists, and there's a \\, meaning an NT-Domain
608                  *      in the user name, THEN discard the user name.
609                  */
610                 if (inst->with_ntdomain_hack &&
611                     ((challenge = pairfind(handler->request->packet->vps,
612                                            PW_USER_NAME)) != NULL) &&
613                     ((username = strchr(challenge->vp_strvalue, '\\')) != NULL)) {
614                         /*
615                          *      Wipe out the NT domain.
616                          *
617                          *      FIXME: Put it into MS-CHAP-Domain?
618                          */
619                         username++; /* skip the \\ */
620                         memmove(challenge->vp_strvalue,
621                                 username,
622                                 strlen(username) + 1); /* include \0 */
623                         challenge->length = strlen(challenge->vp_strvalue);
624                 }
625
626                 /*
627                  *      Remember that in the post-proxy stage, we've got
628                  *      to do the work below, AFTER the call to MS-CHAP
629                  *      authentication...
630                  */
631                 return 1;
632         }
633 #endif
634
635         /*
636          *      This is a wild & crazy hack.
637          */
638         rcode = module_authenticate(PW_AUTHTYPE_MS_CHAP, handler->request);
639
640         /*
641          *      Delete MPPE keys & encryption policy.  We don't
642          *      want these here.
643          */
644         fix_mppe_keys(handler, data);
645
646         /*
647          *      Take the response from the mschap module, and
648          *      return success or failure, depending on the result.
649          */
650         response = NULL;
651         if (rcode == RLM_MODULE_OK) {
652                 pairmove2(&response, &handler->request->reply->vps,
653                          PW_MSCHAP2_SUCCESS);
654                 data->code = PW_EAP_MSCHAPV2_SUCCESS;
655         } else {
656                 /*
657                  *      Don't return anything in the error message.
658                  */
659                 eap_ds->request->code = PW_EAP_FAILURE;
660                 return 1;
661 #if 0
662                 pairmove2(&handler->request->reply->vps, &response
663                           PW_MSCHAP_ERROR);
664                 data->code = PW_EAP_MSCHAPV2_FAILURE;
665 #endif
666         }
667
668         /*
669          *      No response, die.
670          */
671         if (!response) {
672                 radlog(L_ERR, "rlm_eap_mschapv2: No MS-CHAPv2-Success or MS-CHAP-Error was found.");
673                 return 0;
674         }
675
676         /*
677          *      Compose the response (whatever it is),
678          *      and return it to the over-lying EAP module.
679          */
680         eapmschapv2_compose(handler, response);
681         pairfree(&response);
682
683         return 1;
684 }
685
686 /*
687  *      The module name should be the only globally exported symbol.
688  *      That is, everything else should be 'static'.
689  */
690 EAP_TYPE rlm_eap_mschapv2 = {
691         "eap_mschapv2",
692         mschapv2_attach,                /* attach */
693         mschapv2_initiate,              /* Start the initial request */
694         NULL,                           /* authorization */
695         mschapv2_authenticate,          /* authentication */
696         mschapv2_detach                 /* detach */
697 };