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