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