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