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