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