8a5bd4b45f56446c52d725ca3c00ea06144aa8d9
[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 RCSID("$Id$")
24
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include "eap_mschapv2.h"
29
30 #include <freeradius-devel/rad_assert.h>
31
32 typedef struct rlm_eap_mschapv2_t {
33         bool with_ntdomain_hack;
34         bool send_error;
35 } rlm_eap_mschapv2_t;
36
37 static CONF_PARSER module_config[] = {
38         { "with_ntdomain_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_mschapv2_t, with_ntdomain_hack), "no" },
39
40         { "send_error", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_mschapv2_t, send_error), "no" },
41
42         { NULL, -1, 0, NULL, NULL }             /* end the list */
43 };
44
45
46 static void fix_mppe_keys(eap_handler_t *handler, mschapv2_opaque_t *data)
47 {
48         pairfilter(data, &data->mppe_keys, &handler->request->reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
49         pairfilter(data, &data->mppe_keys, &handler->request->reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
50         pairfilter(data, &data->mppe_keys, &handler->request->reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
51         pairfilter(data, &data->mppe_keys, &handler->request->reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
52 }
53
54 /*
55  *      Attach the module.
56  */
57 static int mschapv2_attach(CONF_SECTION *cs, void **instance)
58 {
59         rlm_eap_mschapv2_t *inst;
60
61         *instance = inst = talloc_zero(cs, rlm_eap_mschapv2_t);
62         if (!inst) return -1;
63
64         /*
65          *      Parse the configuration attributes.
66          */
67         if (cf_section_parse(cs, inst, module_config) < 0) {
68                 return -1;
69         }
70
71         return 0;
72 }
73
74
75 /*
76  *      Compose the response.
77  */
78 static int eapmschapv2_compose(eap_handler_t *handler, VALUE_PAIR *reply)
79 {
80         uint8_t *ptr;
81         int16_t length;
82         mschapv2_header_t *hdr;
83         EAP_DS *eap_ds = handler->eap_ds;
84
85         eap_ds->request->code = PW_EAP_REQUEST;
86         eap_ds->request->type.num = PW_EAP_MSCHAPV2;
87
88         /*
89          *      Always called with vendor Microsoft
90          */
91         switch (reply->da->attr) {
92         case PW_MSCHAP_CHALLENGE:
93                 /*
94                  *   0                   1                   2                   3
95                  *   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
96                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
97                  *  |     Code      |   Identifier  |            Length             |
98                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99                  *  |     Type      |   OpCode      |  MS-CHAPv2-ID |  MS-Length...
100                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101                  *  |   MS-Length   |  Value-Size   |  Challenge...
102                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
103                  *  |                             Challenge...
104                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
105                  *  |                             Name...
106                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
107                  */
108                 length = MSCHAPV2_HEADER_LEN + MSCHAPV2_CHALLENGE_LEN + strlen(handler->identity);
109                 eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t, length);
110                 /*
111                  *      Allocate room for the EAP-MS-CHAPv2 data.
112                  */
113                 if (!eap_ds->request->type.data) {
114                         return 0;
115                 }
116                 eap_ds->request->type.length = length;
117
118                 ptr = eap_ds->request->type.data;
119                 hdr = (mschapv2_header_t *) ptr;
120
121                 hdr->opcode = PW_EAP_MSCHAPV2_CHALLENGE;
122                 hdr->mschapv2_id = eap_ds->response->id + 1;
123                 length = htons(length);
124                 memcpy(hdr->ms_length, &length, sizeof(uint16_t));
125                 hdr->value_size = MSCHAPV2_CHALLENGE_LEN;
126
127                 ptr += MSCHAPV2_HEADER_LEN;
128
129                 /*
130                  *      Copy the Challenge, success, or error over.
131                  */
132                 memcpy(ptr, reply->vp_octets, reply->vp_length);
133                 memcpy((ptr + reply->vp_length), handler->identity, strlen(handler->identity));
134                 break;
135
136         case PW_MSCHAP2_SUCCESS:
137                 /*
138                  *   0                   1                   2                   3
139                  *   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
140                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
141                  *  |     Code      |   Identifier  |            Length             |
142                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
143                  *  |     Type      |   OpCode      |  MS-CHAPv2-ID |  MS-Length...
144                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
145                  *  |   MS-Length   |                    Message...
146                  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
147                  */
148                 DEBUG2("MSCHAP Success\n");
149                 length = 46;
150                 eap_ds->request->type.data = talloc_array(eap_ds->request,
151                                                           uint8_t, length);
152                 /*
153                  *      Allocate room for the EAP-MS-CHAPv2 data.
154                  */
155                 if (!eap_ds->request->type.data) {
156                         return 0;
157                 }
158                 memset(eap_ds->request->type.data, 0, length);
159                 eap_ds->request->type.length = length;
160
161                 eap_ds->request->type.data[0] = PW_EAP_MSCHAPV2_SUCCESS;
162                 eap_ds->request->type.data[1] = eap_ds->response->id;
163                 length = htons(length);
164                 memcpy((eap_ds->request->type.data + 2), &length, sizeof(uint16_t));
165                 memcpy((eap_ds->request->type.data + 4), reply->vp_strvalue + 1, 42);
166                 break;
167
168         case PW_MSCHAP_ERROR:
169                 DEBUG2("MSCHAP Failure\n");
170                 length = 4 + reply->vp_length - 1;
171                 eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t, length);
172
173                 /*
174                  *      Allocate room for the EAP-MS-CHAPv2 data.
175                  */
176                 if (!eap_ds->request->type.data) {
177                         return 0;
178                 }
179                 memset(eap_ds->request->type.data, 0, length);
180                 eap_ds->request->type.length = length;
181
182                 eap_ds->request->type.data[0] = PW_EAP_MSCHAPV2_FAILURE;
183                 eap_ds->request->type.data[1] = eap_ds->response->id;
184                 length = htons(length);
185                 memcpy((eap_ds->request->type.data + 2), &length, sizeof(uint16_t));
186                 /*
187                  *      Copy the entire failure message.
188                  */
189                 memcpy((eap_ds->request->type.data + 4),
190                        reply->vp_strvalue + 1, reply->vp_length - 1);
191                 break;
192
193         default:
194                 ERROR("rlm_eap_mschapv2: Internal sanity check failed");
195                 return 0;
196         }
197
198         return 1;
199 }
200
201
202 /*
203  *      Initiate the EAP-MSCHAPV2 session by sending a challenge to the peer.
204  */
205 static int mschapv2_initiate(UNUSED void *instance, eap_handler_t *handler)
206 {
207         int             i;
208         VALUE_PAIR      *challenge;
209         mschapv2_opaque_t *data;
210         REQUEST         *request = handler->request;
211         uint8_t         *p;
212
213         challenge = pairmake(handler, NULL,
214                              "MS-CHAP-Challenge", NULL, T_OP_EQ);
215
216         /*
217          *      Get a random challenge.
218          */
219         challenge->vp_length = MSCHAPV2_CHALLENGE_LEN;
220         challenge->vp_octets = p = talloc_array(challenge, uint8_t, challenge->vp_length);
221         for (i = 0; i < MSCHAPV2_CHALLENGE_LEN; i++) {
222                 p[i] = fr_rand();
223         }
224         RDEBUG2("Issuing Challenge");
225
226         /*
227          *      Keep track of the challenge.
228          */
229         data = talloc_zero(handler, mschapv2_opaque_t);
230         rad_assert(data != NULL);
231
232         /*
233          *      We're at the stage where we're challenging the user.
234          */
235         data->code = PW_EAP_MSCHAPV2_CHALLENGE;
236         memcpy(data->challenge, challenge->vp_octets, MSCHAPV2_CHALLENGE_LEN);
237         data->mppe_keys = NULL;
238         data->reply = NULL;
239
240         handler->opaque = data;
241
242         /*
243          *      Compose the EAP-MSCHAPV2 packet out of the data structure,
244          *      and free it.
245          */
246         eapmschapv2_compose(handler, challenge);
247         pairfree(&challenge);
248
249 #ifdef WITH_PROXY
250         /*
251          *      The EAP session doesn't have enough information to
252          *      proxy the "inside EAP" protocol.  Disable EAP proxying.
253          */
254         handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
255 #endif
256
257         /*
258          *      We don't need to authorize the user at this point.
259          *
260          *      We also don't need to keep the challenge, as it's
261          *      stored in 'handler->eap_ds', which will be given back
262          *      to us...
263          */
264         handler->stage = AUTHENTICATE;
265
266         return 1;
267 }
268
269 #ifdef WITH_PROXY
270 /*
271  *      Do post-proxy processing,
272  *      0 = fail
273  *      1 = OK.
274  *
275  *      Called from rlm_eap.c, eap_postproxy().
276  */
277 static int CC_HINT(nonnull) mschap_postproxy(eap_handler_t *handler, UNUSED void *tunnel_data)
278 {
279         VALUE_PAIR *response = NULL;
280         mschapv2_opaque_t *data;
281         REQUEST *request = handler->request;
282
283         data = (mschapv2_opaque_t *) handler->opaque;
284         rad_assert(request != NULL);
285
286         RDEBUG2("Passing reply from proxy back into the tunnel %d.",
287                 request->reply->code);
288
289         /*
290          *      There is only a limited number of possibilities.
291          */
292         switch (request->reply->code) {
293         case PW_CODE_ACCESS_ACCEPT:
294                 RDEBUG2("Proxied authentication succeeded");
295
296                 /*
297                  *      Move the attribute, so it doesn't go into
298                  *      the reply.
299                  */
300                 pairfilter(data, &response, &request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
301                 break;
302
303         default:
304         case PW_CODE_ACCESS_REJECT:
305                 RDEBUG("Proxied authentication did not succeed");
306                 return 0;
307         }
308
309         /*
310          *      No response, die.
311          */
312         if (!response) {
313                 REDEBUG("Proxied reply contained no MS-CHAP2-Success or MS-CHAP-Error");
314                 return 0;
315         }
316
317         /*
318          *      Done doing EAP proxy stuff.
319          */
320         request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
321         eapmschapv2_compose(handler, response);
322         data->code = PW_EAP_MSCHAPV2_SUCCESS;
323
324         /*
325          *      Delete MPPE keys & encryption policy
326          *
327          *      FIXME: Use intelligent names...
328          */
329         fix_mppe_keys(handler, data);
330
331         /*
332          * save any other attributes for re-use in the final
333          * access-accept e.g. vlan, etc. This lets the PEAP
334          * use_tunneled_reply code work
335          */
336         data->reply = paircopy(data, request->reply->vps);
337
338         /*
339          *      And we need to challenge the user, not ack/reject them,
340          *      so we re-write the ACK to a challenge.  Yuck.
341          */
342         request->reply->code = PW_CODE_ACCESS_CHALLENGE;
343         pairfree(&response);
344
345         return 1;
346 }
347 #endif
348
349 /*
350  *      Authenticate a previously sent challenge.
351  */
352 static int CC_HINT(nonnull) mschapv2_authenticate(void *arg, eap_handler_t *handler)
353 {
354         int rcode, ccode;
355         uint8_t *p;
356         size_t length;
357         char *q;
358         mschapv2_opaque_t *data;
359         EAP_DS *eap_ds = handler->eap_ds;
360         VALUE_PAIR *challenge, *response, *name;
361         rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg;
362         REQUEST *request = handler->request;
363
364         rad_assert(handler->stage == AUTHENTICATE);
365
366         data = (mschapv2_opaque_t *) handler->opaque;
367
368         /*
369          *      Sanity check the response.
370          */
371         if (eap_ds->response->length <= 5) {
372                 REDEBUG("corrupted data");
373                 return 0;
374         }
375
376         ccode = eap_ds->response->type.data[0];
377
378         switch (data->code) {
379         case PW_EAP_MSCHAPV2_FAILURE:
380                 if (ccode == PW_EAP_MSCHAPV2_RESPONSE) {
381                         RDEBUG2("authentication re-try from client after we sent a failure");
382                         break;
383                 }
384
385                 /*
386                  * if we sent error 648 (password expired) to the client
387                  * we might get an MSCHAP-CPW packet here; turn it into a
388                  * regular MS-CHAP2-CPW packet and pass it to rlm_mschap
389                  * (or proxy it, I guess)
390                  */
391                 if (ccode == PW_EAP_MSCHAPV2_CHGPASSWD) {
392                         VALUE_PAIR *cpw;
393                         int mschap_id = eap_ds->response->type.data[1];
394                         int copied=0,seq=1;
395
396                         RDEBUG2("password change packet received");
397
398                         challenge = pairmake_packet("MS-CHAP-Challenge", NULL, T_OP_EQ);
399                         if (!challenge) {
400                                 return 0;
401                         }
402                         pairmemcpy(challenge, data->challenge, MSCHAPV2_CHALLENGE_LEN);
403
404                         cpw = pairmake_packet("MS-CHAP2-CPW", NULL, T_OP_EQ);
405                         cpw->vp_length = 68;
406
407                         cpw->vp_octets = p = talloc_array(cpw, uint8_t, cpw->vp_length);
408                         p[0] = 7;
409                         p[1] = mschap_id;
410                         memcpy(p + 2, eap_ds->response->type.data + 520, 66);
411
412                         /*
413                          * break the encoded password into VPs (3 of them)
414                          */
415                         while (copied < 516) {
416                                 VALUE_PAIR *nt_enc;
417
418                                 int to_copy = 516 - copied;
419                                 if (to_copy > 243)
420                                         to_copy = 243;
421
422                                 nt_enc = pairmake_packet("MS-CHAP-NT-Enc-PW", NULL, T_OP_ADD);
423                                 nt_enc->vp_length = 4 + to_copy;
424
425                                 nt_enc->vp_octets = p = talloc_array(nt_enc, uint8_t, nt_enc->vp_length);
426
427                                 p[0] = 6;
428                                 p[1] = mschap_id;
429                                 p[2] = 0;
430                                 p[3] = seq++;
431
432                                 memcpy(p + 4, eap_ds->response->type.data + 4 + copied, to_copy);
433                                 copied += to_copy;
434                         }
435
436                         RDEBUG2("Built change password packet");
437                         rdebug_pair_list(L_DBG_LVL_2, request, request->packet->vps, NULL);
438
439                         /*
440                          * jump to "authentication"
441                          */
442                         goto packet_ready;
443                 }
444
445                 /*
446                  * we sent a failure and are expecting a failure back
447                  */
448                 if (ccode != PW_EAP_MSCHAPV2_FAILURE) {
449                         REDEBUG("Sent FAILURE expecting FAILURE but got %d", ccode);
450                         return 0;
451                 }
452
453 failure:
454                 request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
455                 eap_ds->request->code = PW_EAP_FAILURE;
456                 return 1;
457
458         case PW_EAP_MSCHAPV2_SUCCESS:
459                 /*
460                  * we sent a success to the client; some clients send a
461                  * success back as-per the RFC, some send an ACK. Permit
462                  * both, I guess...
463                  */
464
465                 switch (ccode) {
466                 case PW_EAP_MSCHAPV2_SUCCESS:
467                         eap_ds->request->code = PW_EAP_SUCCESS;
468
469                         pairfilter(request->reply,
470                                   &request->reply->vps,
471                                   &data->mppe_keys, 0, 0, TAG_ANY);
472                         /* FALL-THROUGH */
473
474                 case PW_EAP_MSCHAPV2_ACK:
475 #ifdef WITH_PROXY
476                         /*
477                          *      It's a success.  Don't proxy it.
478                          */
479                         request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
480 #endif
481                         pairfilter(request->reply,
482                                   &request->reply->vps,
483                                   &data->reply, 0, 0, TAG_ANY);
484                         return 1;
485                 }
486                 REDEBUG("Sent SUCCESS expecting SUCCESS (or ACK) but got %d", ccode);
487                 return 0;
488
489         case PW_EAP_MSCHAPV2_CHALLENGE:
490                 if (ccode == PW_EAP_MSCHAPV2_FAILURE) goto failure;
491
492                 /*
493                  * we sent a challenge, expecting a response
494                  */
495                 if (ccode != PW_EAP_MSCHAPV2_RESPONSE) {
496                         REDEBUG("Sent CHALLENGE expecting RESPONSE but got %d", ccode);
497                         return 0;
498                 }
499                 /* authentication happens below */
500                 break;
501
502         default:
503                 /* should never happen */
504                 REDEBUG("unknown state %d", data->code);
505                 return 0;
506         }
507
508
509         /*
510          *      Ensure that we have at least enough data
511          *      to do the following checks.
512          *
513          *      EAP header (4), EAP type, MS-CHAP opcode,
514          *      MS-CHAP ident, MS-CHAP data length (2),
515          *      MS-CHAP value length.
516          */
517         if (eap_ds->response->length < (4 + 1 + 1 + 1 + 2 + 1)) {
518                 REDEBUG("Response is too short");
519                 return 0;
520         }
521
522         /*
523          *      The 'value_size' is the size of the response,
524          *      which is supposed to be the response (48
525          *      bytes) plus 1 byte of flags at the end.
526          */
527         if (eap_ds->response->type.data[4] != 49) {
528                 REDEBUG("Response is of incorrect length %d", eap_ds->response->type.data[4]);
529                 return 0;
530         }
531
532         /*
533          *      The MS-Length field is 5 + value_size + length
534          *      of name, which is put after the response.
535          */
536         length = (eap_ds->response->type.data[2] << 8) | eap_ds->response->type.data[3];
537         if ((length < (5 + 49)) || (length > (256 + 5 + 49))) {
538                 REDEBUG("Response contains contradictory length %zu %d",
539                         length, 5 + 49);
540                 return 0;
541         }
542
543         /*
544          *      We now know that the user has sent us a response
545          *      to the challenge.  Let's try to authenticate it.
546          *
547          *      We do this by taking the challenge from 'data',
548          *      the response from the EAP packet, and creating VALUE_PAIR's
549          *      to pass to the 'mschap' module.  This is a little wonky,
550          *      but it works.
551          */
552         challenge = pairmake_packet("MS-CHAP-Challenge", NULL, T_OP_EQ);
553         if (!challenge) {
554                 return 0;
555         }
556         pairmemcpy(challenge, data->challenge, MSCHAPV2_CHALLENGE_LEN);
557
558         response = pairmake_packet("MS-CHAP2-Response", NULL, T_OP_EQ);
559         if (!response) {
560                 return 0;
561         }
562         response->vp_length = MSCHAPV2_RESPONSE_LEN;
563         response->vp_octets = p = talloc_array(response, uint8_t, response->vp_length);
564
565         p[0] = eap_ds->response->type.data[1];
566         p[1] = eap_ds->response->type.data[5 + MSCHAPV2_RESPONSE_LEN];
567         memcpy(p + 2, &eap_ds->response->type.data[5], MSCHAPV2_RESPONSE_LEN - 2);
568
569         name = pairmake_packet("MS-CHAP-User-Name", NULL, T_OP_EQ);
570         if (!name) {
571                 return 0;
572         }
573
574         /*
575          *      MS-Length - MS-Value - 5.
576          */
577         name->vp_length = length - 49 - 5;
578         name->vp_strvalue = q = talloc_array(name, char, name->vp_length + 1);
579         memcpy(q,
580                &eap_ds->response->type.data[4 + MSCHAPV2_RESPONSE_LEN],
581                name->vp_length);
582         q[name->vp_length] = '\0';
583
584 packet_ready:
585
586 #ifdef WITH_PROXY
587         /*
588          *      If this options is set, then we do NOT authenticate the
589          *      user here.  Instead, now that we've added the MS-CHAP
590          *      attributes to the request, we STOP, and let the outer
591          *      tunnel code handle it.
592          *
593          *      This means that the outer tunnel code will DELETE the
594          *      EAP attributes, and proxy the MS-CHAP attributes to a
595          *      home server.
596          */
597         if (request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
598                 char *username = NULL;
599                 eap_tunnel_data_t *tunnel;
600
601                 RDEBUG2("cancelling authentication and letting it be proxied");
602
603                 /*
604                  *      Set up the callbacks for the tunnel
605                  */
606                 tunnel = talloc_zero(request, eap_tunnel_data_t);
607
608                 tunnel->tls_session = arg;
609                 tunnel->callback = mschap_postproxy;
610
611                 /*
612                  *      Associate the callback with the request.
613                  */
614                 rcode = request_data_add(request,
615                                          request->proxy,
616                                          REQUEST_DATA_EAP_TUNNEL_CALLBACK,
617                                          tunnel, false);
618                 rad_assert(rcode == 0);
619
620                 /*
621                  *      The State attribute is NOT supposed to
622                  *      go into the proxied packet, it will confuse
623                  *      other RADIUS servers, and they will discard
624                  *      the request.
625                  *
626                  *      The PEAP module will take care of adding
627                  *      the State attribute back, before passing
628                  *      the handler & request back into the tunnel.
629                  */
630                 pairdelete(&request->packet->vps, PW_STATE, 0, TAG_ANY);
631
632                 /*
633                  *      Fix the User-Name when proxying, to strip off
634                  *      the NT Domain, if we're told to, and a User-Name
635                  *      exists, and there's a \\, meaning an NT-Domain
636                  *      in the user name, THEN discard the user name.
637                  */
638                 if (inst->with_ntdomain_hack &&
639                     ((challenge = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY)) != NULL) &&
640                     ((username = strchr(challenge->vp_strvalue, '\\')) != NULL)) {
641                         /*
642                          *      Wipe out the NT domain.
643                          *
644                          *      FIXME: Put it into MS-CHAP-Domain?
645                          */
646                         username++; /* skip the \\ */
647                         pairstrcpy(challenge, username);
648                 }
649
650                 /*
651                  *      Remember that in the post-proxy stage, we've got
652                  *      to do the work below, AFTER the call to MS-CHAP
653                  *      authentication...
654                  */
655                 return 1;
656         }
657 #endif
658
659         /*
660          *      This is a wild & crazy hack.
661          */
662         rcode = process_authenticate(PW_AUTHTYPE_MS_CHAP, request);
663
664         /*
665          *      Delete MPPE keys & encryption policy.  We don't
666          *      want these here.
667          */
668         fix_mppe_keys(handler, data);
669
670         /*
671          *      Take the response from the mschap module, and
672          *      return success or failure, depending on the result.
673          */
674         response = NULL;
675         if (rcode == RLM_MODULE_OK) {
676                 pairfilter(data, &response, &request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
677                 data->code = PW_EAP_MSCHAPV2_SUCCESS;
678         } else if (inst->send_error) {
679                 pairfilter(data, &response, &request->reply->vps, PW_MSCHAP_ERROR, VENDORPEC_MICROSOFT, TAG_ANY);
680                 if (response) {
681                         int n,err,retry;
682                         char buf[34];
683
684                         VERIFY_VP(response);
685
686                         RDEBUG2("MSCHAP-Error: %s", response->vp_strvalue);
687
688                         /*
689                          *      Pxarse the new challenge out of the
690                          *      MS-CHAP-Error, so that if the client
691                          *      issues a re-try, we will know which
692                          *      challenge value that they used.
693                          */
694                         n = sscanf(response->vp_strvalue, "%*cE=%d R=%d C=%32s", &err, &retry, &buf[0]);
695                         if (n == 3) {
696                                 DEBUG2("Found new challenge from MS-CHAP-Error: err=%d retry=%d challenge=%s", err, retry, buf);
697                                 fr_hex2bin(data->challenge, 16, buf, strlen(buf));
698                         } else {
699                                 DEBUG2("Could not parse new challenge from MS-CHAP-Error: %d", n);
700                         }
701                 }
702                 data->code = PW_EAP_MSCHAPV2_FAILURE;
703         } else {
704                 eap_ds->request->code = PW_EAP_FAILURE;
705                 return 1;
706         }
707
708         /*
709          *      No response, die.
710          */
711         if (!response) {
712                 REDEBUG("No MS-CHAP2-Success or MS-CHAP-Error was found");
713                 return 0;
714         }
715
716         /*
717          *      Compose the response (whatever it is),
718          *      and return it to the over-lying EAP module.
719          */
720         eapmschapv2_compose(handler, response);
721         pairfree(&response);
722
723         return 1;
724 }
725
726 /*
727  *      The module name should be the only globally exported symbol.
728  *      That is, everything else should be 'static'.
729  */
730 extern rlm_eap_module_t rlm_eap_mschapv2;
731 rlm_eap_module_t rlm_eap_mschapv2 = {
732         "eap_mschapv2",
733         mschapv2_attach,                /* attach */
734         mschapv2_initiate,              /* Start the initial request */
735         NULL,                           /* authorization */
736         mschapv2_authenticate,          /* authentication */
737         NULL                            /* detach */
738 };