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