The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_mschap.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: fa6a2db7e4712ebe1f41a9182b15b70dfca440b6 $
19  * @file rlm_mschap.c
20  * @brief Implemented mschap authentication.
21  *
22  * @copyright 2000, 2001, 2006 The FreeRADIUS server project
23  */
24 
25 /* MPPE support from Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp> */
26 
27 RCSID("$Id: fa6a2db7e4712ebe1f41a9182b15b70dfca440b6 $")
28 
29 #define LOG_PREFIX mctx->inst->name
30 
31 #include <freeradius-devel/server/base.h>
32 #include <freeradius-devel/server/exec_legacy.h>
33 #include <freeradius-devel/server/module_rlm.h>
34 #include <freeradius-devel/server/password.h>
35 #include <freeradius-devel/tls/strerror.h>
36 #include <freeradius-devel/util/debug.h>
37 #include <freeradius-devel/radius/defs.h>
38 
39 #include <freeradius-devel/util/base16.h>
40 #include <freeradius-devel/util/md4.h>
41 #include <freeradius-devel/util/md5.h>
42 #include <freeradius-devel/util/misc.h>
43 #include <freeradius-devel/util/sha1.h>
44 
45 #include <freeradius-devel/unlang/function.h>
46 #include <freeradius-devel/unlang/xlat_func.h>
47 
48 #include <sys/wait.h>
49 #include <ctype.h>
50 
51 #include "rlm_mschap.h"
52 #include "mschap.h"
53 #include "smbdes.h"
54 
55 #ifdef WITH_AUTH_WINBIND
56 #include "auth_wbclient.h"
57 #endif
58 
59 #ifdef WITH_TLS
60 USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
61 # include <freeradius-devel/tls/openssl_user_macros.h>
62 # include <openssl/rc4.h>
63 #endif
64 
65 #ifdef __APPLE__
66 unlang_action_t od_mschap_auth(rlm_rcode_t *p_result, request_t *request, fr_pair_t *challenge, fr_pair_t *usernamepair,
67  mschap_auth_call_env_t *env_data);
68 #endif
69 
70 /* Allowable account control bits */
71 #define ACB_DISABLED 0x00010000 //!< User account disabled.
72 #define ACB_HOMDIRREQ 0x00020000 //!< Home directory required.
73 #define ACB_PWNOTREQ 0x00040000 //!< User password not required.
74 #define ACB_TEMPDUP 0x00080000 //!< Temporary duplicate account.
75 #define ACB_NORMAL 0x00100000 //!< Normal user account.
76 #define ACB_MNS 0x00200000 //!< MNS logon user account.
77 #define ACB_DOMTRUST 0x00400000 //!< Interdomain trust account.
78 #define ACB_WSTRUST 0x00800000 //!< Workstation trust account.
79 #define ACB_SVRTRUST 0x01000000 //!< Server trust account.
80 #define ACB_PWNOEXP 0x02000000 //!< User password does not expire.
81 #define ACB_AUTOLOCK 0x04000000 //!< Account auto locked.
82 #define ACB_FR_EXPIRED 0x00020000 //!< Password Expired.
83 
84 static const conf_parser_t passchange_config[] = {
85  { FR_CONF_OFFSET_FLAGS("ntlm_auth", CONF_FLAG_XLAT, rlm_mschap_t, ntlm_cpw) },
87 };
88 
89 static const conf_parser_t winbind_config[] = {
90  { FR_CONF_OFFSET("username", rlm_mschap_t, wb_username) },
91 #ifdef WITH_AUTH_WINBIND
92  { FR_CONF_OFFSET("retry_with_normalised_username", rlm_mschap_t, wb_retry_with_normalised_username), .dflt = "no" },
93 #endif
95 };
96 
97 static const conf_parser_t module_config[] = {
98  { FR_CONF_OFFSET("normalise", rlm_mschap_t, normify), .dflt = "yes" },
99 
100  /*
101  * Cache the password by default.
102  */
103  { FR_CONF_OFFSET("use_mppe", rlm_mschap_t, use_mppe), .dflt = "yes" },
104  { FR_CONF_OFFSET("require_encryption", rlm_mschap_t, require_encryption), .dflt = "no" },
105  { FR_CONF_OFFSET("require_strong", rlm_mschap_t, require_strong), .dflt = "no" },
106  { FR_CONF_OFFSET("with_ntdomain_hack", rlm_mschap_t, with_ntdomain_hack), .dflt = "yes" },
107  { FR_CONF_OFFSET_FLAGS("ntlm_auth", CONF_FLAG_XLAT, rlm_mschap_t, ntlm_auth) },
108  { FR_CONF_OFFSET("ntlm_auth_timeout", rlm_mschap_t, ntlm_auth_timeout) },
109 
110  { FR_CONF_POINTER("passchange", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) passchange_config },
111  { FR_CONF_OFFSET("allow_retry", rlm_mschap_t, allow_retry), .dflt = "yes" },
112  { FR_CONF_OFFSET("retry_msg", rlm_mschap_t, retry_msg) },
113 
114 
115 #ifdef __APPLE__
116  { FR_CONF_OFFSET("use_open_directory", rlm_mschap_t, open_directory), .dflt = "yes" },
117 #endif
118 
119  { FR_CONF_POINTER("winbind", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) winbind_config },
120 
121  /*
122  * These are now in a subsection above.
123  */
124  { FR_CONF_DEPRECATED("winbind_username", rlm_mschap_t, wb_username) },
125 #ifdef WITH_AUTH_WINBIND
126  { FR_CONF_DEPRECATED("winbind_retry_with_normalised_username", rlm_mschap_t, wb_retry_with_normalised_username) },
127 #endif
129 };
130 
131 #define MSCHAP_CALL_ENV(_x) \
132 static const call_env_method_t mschap_ ## _x ## _method_env = { \
133  FR_CALL_ENV_METHOD_OUT(mschap_ ## _x ## _call_env_t), \
134  .env = (call_env_parser_t[]){ \
135  { FR_CALL_ENV_SUBSECTION("attributes", NULL, CALL_ENV_FLAG_REQUIRED, _x ## _call_env) }, \
136  CALL_ENV_TERMINATOR \
137  } \
138 }
139 
140 #define MSCHAP_COMMON_CALL_ENV(_x) \
141 { FR_CALL_ENV_PARSE_ONLY_OFFSET("chap_challenge", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_challenge), \
142  .pair.dflt = "&Vendor-Specific.Microsoft.CHAP-Challenge", .pair.dflt_quote = T_BARE_WORD }, \
143 { FR_CALL_ENV_PARSE_ONLY_OFFSET("chap_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_response), \
144  .pair.dflt = "&Vendor-Specific.Microsoft.CHAP-Response", .pair.dflt_quote = T_BARE_WORD }, \
145 { FR_CALL_ENV_PARSE_ONLY_OFFSET("chap2_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap2_response), \
146  .pair.dflt = "&Vendor-Specific.Microsoft.CHAP2-Response", .pair.dflt_quote = T_BARE_WORD }
147 
148 #define MSCHAP_OPT_CALL_ENV(_opt, _x) \
149 { FR_CALL_ENV_PARSE_ONLY_OFFSET(STRINGIFY(_opt), FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE, mschap_ ## _x ## _call_env_t, _opt) }
150 
151 typedef struct {
152  tmpl_t const *username;
157 
162 };
163 
165 
169  MSCHAP_OPT_CALL_ENV(chap2_success, auth),
170  MSCHAP_OPT_CALL_ENV(chap_error, auth),
171  MSCHAP_OPT_CALL_ENV(chap_mppe_keys, auth),
172  MSCHAP_OPT_CALL_ENV(mppe_encryption_policy, auth),
173  MSCHAP_OPT_CALL_ENV(mppe_recv_key, auth),
174  MSCHAP_OPT_CALL_ENV(mppe_send_key, auth),
175  MSCHAP_OPT_CALL_ENV(mppe_encryption_types, auth),
176  MSCHAP_OPT_CALL_ENV(chap2_cpw, auth),
177  MSCHAP_OPT_CALL_ENV(chap_nt_enc_pw, auth),
179 };
180 
183  .env = (call_env_parser_t[]){ \
185  { FR_CALL_ENV_SUBSECTION("passchange", NULL, CALL_ENV_FLAG_SUBSECTION,
186  ((call_env_parser_t[]) {
187  { FR_CALL_ENV_PARSE_ONLY_OFFSET("ntlm_auth_username", FR_TYPE_STRING, CALL_ENV_FLAG_NONE, mschap_auth_call_env_t, ntlm_cpw_username) },
191  }))},
192  { FR_CALL_ENV_SUBSECTION("winbind", NULL, CALL_ENV_FLAG_NONE,
193  ((call_env_parser_t[]) {
197  }))},
199  }
200 };
201 
202 typedef struct {
208 
211  MSCHAP_OPT_CALL_ENV(chap2_cpw, autz),
213 };
214 
216 
217 typedef struct {
218  fr_value_box_list_t cpw_user;
219  fr_value_box_list_t cpw_domain;
220  fr_value_box_list_t local_cpw_result;
221  uint8_t new_nt_encrypted[516];
222  uint8_t old_nt_hash[NT_DIGEST_LENGTH];
225 
226 typedef struct {
227  char const *name;
236 
238 static fr_dict_t const *dict_radius;
239 
242  { .out = &dict_freeradius, .proto = "freeradius" },
243  { .out = &dict_radius, .proto = "radius" },
244  { NULL }
245 };
246 
258 
261  { .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
262  { .out = &attr_cleartext_password, .name = "Password.Cleartext", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
263  { .out = &attr_eap_identity, .name = "EAP-Identity", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
264  { .out = &attr_ms_chap_new_cleartext_password, .name = "MS-CHAP-New-Cleartext-Password", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
265  { .out = &attr_ms_chap_new_nt_password, .name = "MS-CHAP-New-NT-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
266  { .out = &attr_ms_chap_peer_challenge, .name = "MS-CHAP-Peer-Challenge", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
267  { .out = &attr_ms_chap_use_ntlm_auth, .name = "MS-CHAP-Use-NTLM-Auth", .type = FR_TYPE_UINT8, .dict = &dict_freeradius },
268  { .out = &attr_ms_chap_user_name, .name = "MS-CHAP-User-Name", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
269  { .out = &attr_nt_password, .name = "Password.NT", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
270  { .out = &attr_smb_account_ctrl_text, .name = "SMB-Account-Ctrl-Text", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
271  { .out = &attr_smb_account_ctrl, .name = "SMB-Account-Ctrl", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
272 
273  { NULL }
274 };
275 
277 {
278  fr_pair_t *vp;
279 
280  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_name);
281  if (vp) return vp;
282 
283  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_identity);
284  if (vp) return vp;
285 
286  REDEBUG("No user identity found in current request");
287 
288  return NULL;
289 }
290 
291 static int pdb_decode_acct_ctrl(char const *p)
292 {
293  int acct_ctrl = 0;
294  int done = 0;
295 
296  /*
297  * Check if the account type bits have been encoded after the
298  * NT password (in the form [NDHTUWSLXI]).
299  */
300 
301  if (*p != '[') return 0;
302 
303  for (p++; *p && !done; p++) {
304  switch (*p) {
305  case 'N': /* 'N'o password. */
306  acct_ctrl |= ACB_PWNOTREQ;
307  break;
308 
309  case 'D': /* 'D'isabled. */
310  acct_ctrl |= ACB_DISABLED ;
311  break;
312 
313  case 'H': /* 'H'omedir required. */
314  acct_ctrl |= ACB_HOMDIRREQ;
315  break;
316 
317  case 'T': /* 'T'emp account. */
318  acct_ctrl |= ACB_TEMPDUP;
319  break;
320 
321  case 'U': /* 'U'ser account (normal). */
322  acct_ctrl |= ACB_NORMAL;
323  break;
324 
325  case 'M': /* 'M'NS logon user account. What is this? */
326  acct_ctrl |= ACB_MNS;
327  break;
328 
329  case 'W': /* 'W'orkstation account. */
330  acct_ctrl |= ACB_WSTRUST;
331  break;
332 
333  case 'S': /* 'S'erver account. */
334  acct_ctrl |= ACB_SVRTRUST;
335  break;
336 
337  case 'L': /* 'L'ocked account. */
338  acct_ctrl |= ACB_AUTOLOCK;
339  break;
340 
341  case 'X': /* No 'X'piry on password */
342  acct_ctrl |= ACB_PWNOEXP;
343  break;
344 
345  case 'I': /* 'I'nterdomain trust account. */
346  acct_ctrl |= ACB_DOMTRUST;
347  break;
348 
349  case 'e': /* 'e'xpired, the password has */
350  acct_ctrl |= ACB_FR_EXPIRED;
351  break;
352 
353  case ' ': /* ignore spaces */
354  break;
355 
356  case ':':
357  case '\n':
358  case '\0':
359  case ']':
360  default:
361  done = 1;
362  break;
363  }
364  }
365 
366  return acct_ctrl;
367 }
368 
370  { .required = true, .single = true, .type = FR_TYPE_STRING },
371  { .concat = true, .type = FR_TYPE_STRING },
373 };
374 
375 /** Get data from MSCHAP attributes
376  *
377  * Pulls NT-Response, LM-Response, or Challenge from MSCHAP
378  * attributes.
379  *
380  * @ingroup xlat_functions
381  */
382 static xlat_action_t mschap_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
383  xlat_ctx_t const *xctx,
384  request_t *request, fr_value_box_list_t *in)
385 {
386  mschap_xlat_call_env_t *env_data = talloc_get_type_abort(xctx->env_data, mschap_xlat_call_env_t);
387  size_t data_len;
388  uint8_t const *data = NULL;
389  uint8_t buffer[32];
390  fr_pair_t *user_name;
391  fr_pair_t *chap_challenge, *response;
392  rlm_mschap_t const *inst = talloc_get_type_abort(xctx->mctx->inst->data, rlm_mschap_t);
393  fr_value_box_t *arg = fr_value_box_list_head(in);
394  fr_value_box_t *vb;
395  bool tainted = false;
396 
397  response = NULL;
398 
399  /*
400  * Challenge means MS-CHAPv1 challenge, or
401  * hash of MS-CHAPv2 challenge, and peer challenge.
402  */
403  if (strncasecmp(arg->vb_strvalue, "Challenge", 9) == 0) {
404  chap_challenge = fr_pair_find_by_da_nested(&request->request_pairs, NULL,
405  tmpl_attr_tail_da(env_data->chap_challenge));
406  if (!chap_challenge) {
407  REDEBUG("No MS-CHAP-Challenge in the request");
408  return XLAT_ACTION_FAIL;
409  }
410  tainted = chap_challenge->vp_tainted;
411 
412  /*
413  * MS-CHAP-Challenges are 8 octets,
414  * for MS-CHAPv1
415  */
416  if (chap_challenge->vp_length == 8) {
417  RDEBUG2("mschap1: %02x", chap_challenge->vp_octets[0]);
418  data = chap_challenge->vp_octets;
419  data_len = 8;
420 
421  /*
422  * MS-CHAP-Challenges are 16 octets,
423  * for MS-CHAPv2.
424  */
425  } else if (chap_challenge->vp_length == 16) {
426  fr_pair_t *name_vp;
427  fr_pair_t *response_name;
428  char const *username_str;
429  size_t username_len;
430 
431  response = fr_pair_find_by_da_nested(&request->request_pairs, NULL,
432  tmpl_attr_tail_da(env_data->chap2_response));
433  if (!response) {
434  REDEBUG("Vendor-Specific.Microsoft.CHAP2-Response is required to calculate MS-CHAPv1 challenge");
435  return XLAT_ACTION_FAIL;
436  }
437 
438  /*
439  * FIXME: Much of this is copied from
440  * below. We should put it into a
441  * separate function.
442  */
443 
444  /*
445  * Responses are 50 octets.
446  */
447  if (response->vp_length < 50) {
448  REDEBUG("Vendor-Specific.Microsoft.CHAP-Response has the wrong format");
449  return XLAT_ACTION_FAIL;
450  }
451 
452  user_name = mschap_identity_find(request, tmpl_attr_tail_da(env_data->username));
453  if (!user_name) return XLAT_ACTION_FAIL;
454 
455  /*
456  * Check for MS-CHAP-User-Name and if found, use it
457  * to construct the MSCHAPv1 challenge. This is
458  * set by rlm_eap_mschap to the MS-CHAP Response
459  * packet Name field.
460  *
461  * We prefer this to the User-Name in the
462  * packet.
463  */
464  response_name = fr_pair_find_by_da(&request->request_pairs, NULL, attr_ms_chap_user_name);
465  name_vp = response_name ? response_name : user_name;
466 
467  /*
468  * with_ntdomain_hack moved here, too.
469  */
470  username_str = memchr(name_vp->vp_strvalue, '\\', name_vp->vp_length);
471  if (username_str != NULL) {
472  if (inst->with_ntdomain_hack) {
473  username_str++;
474  } else {
475  RWDEBUG2("NT Domain delimiter found, should 'with_ntdomain_hack' be enabled?");
476  username_str = name_vp->vp_strvalue;
477  }
478  } else {
479  username_str = name_vp->vp_strvalue;
480  }
481  username_len = name_vp->vp_length - (username_str - name_vp->vp_strvalue);
482 
483  if (response_name &&
484  ((user_name->vp_length != response_name->vp_length) ||
485  (memcmp(user_name->vp_strvalue, response_name->vp_strvalue, user_name->vp_length) != 0))) {
486  RWDEBUG2("%pP is not the same as %pP from EAP-MSCHAPv2", user_name, response_name);
487  }
488 
489  /*
490  * Get the MS-CHAPv1 challenge
491  * from the MS-CHAPv2 peer challenge,
492  * our challenge, and the user name.
493  */
494  RDEBUG2("Creating challenge with username \"%pV\"",
495  fr_box_strvalue_len(username_str, username_len));
496  mschap_challenge_hash(buffer, response->vp_octets + 2, chap_challenge->vp_octets,
497  username_str, username_len);
498  data = buffer;
499  data_len = 8;
500  } else {
501  REDEBUG("Invalid MS-CHAP challenge length");
502  return XLAT_ACTION_FAIL;
503  }
504 
505  /*
506  * Get the MS-CHAPv1 response, or the MS-CHAPv2
507  * response.
508  */
509  } else if (strncasecmp(arg->vb_strvalue, "NT-Response", 11) == 0) {
510  response = fr_pair_find_by_da_nested(&request->request_pairs, NULL,
511  tmpl_attr_tail_da(env_data->chap_response));
512  if (!response) response = fr_pair_find_by_da_nested(&request->request_pairs, NULL,
513  tmpl_attr_tail_da(env_data->chap2_response));
514  if (!response) {
515  REDEBUG("No MS-CHAP-Response or MS-CHAP2-Response was found in the request");
516  return XLAT_ACTION_FAIL;
517  }
518  tainted = response->vp_tainted;
519 
520  /*
521  * For MS-CHAPv1, the NT-Response exists only
522  * if the second octet says so.
523  */
524  if ((response->da == tmpl_attr_tail_da(env_data->chap_response)) && ((response->vp_octets[1] & 0x01) == 0)) {
525  REDEBUG("No NT-Response in MS-CHAP-Response");
526  return XLAT_ACTION_FAIL;
527  }
528 
529  if (response->vp_length < 50) {
530  REDEBUG("Vendor-Specific.Microsoft.CHAP-Response has the wrong format");
531  return XLAT_ACTION_FAIL;
532  }
533 
534  /*
535  * MS-CHAP-Response and MS-CHAP2-Response have
536  * the NT-Response at the same offset, and are
537  * the same length.
538  */
539  data = response->vp_octets + 26;
540  data_len = 24;
541 
542  /*
543  * LM-Response is deprecated, and exists only
544  * in MS-CHAPv1, and not often there.
545  */
546  } else if (strncasecmp(arg->vb_strvalue, "LM-Response", 11) == 0) {
547  response = fr_pair_find_by_da_nested(&request->request_pairs, NULL,
548  tmpl_attr_tail_da(env_data->chap_response));
549  if (!response) {
550  REDEBUG("No MS-CHAP-Response was found in the request");
551  return XLAT_ACTION_FAIL;
552  }
553  tainted = response->vp_tainted;
554 
555  if (response->vp_length < 50) {
556  REDEBUG("Vendor-Specific.Microsoft.CHAP-Response has the wrong format");
557  return XLAT_ACTION_FAIL;
558  }
559  /*
560  * For MS-CHAPv1, the LM-Response exists only
561  * if the second octet says so.
562  */
563  if ((response->vp_octets[1] & 0x01) != 0) {
564  REDEBUG("No LM-Response in MS-CHAP-Response");
565  return XLAT_ACTION_FAIL;
566  }
567  data = response->vp_octets + 2;
568  data_len = 24;
569 
570  /*
571  * Pull the domain name out of the User-Name, if it exists.
572  *
573  * This is the full domain name, not just the name after host/
574  */
575  } else if (strncasecmp(arg->vb_strvalue, "Domain-Name", 11) == 0) {
576  char *p;
577 
578  MEM(vb = fr_value_box_alloc_null(ctx));
579 
580  user_name = mschap_identity_find(request, tmpl_attr_tail_da(env_data->username));
581  if (!user_name) return XLAT_ACTION_FAIL;
582 
583  /*
584  * First check to see if this is a host/ style User-Name
585  * (a la Kerberos host principal)
586  */
587  if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) {
588  /*
589  * If we're getting a User-Name formatted in this way,
590  * it's likely due to PEAP. The Windows Domain will be
591  * the first domain component following the hostname,
592  * or the machine name itself if only a hostname is supplied
593  */
594  p = strchr(user_name->vp_strvalue, '.');
595  if (!p) {
596  RDEBUG2("setting Domain-Name to same as machine name");
597  fr_value_box_strdup(ctx, vb, NULL, user_name->vp_strvalue + 5, user_name->vp_tainted);
598  } else {
599  p++; /* skip the period */
600 
601  fr_value_box_strdup(ctx, vb, NULL, p, user_name->vp_tainted);
602  }
603  } else {
604  p = strchr(user_name->vp_strvalue, '\\');
605  if (!p) {
606  REDEBUG("No domain name was found in the User-Name");
607  talloc_free(vb);
608  return XLAT_ACTION_FAIL;
609  }
610 
611  /*
612  * Hack. This is simpler than the alternatives.
613  */
614  *p = '\0';
615  fr_value_box_strdup(ctx, vb, NULL, user_name->vp_strvalue, user_name->vp_tainted);
616  *p = '\\';
617  }
618 
619  fr_dcursor_append(out, vb);
620  return XLAT_ACTION_DONE;
621 
622  /*
623  * Pull the NT-Domain out of the User-Name, if it exists.
624  */
625  } else if (strncasecmp(arg->vb_strvalue, "NT-Domain", 9) == 0) {
626  char *p, *q;
627 
628  MEM(vb = fr_value_box_alloc_null(ctx));
629 
630  user_name = mschap_identity_find(request, tmpl_attr_tail_da(env_data->username));
631  if (!user_name) return XLAT_ACTION_FAIL;
632 
633  /*
634  * First check to see if this is a host/ style User-Name
635  * (a la Kerberos host principal)
636  */
637  if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) {
638  /*
639  * If we're getting a User-Name formatted in this way,
640  * it's likely due to PEAP. The Windows Domain will be
641  * the first domain component following the hostname,
642  * or the machine name itself if only a hostname is supplied
643  */
644  p = strchr(user_name->vp_strvalue, '.');
645  if (!p) {
646  RDEBUG2("setting NT-Domain to same as machine name");
647  fr_value_box_strdup(ctx, vb, NULL, user_name->vp_strvalue + 5, user_name->vp_tainted);
648  } else {
649  p++; /* skip the period */
650  q = strchr(p, '.');
651  /*
652  * use the same hack as below
653  * only if another period was found
654  */
655  if (q) *q = '\0';
656  fr_value_box_strdup(ctx, vb, NULL, p, user_name->vp_tainted);
657  if (q) *q = '.';
658  }
659  } else {
660  p = strchr(user_name->vp_strvalue, '\\');
661  if (!p) {
662  REDEBUG("No NT-Domain was found in the User-Name");
663  talloc_free(vb);
664  return XLAT_ACTION_FAIL;
665  }
666 
667  /*
668  * Hack. This is simpler than the alternatives.
669  */
670  *p = '\0';
671  fr_value_box_strdup(ctx, vb, NULL, user_name->vp_strvalue, user_name->vp_tainted);
672  *p = '\\';
673  }
674 
675  fr_dcursor_append(out, vb);
676  return XLAT_ACTION_DONE;
677 
678  /*
679  * Pull the User-Name out of the User-Name...
680  */
681  } else if (strncasecmp(arg->vb_strvalue, "User-Name", 9) == 0) {
682  char const *p, *q;
683 
684  user_name = mschap_identity_find(request, tmpl_attr_tail_da(env_data->username));
685  if (!user_name) return XLAT_ACTION_FAIL;
686 
687  MEM(vb = fr_value_box_alloc_null(ctx));
688 
689  /*
690  * First check to see if this is a host/ style User-Name
691  * (a la Kerberos host principal)
692  */
693  if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) {
694  p = user_name->vp_strvalue + 5;
695  /*
696  * If we're getting a User-Name formatted in this way,
697  * it's likely due to PEAP. When authenticating this against
698  * a Domain, Windows will expect the User-Name to be in the
699  * format of hostname$, the SAM version of the name, so we
700  * have to convert it to that here. We do so by stripping
701  * off the first 5 characters (host/), and copying everything
702  * from that point to the first period into a string and appending
703  * a $ to the end.
704  */
705  q = strchr(p, '.');
706 
707  /*
708  * use the same hack as above
709  * only if a period was found
710  */
711  if (q) {
712  fr_value_box_asprintf(ctx, vb, NULL, true, "%.*s$", (int) (q - p), p);
713  } else {
714  fr_value_box_asprintf(ctx, vb, NULL, true, "%s$", p);
715  }
716  } else {
717  p = strchr(user_name->vp_strvalue, '\\');
718  if (p) {
719  p++; /* skip the backslash */
720  } else {
721  p = user_name->vp_strvalue; /* use the whole User-Name */
722  }
723  fr_value_box_strdup(ctx, vb, NULL, p, user_name->vp_tainted);
724  }
725 
726  fr_dcursor_append(out, vb);
727  return XLAT_ACTION_DONE;
728 
729  /*
730  * Return the NT-Hash of the passed string
731  */
732  } else if (strncasecmp(arg->vb_strvalue, "NT-Hash", 7) == 0) {
733  arg = fr_value_box_list_next(in, arg);
734  if ((!arg) || (arg->vb_length == 0))
735  return XLAT_ACTION_FAIL;
736 
737  if (mschap_nt_password_hash(buffer, arg->vb_strvalue) < 0) {
738  REDEBUG("Failed generating Password.NT");
739  *buffer = '\0';
740  return XLAT_ACTION_FAIL;
741  }
742 
743  MEM(vb = fr_value_box_alloc_null(ctx));
744  fr_value_box_memdup(ctx, vb, NULL, buffer, NT_DIGEST_LENGTH, false);
745  RDEBUG2("NT-Hash of \"known-good\" password: %pV", vb);
746  fr_dcursor_append(out, vb);
747  return XLAT_ACTION_DONE;
748 
749  /*
750  * Return the LM-Hash of the passed string
751  */
752  } else if (strncasecmp(arg->vb_strvalue, "LM-Hash", 7) == 0) {
753  arg = fr_value_box_list_next(in, arg);
754  if ((!arg) || (arg->vb_length == 0))
755  return XLAT_ACTION_FAIL;
756 
757  smbdes_lmpwdhash(arg->vb_strvalue, buffer);
758 
759  MEM(vb = fr_value_box_alloc_null(ctx));
760  fr_value_box_memdup(ctx, vb, NULL, buffer, LM_DIGEST_LENGTH, false);
761  RDEBUG2("LM-Hash of %s = %pV", arg->vb_strvalue, vb);
762  fr_dcursor_append(out, vb);
763  return XLAT_ACTION_DONE;
764  } else {
765  REDEBUG("Unknown expansion string '%pV'", arg);
766  return XLAT_ACTION_FAIL;
767  }
768 
769  /*
770  * Didn't set anything: this is bad.
771  */
772  if (!data) {
773  RWDEBUG2("Failed to do anything intelligent");
774  return XLAT_ACTION_FAIL;
775  }
776 
777  MEM(vb = fr_value_box_alloc_null(ctx));
778  fr_value_box_memdup(ctx, vb, NULL, data, data_len, tainted);
779 
780  fr_dcursor_append(out, vb);
781  return XLAT_ACTION_DONE;
782 }
783 
784 
785 #ifdef WITH_AUTH_WINBIND
786 /*
787  * Free connection pool winbind context
788  */
789 static int _mod_conn_free(struct wbcContext **wb_ctx)
790 {
791  wbcCtxFree(*wb_ctx);
792 
793  return 0;
794 }
795 
796 /*
797  * Create connection pool winbind context
798  */
799 static void *mod_conn_create(TALLOC_CTX *ctx, void *instance, UNUSED fr_time_delta_t timeout)
800 {
801  struct wbcContext **wb_ctx;
803 
804  wb_ctx = talloc_zero(ctx, struct wbcContext *);
805  *wb_ctx = wbcCtxCreate();
806 
807  if (*wb_ctx == NULL) {
808  ERROR("failed to create winbind context");
809  talloc_free(wb_ctx);
810  return NULL;
811  }
812 
813  talloc_set_destructor(wb_ctx, _mod_conn_free);
814 
815  return *wb_ctx;
816 }
817 #endif
818 
819 /*
820  * Add MPPE attributes to the reply.
821  */
823  request_t *request, fr_dict_attr_t const *da, uint8_t const *value, size_t len)
824 {
825  fr_pair_t *vp;
826 
827  MEM(pair_update_reply(&vp, da) >= 0);
828  fr_pair_value_memdup(vp, value, len, false);
829  RINDENT();
830  RDEBUG2("&reply.%pP", vp);
831  REXDENT();
832 }
833 
834 /*
835  * Write a string to an fd, followed by "\n"
836  */
837 static int write_all(int fd, char const *buf, int len) {
838  int rv, done=0;
839 
840  while (done < len) {
841  rv = write(fd, buf+done, len-done);
842  if (rv <= 0)
843  break;
844  done += rv;
845  }
846  rv = write(fd, "\n", 1);
847  if (rv <= 0) return rv;
848  return done;
849 }
850 
851 /*
852  * Perform an MS-CHAP2 password change
853  */
854 
855 static int CC_HINT(nonnull) do_mschap_cpw(rlm_mschap_t const *inst, request_t *request, mschap_auth_ctx_t *auth_ctx,
856  uint8_t *new_nt_password, uint8_t *old_nt_hash)
857 {
858  mschap_cpw_ctx_t *cpw_ctx = auth_ctx->cpw_ctx;
859  fr_value_box_t *vb;
860 
861  if (inst->ntlm_cpw && auth_ctx->method != AUTH_INTERNAL) {
862  /*
863  * we're going to run ntlm_auth in helper-mode
864  * we're expecting to use the ntlm-change-password-1 protocol
865  * which needs the following on stdin:
866  *
867  * username: %mschap(User-Name)
868  * nt-domain: %mschap(NT-Domain)
869  * new-nt-password-blob: bin2hex(new_nt_password) - 1032 bytes encoded
870  * old-nt-hash-blob: bin2hex(old_nt_hash) - 32 bytes encoded
871  * new-lm-password-blob: 00000...0000 - 1032 bytes null
872  * old-lm-hash-blob: 000....000 - 32 bytes null
873  * .\n
874  *
875  * ...and it should then print out
876  *
877  * Password-Change: Yes
878  *
879  * or
880  *
881  * Password-Change: No
882  * Password-Change-Error: blah
883  */
884 
885  int status, len, to_child=-1, from_child=-1;
886  pid_t pid, child_pid;
887  char buf[2048];
888  char *pmsg;
889  char const *emsg;
890 
891  RDEBUG2("Doing MS-CHAPv2 password change via ntlm_auth helper");
892 
893  /*
894  * Start up ntlm_auth with a pipe on stdin and stdout
895  */
896 
897  pid = radius_start_program_legacy(&to_child, &from_child, NULL, inst->ntlm_cpw, request, true, NULL, false);
898  if (pid < 0) {
899  REDEBUG("could not exec ntlm_auth cpw command");
900  return -1;
901  }
902 
903  /*
904  * write the stuff to the client
905  */
906 
907  vb = fr_value_box_list_head(&cpw_ctx->cpw_user);
908  if (!vb) goto ntlm_auth_err;
909 
910  if (write_all(to_child, vb->vb_strvalue, vb->vb_length) != (int)vb->vb_length) {
911  REDEBUG("Failed to write username to child");
912  goto ntlm_auth_err;
913  }
914 
915  if (auth_ctx->env_data->ntlm_cpw_domain) {
916  vb = fr_value_box_list_head(&cpw_ctx->cpw_domain);
917  if (!vb) goto no_domain;
918 
919  if (write_all(to_child, vb->vb_strvalue, vb->vb_length) != (int)vb->vb_length) {
920  REDEBUG("Failed to write domain to child");
921  goto ntlm_auth_err;
922  }
923  } else {
924  no_domain:
925  RWDEBUG2("No ntlm_auth domain set, username must be full-username to work");
926  }
927 
928  /* now the password blobs */
929  len = snprintf(buf, sizeof(buf), "new-nt-password-blob: ");
930  fr_base16_encode(&FR_SBUFF_OUT(buf + len, sizeof(buf) - len), &FR_DBUFF_TMP(new_nt_password, 516));
931  len = strlen(buf);
932  if (write_all(to_child, buf, len) != len) {
933  RDEBUG2("failed to write new password blob to child");
934  goto ntlm_auth_err;
935  }
936 
937  len = snprintf(buf, sizeof(buf), "old-nt-hash-blob: ");
938  fr_base16_encode(&FR_SBUFF_OUT(buf + len, sizeof(buf) - len), &FR_DBUFF_TMP(old_nt_hash, NT_DIGEST_LENGTH));
939  len = strlen(buf);
940  if (write_all(to_child, buf, len) != len) {
941  REDEBUG("Failed to write old hash blob to child");
942  goto ntlm_auth_err;
943  }
944 
945  /*
946  * In current samba versions, failure to supply empty LM password/hash
947  * blobs causes the change to fail.
948  */
949  len = snprintf(buf, sizeof(buf), "new-lm-password-blob: %01032i", 0);
950  if (write_all(to_child, buf, len) != len) {
951  REDEBUG("Failed to write dummy LM password to child");
952  goto ntlm_auth_err;
953  }
954  len = snprintf(buf, sizeof(buf), "old-lm-hash-blob: %032i", 0);
955  if (write_all(to_child, buf, len) != len) {
956  REDEBUG("Failed to write dummy LM hash to child");
957  goto ntlm_auth_err;
958  }
959  if (write_all(to_child, ".", 1) != 1) {
960  REDEBUG("Failed to send finish to child");
961  goto ntlm_auth_err;
962  }
963  close(to_child);
964  to_child = -1;
965 
966  /*
967  * Read from the child
968  */
969  len = radius_readfrom_program_legacy(from_child, pid, fr_time_delta_from_sec(10), buf, sizeof(buf));
970  if (len < 0) {
971  /* radius_readfrom_program_legacy will have closed from_child for us */
972  REDEBUG("Failure reading from child");
973  return -1;
974  }
975  close(from_child);
976  from_child = -1;
977 
978  buf[len] = 0;
979  RDEBUG2("ntlm_auth said: %s", buf);
980 
981  child_pid = waitpid(pid, &status, 0);
982  if (child_pid == 0) {
983  REDEBUG("Timeout waiting for child");
984  return -1;
985  }
986  if (child_pid != pid) {
987  REDEBUG("Abnormal exit status: %s", fr_syserror(errno));
988  return -1;
989  }
990 
991  if (strstr(buf, "Password-Change: Yes")) {
992  RDEBUG2("ntlm_auth password change succeeded");
993  return 0;
994  }
995 
996  pmsg = strstr(buf, "Password-Change-Error: ");
997  if (pmsg) {
998  emsg = strsep(&pmsg, "\n");
999  } else {
1000  emsg = "could not find error";
1001  }
1002  REDEBUG("ntlm auth password change failed: %s", emsg);
1003 
1004 ntlm_auth_err:
1005  /* safe because these either need closing or are == -1 */
1006  close(to_child);
1007  close(from_child);
1008 
1009  return -1;
1010 
1011  /*
1012  * Decrypt the new password blob, add it as a temporary request
1013  * variable, xlat the local_cpw string, then remove it
1014  *
1015  * this allows is to write e..g
1016  *
1017  * %sql(insert into ...)
1018  *
1019  * ...or...
1020  *
1021  * %exec('/path/to', %mschap(User-Name), %{MS-CHAP-New-Cleartext-Password})"
1022  *
1023  */
1024 #ifdef WITH_TLS
1025  } else if (auth_ctx->env_data->local_cpw) {
1026  RDEBUG2("Doing MS-CHAPv2 password change locally");
1027 
1028  vb = fr_value_box_list_head(&cpw_ctx->local_cpw_result);
1029 
1030  if (!vb){
1031  return -1;
1032  } else if (vb->vb_length == 0) {
1033  REDEBUG("Local MS-CHAPv2 password change - didn't give any result, assuming failure");
1034  return -1;
1035  }
1036 
1037  RDEBUG2("MS-CHAPv2 password change succeeded: %pV", vb);
1038 
1039  /*
1040  * Update the Password.NT attribute with the new hash this lets us
1041  * fall through to the authentication code using the new hash,
1042  * not the old one.
1043  */
1044  fr_pair_value_memdup(auth_ctx->nt_password, auth_ctx->cpw_ctx->new_hash->vp_octets,
1045  auth_ctx->cpw_ctx->new_hash->vp_length, false);
1046 
1047  /*
1048  * Rock on! password change succeeded.
1049  */
1050  return 0;
1051 #endif
1052  } else {
1053  REDEBUG("MS-CHAPv2 password change not configured");
1054  }
1055 
1056  return -1;
1057 }
1058 
1059 /*
1060  * Do the MS-CHAP stuff.
1061  *
1062  * This function is here so that all of the MS-CHAP related
1063  * authentication is in one place, and we can perhaps later replace
1064  * it with code to call winbindd, or something similar.
1065  */
1066 static int CC_HINT(nonnull (1, 2, 4, 5, 6)) do_mschap(rlm_mschap_t const *inst,
1067  request_t *request,
1068  fr_pair_t *password,
1069  uint8_t const *challenge,
1070  uint8_t const *response,
1071  uint8_t nthashhash[static NT_DIGEST_LENGTH],
1072  MSCHAP_AUTH_METHOD method,
1073 #ifdef WITH_AUTH_WINBIND
1074  mschap_auth_call_env_t *env_data)
1075 #else
1076  UNUSED mschap_auth_call_env_t *env_data)
1077 #endif
1078 {
1079  uint8_t calculated[24];
1080 
1081  memset(nthashhash, 0, NT_DIGEST_LENGTH);
1082 
1083  switch (method) {
1084  case AUTH_INTERNAL:
1085  case AUTH_AUTO:
1086  /*
1087  * Do normal authentication.
1088  */
1089  {
1090  /*
1091  * No password: can't do authentication.
1092  */
1093  if (!password) {
1094  if (method == AUTH_AUTO) goto do_ntlm;
1095 
1096  REDEBUG("FAILED: No Password.NT/LM. Cannot perform authentication");
1097  return -1;
1098  }
1099 
1100  smbdes_mschap(password->vp_octets, challenge, calculated);
1101  if (fr_digest_cmp(response, calculated, 24) != 0) {
1102  return -1;
1103  }
1104 
1105  /*
1106  * If the password exists, and is an NT-Password,
1107  * then calculate the hash of the NT hash. Doing this
1108  * here minimizes work for later.
1109  */
1110  if (password->da == attr_nt_password) fr_md4_calc(nthashhash, password->vp_octets, MD4_DIGEST_LENGTH);
1111  break;
1112  }
1113  case AUTH_NTLMAUTH_EXEC:
1114  do_ntlm:
1115  /*
1116  * Run ntlm_auth
1117  */
1118  {
1119  int result;
1120  char buffer[256];
1121  size_t len;
1122 
1123  /*
1124  * Run the program, and expect that we get 16
1125  */
1126  result = radius_exec_program_legacy(buffer, sizeof(buffer), request, inst->ntlm_auth, NULL,
1127  true, true, inst->ntlm_auth_timeout);
1128  if (result != 0) {
1129  char *p;
1130 
1131  /*
1132  * Do checks for numbers, which are
1133  * language neutral. They're also
1134  * faster.
1135  */
1136  p = strcasestr(buffer, "0xC0000");
1137  if (p) {
1138  result = 0;
1139 
1140  p += 7;
1141  if (strcmp(p, "224") == 0) {
1142  result = -648;
1143 
1144  } else if (strcmp(p, "234") == 0) {
1145  result = -647;
1146 
1147  } else if (strcmp(p, "072") == 0) {
1148  result = -691;
1149 
1150  } else if (strcasecmp(p, "05E") == 0) {
1151  result = -2;
1152  }
1153 
1154  if (result != 0) {
1155  REDEBUG2("%s", buffer);
1156  return result;
1157  }
1158 
1159  /*
1160  * Else fall through to more ridiculous checks.
1161  */
1162  }
1163 
1164  /*
1165  * Look for variants of expire password.
1166  */
1167  if (strcasestr(buffer, "0xC0000224") ||
1168  strcasestr(buffer, "Password expired") ||
1169  strcasestr(buffer, "Password has expired") ||
1170  strcasestr(buffer, "Password must be changed") ||
1171  strcasestr(buffer, "Must change password")) {
1172  return -648;
1173  }
1174 
1175  if (strcasestr(buffer, "0xC0000234") ||
1176  strcasestr(buffer, "Account locked out")) {
1177  REDEBUG2("%s", buffer);
1178  return -647;
1179  }
1180 
1181  if (strcasestr(buffer, "0xC0000072") ||
1182  strcasestr(buffer, "Account disabled")) {
1183  REDEBUG2("%s", buffer);
1184  return -691;
1185  }
1186 
1187  if (strcasestr(buffer, "0xC000005E") ||
1188  strcasestr(buffer, "No logon servers")) {
1189  REDEBUG2("%s", buffer);
1190  return -2;
1191  }
1192 
1193  if (strcasestr(buffer, "could not obtain winbind separator") ||
1194  strcasestr(buffer, "Reading winbind reply failed")) {
1195  REDEBUG2("%s", buffer);
1196  return -2;
1197  }
1198 
1199  RDEBUG2("External script failed");
1200  p = strchr(buffer, '\n');
1201  if (p) *p = '\0';
1202 
1203  REDEBUG("External script says: %s", buffer);
1204  return -1;
1205  }
1206 
1207  /*
1208  * Parse the answer as an nthashhash.
1209  *
1210  * ntlm_auth currently returns:
1211  * NT_KEY: 000102030405060708090a0b0c0d0e0f
1212  */
1213  if (memcmp(buffer, "NT_KEY: ", 8) != 0) {
1214  REDEBUG("Invalid output from ntlm_auth: expecting 'NT_KEY: ' prefix");
1215  return -1;
1216  }
1217 
1218  /*
1219  * Check the length. It should be at least 32, with an LF at the end.
1220  */
1221  len = strlen(buffer + 8);
1222  if (len < 32) {
1223  REDEBUG2("Invalid output from ntlm_auth: NT_KEY too short, expected 32 bytes got %zu bytes",
1224  len);
1225 
1226  return -1;
1227  }
1228 
1229  /*
1230  * Update the NT hash hash, from the NT key.
1231  */
1232  if (fr_base16_decode(NULL, &FR_DBUFF_TMP(nthashhash, NT_DIGEST_LENGTH),
1233  &FR_SBUFF_IN(buffer + 8, len), false) != NT_DIGEST_LENGTH) {
1234  REDEBUG("Invalid output from ntlm_auth: NT_KEY has non-hex values");
1235  return -1;
1236  }
1237 
1238  break;
1239  }
1240 #ifdef WITH_AUTH_WINBIND
1241  case AUTH_WBCLIENT:
1242  /*
1243  * Process auth via the wbclient library
1244  */
1245  return do_auth_wbclient(inst, request, challenge, response, nthashhash, env_data);
1246 #endif
1247  default:
1248  /* We should never reach this line */
1249  RERROR("Internal error: Unknown mschap auth method (%d)", method);
1250  return -1;
1251  }
1252 
1253  return 0;
1254 }
1255 
1256 
1257 /*
1258  * Data for the hashes.
1259  */
1260 static const uint8_t SHSpad1[40] =
1261  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1262  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1263  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1264  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
1265 
1266 static const uint8_t SHSpad2[40] =
1267  { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
1268  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
1269  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
1270  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
1271 
1272 static const uint8_t magic1[27] =
1273  { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
1274  0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
1275  0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
1276 
1277 static const uint8_t magic2[84] =
1278  { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
1279  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
1280  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
1281  0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
1282  0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
1283  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
1284  0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
1285  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
1286  0x6b, 0x65, 0x79, 0x2e };
1287 
1288 static const uint8_t magic3[84] =
1289  { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
1290  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
1291  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
1292  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
1293  0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
1294  0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
1295  0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
1296  0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
1297  0x6b, 0x65, 0x79, 0x2e };
1298 
1299 
1300 static void mppe_GetMasterKey(uint8_t const *nt_hashhash, uint8_t const *nt_response,
1301  uint8_t *masterkey)
1302 {
1303  uint8_t digest[20];
1304  fr_sha1_ctx Context;
1305 
1306  fr_sha1_init(&Context);
1307  fr_sha1_update(&Context, nt_hashhash, NT_DIGEST_LENGTH);
1308  fr_sha1_update(&Context, nt_response, 24);
1309  fr_sha1_update(&Context, magic1, 27);
1310  fr_sha1_final(digest, &Context);
1311 
1312  memcpy(masterkey, digest, 16); //-V512
1313 }
1314 
1315 
1316 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey, uint8_t *sesskey,
1317  int keylen, int issend)
1318 {
1319  uint8_t digest[20];
1320  const uint8_t *s;
1321  fr_sha1_ctx Context;
1322 
1323  memset(digest, 0, 20);
1324 
1325  if(issend) {
1326  s = magic3;
1327  } else {
1328  s = magic2;
1329  }
1330 
1331  fr_sha1_init(&Context);
1332  fr_sha1_update(&Context, masterkey, 16);
1333  fr_sha1_update(&Context, SHSpad1, 40);
1334  fr_sha1_update(&Context, s, 84);
1335  fr_sha1_update(&Context, SHSpad2, 40);
1336  fr_sha1_final(digest, &Context);
1337 
1338  memcpy(sesskey, digest, keylen);
1339 }
1340 
1341 
1342 static void mppe_chap2_get_keys128(uint8_t const *nt_hashhash, uint8_t const *nt_response,
1343  uint8_t *sendkey, uint8_t *recvkey)
1344 {
1345  uint8_t masterkey[16];
1346 
1347  mppe_GetMasterKey(nt_hashhash, nt_response, masterkey);
1348 
1349  mppe_GetAsymmetricStartKey(masterkey, sendkey, 16, 1);
1350  mppe_GetAsymmetricStartKey(masterkey, recvkey, 16, 0);
1351 }
1352 
1353 /*
1354  * Generate MPPE keys.
1355  */
1356 static void mppe_chap2_gen_keys128(uint8_t const *nt_hashhash, uint8_t const *response,
1357  uint8_t *sendkey, uint8_t *recvkey)
1358 {
1359  uint8_t enckey1[16];
1360  uint8_t enckey2[16];
1361 
1362  mppe_chap2_get_keys128(nt_hashhash, response, enckey1, enckey2);
1363 
1364  /*
1365  * dictionary.microsoft defines these attributes as
1366  * 'encrypt=2'. The functions in src/lib/radius.c will
1367  * take care of encrypting/decrypting them as appropriate,
1368  * so that we don't have to.
1369  */
1370  memcpy (sendkey, enckey1, 16);
1371  memcpy (recvkey, enckey2, 16);
1372 }
1373 
1374 
1375 /*
1376  * mod_authorize() - authorize user if we can authenticate
1377  * it later. Add Auth-Type attribute if present in module
1378  * configuration (usually Auth-Type must be "MS-CHAP")
1379  */
1380 static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1381 {
1383  mschap_autz_call_env_t *env_data = talloc_get_type_abort(mctx->env_data, mschap_autz_call_env_t);
1384  fr_pair_t *challenge = NULL;
1385  fr_pair_t *parent;
1386 
1387  challenge = fr_pair_find_by_da_nested(&request->request_pairs, NULL, tmpl_attr_tail_da(env_data->chap_challenge));
1388  if (!challenge) RETURN_MODULE_NOOP;
1389 
1390  /*
1391  * The responses MUST be in the same group as the challenge.
1392  */
1393  parent = fr_pair_parent(challenge);
1394  fr_assert(parent != NULL);
1395 
1396  if (!fr_pair_find_by_da(&parent->vp_group, NULL, tmpl_attr_tail_da(env_data->chap_response)) &&
1397  !fr_pair_find_by_da(&parent->vp_group, NULL, tmpl_attr_tail_da(env_data->chap2_response)) &&
1398  (env_data->chap2_cpw &&
1399  !fr_pair_find_by_da(&parent->vp_group, NULL, tmpl_attr_tail_da(env_data->chap2_cpw)))) {
1400  RDEBUG2("Found MS-CHAP-Challenge, but no MS-CHAP response or Change-Password");
1402  }
1403 
1404  if (!inst->auth_type) {
1405  WARN("No 'authenticate %s {...}' section or 'Auth-Type = %s' set. Cannot setup MS-CHAP authentication",
1406  mctx->inst->name, mctx->inst->name);
1408  }
1409 
1411 
1413 }
1414 
1416  unsigned char ident, int mschap_result, int mschap_version, fr_pair_t *smb_ctrl,
1417  mschap_auth_call_env_t *env_data)
1418 {
1419  rlm_rcode_t rcode = RLM_MODULE_OK;
1420  int error = 0;
1421  int retry = 0;
1422  char const *message = NULL;
1423 
1424  int i;
1425  char new_challenge[33], buffer[128];
1426  char *p;
1427 
1428  if ((mschap_result == -648) ||
1429  ((mschap_result == 0) &&
1430  (smb_ctrl && ((smb_ctrl->vp_uint32 & ACB_FR_EXPIRED) != 0)))) {
1431  REDEBUG("Password has expired. User should retry authentication");
1432  error = 648;
1433 
1434  /*
1435  * A password change is NOT a retry! We MUST have retry=0 here.
1436  */
1437  retry = 0;
1438  message = "Password expired";
1439  rcode = RLM_MODULE_REJECT;
1440 
1441  /*
1442  * Account is disabled.
1443  *
1444  * They're found, but they don't exist, so we
1445  * return 'not found'.
1446  */
1447  } else if ((mschap_result == -691) ||
1448  (smb_ctrl && (((smb_ctrl->vp_uint32 & ACB_DISABLED) != 0) ||
1449  ((smb_ctrl->vp_uint32 & (ACB_NORMAL|ACB_WSTRUST)) == 0)))) {
1450  REDEBUG("SMB-Account-Ctrl (or ntlm_auth) "
1451  "says that the account is disabled, "
1452  "or is not a normal or workstation trust account");
1453  error = 691;
1454  retry = 0;
1455  message = "Account disabled";
1456  rcode = RLM_MODULE_NOTFOUND;
1457 
1458  /*
1459  * User is locked out.
1460  */
1461  } else if ((mschap_result == -647) ||
1462  (smb_ctrl && ((smb_ctrl->vp_uint32 & ACB_AUTOLOCK) != 0))) {
1463  REDEBUG("SMB-Account-Ctrl (or ntlm_auth) "
1464  "says that the account is locked out");
1465  error = 647;
1466  retry = 0;
1467  message = "Account locked out";
1468  rcode = RLM_MODULE_DISALLOW;
1469 
1470  } else if (mschap_result < 0) {
1471  REDEBUG("%s is incorrect", mschap_version == 1 ? env_data->chap_response->name : env_data->chap2_response->name);
1472  error = 691;
1473  retry = inst->allow_retry;
1474  message = "Authentication failed";
1475  rcode = RLM_MODULE_REJECT;
1476  }
1477 
1478  if (rcode == RLM_MODULE_OK) RETURN_MODULE_OK;
1479 
1480  switch (mschap_version) {
1481  case 1:
1482  for (p = new_challenge, i = 0; i < 2; i++) p += snprintf(p, 9, "%08x", fr_rand());
1483  snprintf(buffer, sizeof(buffer), "E=%i R=%i C=%s V=2",
1484  error, retry, new_challenge);
1485  break;
1486 
1487  case 2:
1488  for (p = new_challenge, i = 0; i < 4; i++) p += snprintf(p, 9, "%08x", fr_rand());
1489  snprintf(buffer, sizeof(buffer), "E=%i R=%i C=%s V=3 M=%s",
1490  error, retry, new_challenge, message);
1491  break;
1492 
1493  default:
1495  }
1496  if (env_data->chap_error) mschap_add_reply(request, ident, tmpl_attr_tail_da(env_data->chap_error),
1497  buffer, strlen(buffer));
1498 
1499  RETURN_MODULE_RCODE(rcode);
1500 }
1501 
1502 
1503 /** Find a Password.NT value, or create one from a Password.Cleartext, or Password.With-Header attribute
1504  *
1505  * @param[in] ctx to allocate ephemeral passwords in.
1506  * @param[out] out Our new Password.NT.
1507  * @param[in] inst Module configuration.
1508  * @param[in] request The current request.
1509  * @return
1510  * - 0 on success.
1511  * - -1 on failure.
1512  */
1513 static int CC_HINT(nonnull(1, 2, 3)) nt_password_find(TALLOC_CTX *ctx, fr_pair_t **out,
1514  rlm_mschap_t const *inst, request_t *request)
1515 {
1516  fr_pair_t *password;
1517  fr_dict_attr_t const *allowed_passwords[] = { attr_cleartext_password, attr_nt_password };
1518  bool ephemeral;
1519 
1520  *out = NULL; /* Init output pointer */
1521 
1522  password = password_find(&ephemeral, ctx, request,
1523  allowed_passwords, NUM_ELEMENTS(allowed_passwords), inst->normify);
1524  if (!password) {
1525  if (inst->method == AUTH_INTERNAL) {
1526  /*
1527  * Search for passwords in the parent
1528  * FIXME: This is a hack and should be removed
1529  * When EAP-MSCHAPv2 supports sections.
1530  */
1531  if (request->parent) {
1532  password = password_find(&ephemeral, ctx, request->parent,
1533  allowed_passwords,
1534  NUM_ELEMENTS(allowed_passwords), inst->normify);
1535  if (password) goto found_password;
1536  }
1537 
1538  /*
1539  * If we're doing internal auth, then this is an issue
1540  */
1541  RWDEBUG2("No &control.%s or &control.%s found. Cannot create Password.NT",
1543  return -1;
1544 
1545  /*
1546  * ..if we're not, then we can call out to external sources.
1547  */
1548  } else {
1549  return 0;
1550  }
1551  }
1552 
1553 found_password:
1554  if (password->da == attr_cleartext_password) {
1555  uint8_t *p;
1556  int ret;
1557  fr_pair_t *nt_password;
1558 
1559  MEM(nt_password = fr_pair_afrom_da(ctx, attr_nt_password));
1560  MEM(fr_pair_value_mem_alloc(nt_password, &p, NT_DIGEST_LENGTH, false) == 0);
1561  ret = mschap_nt_password_hash(p, password->vp_strvalue);
1562 
1563  if (ret < 0) {
1564  RERROR("Failed generating Password.NT");
1565  talloc_free(nt_password);
1566  if (ephemeral) TALLOC_FREE(password);
1567  return -1;
1568  }
1569 
1570  if (RDEBUG_ENABLED3) {
1571  RDEBUG3("Hashed &control.%pP to create %s = %pV",
1572  password, attr_nt_password->name, fr_box_octets(p, NT_DIGEST_LENGTH));
1573  } else {
1574  RDEBUG2("Hashed &control.%s to create %s", attr_nt_password->name, password->da->name);
1575  }
1576 
1577  if (ephemeral) TALLOC_FREE(password);
1578 
1579  *out = nt_password;
1580 
1581  return 0;
1582  }
1583 
1584  fr_assert(password->da == attr_nt_password);
1585 
1586  if (RDEBUG_ENABLED3) {
1587  RDEBUG3("Found &control.%pP", password);
1588  } else {
1589  RDEBUG2("Found &control.%s", attr_nt_password->name);
1590  }
1591  *out = password;
1592 
1593  return 0;
1594 }
1595 
1596 /*
1597  * mschap_cpw_request_process() - do the work to handle an MS-CHAP password
1598  * change request.
1599  */
1601  rlm_mschap_t const *inst,
1602  request_t *request,
1603  mschap_auth_ctx_t *auth_ctx)
1604 {
1605  mschap_auth_call_env_t *env_data = auth_ctx->env_data;
1606  mschap_cpw_ctx_t *cpw_ctx = auth_ctx->cpw_ctx;
1607 
1608  /*
1609  * Perform the actual password change
1610  */
1611  if (do_mschap_cpw(inst, request, auth_ctx, cpw_ctx->new_nt_encrypted, cpw_ctx->old_nt_hash) < 0) {
1612  char buffer[128];
1613 
1614  REDEBUG("Password change failed");
1615 
1616  if (env_data->chap_error) {
1617  snprintf(buffer, sizeof(buffer), "E=709 R=0 M=Password change failed");
1618  mschap_add_reply(request, auth_ctx->cpw->vp_octets[1],
1619  tmpl_attr_tail_da(env_data->chap_error), buffer, strlen(buffer));
1620  }
1621 
1623  }
1624 
1625  RDEBUG2("Password change successful");
1626 
1628 }
1629 
1630 /** Validate data required for change password requests.
1631  *
1632  */
1633 static int mschap_cpw_prepare(request_t *request, mschap_auth_ctx_t *auth_ctx)
1634 {
1635  mschap_auth_call_env_t *env_data = auth_ctx->env_data;
1636  mschap_cpw_ctx_t *cpw_ctx;
1637  fr_pair_t *nt_enc = NULL;
1638  int seq, new_nt_enc_len;
1639 
1640  /*
1641  * mschap2 password change request.
1642  *
1643  * We cheat - first decode and execute the passchange.
1644  * Then extract the response, add it into the request
1645  * and then jump into mschap2 auth with the challenge/
1646  * response.
1647  */
1648  RDEBUG2("MS-CHAPv2 password change request received");
1649 
1650  if (auth_ctx->cpw->vp_length != 68) {
1651  REDEBUG("%s has the wrong format: length %zu != 68", env_data->chap2_cpw->name, auth_ctx->cpw->vp_length);
1652  return -1;
1653  }
1654 
1655  if (auth_ctx->cpw->vp_octets[0] != 7) {
1656  REDEBUG("%s has the wrong format: code %d != 7", env_data->chap2_cpw->name, auth_ctx->cpw->vp_octets[0]);
1657  return -1;
1658  }
1659 
1660  MEM(auth_ctx->cpw_ctx = talloc_zero(auth_ctx, mschap_cpw_ctx_t));
1661  cpw_ctx = auth_ctx->cpw_ctx;
1662 
1663  /*
1664  * Look for the new (encrypted) password.
1665  *
1666  * Bah, stupid composite attributes...
1667  * we're expecting 3 attributes with the leading bytes -
1668  * 06:<mschapid>:00:01:<1st chunk>
1669  * 06:<mschapid>:00:02:<2nd chunk>
1670  * 06:<mschapid>:00:03:<3rd chunk>
1671  */
1672  new_nt_enc_len = 0;
1673  for (seq = 1; seq < 4; seq++) {
1674  int found = 0;
1675 
1676  while ((nt_enc = fr_pair_find_by_da_nested(&request->request_pairs, nt_enc,
1677  tmpl_attr_tail_da(env_data->chap_nt_enc_pw)))) {
1678  if (nt_enc->vp_length < 4) {
1679  REDEBUG("%s with invalid format", env_data->chap_nt_enc_pw->name);
1680  return -1;
1681  }
1682 
1683  if (nt_enc->vp_octets[0] != 6) {
1684  REDEBUG("%s with invalid format", env_data->chap_nt_enc_pw->name);
1685  return -1;
1686  }
1687 
1688  if ((nt_enc->vp_octets[2] == 0) && (nt_enc->vp_octets[3] == seq)) {
1689  found = 1;
1690  break;
1691  }
1692  }
1693 
1694  if (!found) {
1695  REDEBUG("Could not find %s with sequence number %d", env_data->chap_nt_enc_pw->name, seq);
1696  return -1;
1697  }
1698 
1699  if ((new_nt_enc_len + nt_enc->vp_length - 4) > sizeof(cpw_ctx->new_nt_encrypted)) {
1700  REDEBUG("Unpacked %s length > 516", env_data->chap_nt_enc_pw->name);
1701  return -1;
1702  }
1703 
1704  memcpy(cpw_ctx->new_nt_encrypted + new_nt_enc_len, nt_enc->vp_octets + 4, nt_enc->vp_length - 4);
1705  new_nt_enc_len += nt_enc->vp_length - 4;
1706  }
1707 
1708  if (new_nt_enc_len != 516) {
1709  REDEBUG("Unpacked %s length is %d - should be 516", env_data->chap_nt_enc_pw->name, new_nt_enc_len);
1710  return -1;
1711  }
1712 
1713  /*
1714  * RFC 2548 is confusing here. It claims:
1715  *
1716  * 1 byte code
1717  * 1 byte ident
1718  * 16 octets - old hash encrypted with new hash
1719  * 24 octets - peer challenge
1720  * this is actually:
1721  * 16 octets - peer challenge
1722  * 8 octets - reserved
1723  * 24 octets - nt response
1724  * 2 octets - flags (ignored)
1725  */
1726 
1727  memcpy(cpw_ctx->old_nt_hash, auth_ctx->cpw->vp_octets + 2, sizeof(cpw_ctx->old_nt_hash));
1728 
1729  RDEBUG2("Password change payload valid");
1730  return 0;
1731 }
1732 
1733 static CC_HINT(nonnull(1,2,3,4,5,8,9)) unlang_action_t mschap_process_response(rlm_rcode_t *p_result,
1734  int *mschap_version,
1735  uint8_t nthashhash[static NT_DIGEST_LENGTH],
1736  rlm_mschap_t const *inst,
1737  request_t *request,
1738  fr_pair_t *smb_ctrl,
1739  fr_pair_t *nt_password,
1740  fr_pair_t *challenge,
1741  fr_pair_t *response,
1742  MSCHAP_AUTH_METHOD method,
1743  mschap_auth_call_env_t *env_data)
1744 {
1745  int offset;
1746  rlm_rcode_t mschap_result;
1747 
1748  *mschap_version = 1;
1749 
1750  RDEBUG2("Processing MS-CHAPv1 response");
1751 
1752  /*
1753  * MS-CHAPv1 challenges are 8 octets.
1754  */
1755  if (challenge->vp_length < 8) {
1756  REDEBUG("%s has the wrong format", env_data->chap_challenge->name);
1758  }
1759 
1760  /*
1761  * Responses are 50 octets.
1762  */
1763  if (response->vp_length < 50) {
1764  REDEBUG("%s has the wrong format", env_data->chap_response->name);
1766  }
1767 
1768  /*
1769  * We are doing MS-CHAP. Calculate the MS-CHAP
1770  * response
1771  */
1772  if (!(response->vp_octets[1] & 0x01)) {
1773  REDEBUG2("Client used unsupported method LM-Password");
1775  }
1776 
1777  offset = 26;
1778 
1779  /*
1780  * Do the MS-CHAP authentication.
1781  */
1782  mschap_result = do_mschap(inst, request, nt_password, challenge->vp_octets,
1783  response->vp_octets + offset, nthashhash, method, env_data);
1784 
1785  /*
1786  * Check for errors, and add MSCHAP-Error if necessary.
1787  */
1788  return mschap_error(p_result, inst, request, *response->vp_octets, mschap_result, *mschap_version, smb_ctrl, env_data);
1789 }
1790 
1791 static unlang_action_t CC_HINT(nonnull(1,2,3,4,5,8,9)) mschap_process_v2_response(rlm_rcode_t *p_result,
1792  int *mschap_version,
1793  uint8_t nthashhash[static NT_DIGEST_LENGTH],
1794  rlm_mschap_t const *inst,
1795  request_t *request,
1796  fr_pair_t *smb_ctrl,
1797  fr_pair_t *nt_password,
1798  fr_pair_t *challenge,
1799  fr_pair_t *response,
1800  MSCHAP_AUTH_METHOD method,
1801  mschap_auth_call_env_t *env_data)
1802 {
1803  uint8_t mschap_challenge[16];
1804  fr_pair_t *user_name, *name_vp, *response_name, *peer_challenge_attr;
1805  uint8_t const *peer_challenge;
1806  char const *username_str;
1807  size_t username_len;
1808  int mschap_result;
1809  rlm_rcode_t rcode;
1810  char msch2resp[42];
1811 
1812  *mschap_version = 2;
1813 
1814  RDEBUG2("Processing MS-CHAPv2 response");
1815 
1816  /*
1817  * MS-CHAPv2 challenges are 16 octets.
1818  */
1819  if (challenge->vp_length < 16) {
1820  REDEBUG("%s has the wrong format", env_data->chap_challenge->name);
1822  }
1823 
1824  /*
1825  * Responses are 50 octets.
1826  */
1827  if (response->vp_length < 50) {
1828  REDEBUG("%s has the wrong format", env_data->chap2_response->name);
1830  }
1831 
1832  /*
1833  * We also require a User-Name
1834  */
1835  user_name = mschap_identity_find(request, tmpl_attr_tail_da(env_data->username));
1836  if (!user_name) RETURN_MODULE_FAIL;
1837 
1838  /*
1839  * Check for MS-CHAP-User-Name and if found, use it
1840  * to construct the MSCHAPv1 challenge. This is
1841  * set by rlm_eap_mschap to the MS-CHAP Response
1842  * packet Name field.
1843  *
1844  * We prefer this to the User-Name in the
1845  * packet.
1846  */
1847  response_name = fr_pair_find_by_da(&request->request_pairs, NULL, attr_ms_chap_user_name);
1848  name_vp = response_name ? response_name : user_name;
1849 
1850  /*
1851  * with_ntdomain_hack moved here, too.
1852  */
1853  if ((username_str = strchr(name_vp->vp_strvalue, '\\')) != NULL) {
1854  if (inst->with_ntdomain_hack) {
1855  username_str++;
1856  } else {
1857  RWDEBUG2("NT Domain delimiter found, should 'with_ntdomain_hack' be enabled?");
1858  username_str = name_vp->vp_strvalue;
1859  }
1860  } else {
1861  username_str = name_vp->vp_strvalue;
1862  }
1863  username_len = name_vp->vp_length - (username_str - name_vp->vp_strvalue);
1864 
1865  if (response_name && ((user_name->vp_length != response_name->vp_length) ||
1866  (strncasecmp(user_name->vp_strvalue, response_name->vp_strvalue, user_name->vp_length) != 0))) {
1867  RWDEBUG("%pP is not the same as %pP from EAP-MSCHAPv2", user_name, response_name);
1868  }
1869 
1870 #ifdef __APPLE__
1871  /*
1872  * No "known good" Password.NT attribute. Try to do
1873  * OpenDirectory authentication.
1874  *
1875  * If OD determines the user is an AD user it will return noop, which
1876  * indicates the auth process should continue directly to AD.
1877  * Otherwise OD will determine auth success/fail.
1878  */
1879  if (!nt_password && inst->open_directory) {
1880  RDEBUG2("No Password.NT available. Trying OpenDirectory Authentication");
1881  od_mschap_auth(&rcode, request, challenge, user_name, env_data);
1882  if (rcode != RLM_MODULE_NOOP) RETURN_MODULE_RCODE(rcode);
1883  }
1884 #endif
1885  peer_challenge = response->vp_octets + 2;
1886 
1887  peer_challenge_attr = fr_pair_find_by_da(&request->control_pairs, NULL, attr_ms_chap_peer_challenge);
1888  if (peer_challenge_attr) {
1889  RDEBUG2("Overriding peer challenge");
1890  peer_challenge = peer_challenge_attr->vp_octets;
1891  }
1892 
1893  /*
1894  * The old "mschapv2" function has been moved to
1895  * here.
1896  *
1897  * MS-CHAPv2 takes some additional data to create an
1898  * MS-CHAPv1 challenge, and then does MS-CHAPv1.
1899  */
1900  RDEBUG2("Creating challenge with username \"%pV\"",
1901  fr_box_strvalue_len(username_str, username_len));
1902  mschap_challenge_hash(mschap_challenge, /* resulting challenge */
1903  peer_challenge, /* peer challenge */
1904  challenge->vp_octets, /* our challenge */
1905  username_str, username_len); /* user name */
1906 
1907  mschap_result = do_mschap(inst, request, nt_password, mschap_challenge,
1908  response->vp_octets + 26, nthashhash, method, env_data);
1909 
1910  /*
1911  * Check for errors, and add MSCHAP-Error if necessary.
1912  */
1913  mschap_error(&rcode, inst, request, *response->vp_octets,
1914  mschap_result, *mschap_version, smb_ctrl, env_data);
1915  if (rcode != RLM_MODULE_OK) RETURN_MODULE_RCODE(rcode);
1916 
1917 #ifdef WITH_AUTH_WINBIND
1918  if (inst->wb_retry_with_normalised_username) {
1919  response_name = fr_pair_find_by_da(&request->request_pairs, NULL, attr_ms_chap_user_name);
1920  if (response_name) {
1921  if (strcmp(username_str, response_name->vp_strvalue)) {
1922  RDEBUG2("Normalising username %pV -> %pV",
1923  fr_box_strvalue_len(username_str, username_len),
1924  &response_name->data);
1925  username_str = response_name->vp_strvalue;
1926  }
1927  }
1928  }
1929 #endif
1930 
1931  mschap_auth_response(username_str, /* without the domain */
1932  username_len, /* Length of username str */
1933  nthashhash, /* nt-hash-hash */
1934  response->vp_octets + 26, /* peer response */
1935  peer_challenge, /* peer challenge */
1936  challenge->vp_octets, /* our challenge */
1937  msch2resp); /* calculated MPPE key */
1938  if (env_data->chap2_success) mschap_add_reply(request, *response->vp_octets,
1939  tmpl_attr_tail_da(env_data->chap2_success), msch2resp, 42);
1940 
1942 }
1943 
1944 /** Complete mschap authentication after any tmpls have been expanded.
1945  *
1946  */
1947 static unlang_action_t mod_authenticate_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
1948 {
1949  mschap_auth_ctx_t *auth_ctx = talloc_get_type_abort(uctx, mschap_auth_ctx_t);
1950  mschap_auth_call_env_t *env_data = talloc_get_type_abort(auth_ctx->env_data, mschap_auth_call_env_t);
1952  fr_pair_t *challenge = NULL;
1953  fr_pair_t *response = NULL;
1954  fr_pair_t *parent;
1955  uint8_t nthashhash[NT_DIGEST_LENGTH];
1956  int mschap_version = 0;
1957  rlm_rcode_t rcode = RLM_MODULE_OK;
1958 
1959  if (auth_ctx->cpw) {
1960  uint8_t *p;
1961 
1962  /*
1963  * Password change does require the NT password
1964  */
1965  if (!auth_ctx->nt_password) {
1966  REDEBUG("Missing Password.NT - required for change password request");
1968  }
1969  if (!env_data->chap_nt_enc_pw) {
1970  REDEBUG("chap_nt_enc_pw option is not set - required for change password request");
1972  }
1973 
1974  mschap_process_cpw_request(&rcode, inst, request, auth_ctx);
1975  if (rcode != RLM_MODULE_OK) goto finish;
1976 
1977  /*
1978  * Clear any expiry bit so the user can now login;
1979  * obviously the password change action will need
1980  * to have cleared this bit in the config/SQL/wherever.
1981  */
1982  if (auth_ctx->smb_ctrl && auth_ctx->smb_ctrl->vp_uint32 & ACB_FR_EXPIRED) {
1983  RDEBUG2("Clearing expiry bit in SMB-Acct-Ctrl to allow authentication");
1984  auth_ctx->smb_ctrl->vp_uint32 &= ~ACB_FR_EXPIRED;
1985  }
1986 
1987  /*
1988  * Extract the challenge & response from the end of the
1989  * password change, add them into the request and then
1990  * continue with the authentication.
1991  */
1992  MEM(pair_update_request(&response, tmpl_attr_tail_da(env_data->chap2_response)) >= 0);
1993  MEM(fr_pair_value_mem_alloc(response, &p, 50, auth_ctx->cpw->vp_tainted) == 0);
1994 
1995  /* ident & flags */
1996  p[0] = auth_ctx->cpw->vp_octets[1];
1997  p[1] = 0;
1998  /* peer challenge and client NT response */
1999  memcpy(p + 2, auth_ctx->cpw->vp_octets + 18, 48);
2000  }
2001 
2002  challenge = fr_pair_find_by_da_nested(&request->request_pairs, NULL, tmpl_attr_tail_da(env_data->chap_challenge));
2003  if (!challenge) {
2004  REDEBUG("&control.Auth-Type = %s set for a request that does not contain &%s",
2005  auth_ctx->name, env_data->chap_challenge->name);
2006  rcode = RLM_MODULE_INVALID;
2007  goto finish;
2008  }
2009 
2010  /*
2011  * The responses MUST be in the same group as the challenge.
2012  */
2013  parent = fr_pair_parent(challenge);
2014  fr_assert(parent != NULL);
2015 
2016  /*
2017  * We also require an MS-CHAP-Response.
2018  */
2019  if ((response = fr_pair_find_by_da(&parent->vp_group, NULL, tmpl_attr_tail_da(env_data->chap_response)))) {
2020  mschap_process_response(&rcode,
2021  &mschap_version, nthashhash,
2022  inst, request,
2023  auth_ctx->smb_ctrl, auth_ctx->nt_password,
2024  challenge, response,
2025  auth_ctx->method, auth_ctx->env_data);
2026  if (rcode != RLM_MODULE_OK) goto finish;
2027  } else if ((response = fr_pair_find_by_da_nested(&parent->vp_group, NULL, tmpl_attr_tail_da(env_data->chap2_response)))) {
2029  &mschap_version, nthashhash,
2030  inst, request,
2031  auth_ctx->smb_ctrl, auth_ctx->nt_password,
2032  challenge, response,
2033  auth_ctx->method, auth_ctx->env_data);
2034  if (rcode != RLM_MODULE_OK) goto finish;
2035  } else { /* Neither CHAPv1 or CHAPv2 response: die */
2036  REDEBUG("&control.Auth-Type = %s set for a request that does not contain &%s or &%s attributes",
2037  auth_ctx->name, env_data->chap_response->name, env_data->chap2_response->name);
2038  rcode = RLM_MODULE_INVALID;
2039  goto finish;
2040  }
2041 
2042  /* now create MPPE attributes */
2043  if (inst->use_mppe) {
2044  fr_pair_t *vp;
2045  uint8_t mppe_sendkey[34];
2046  uint8_t mppe_recvkey[34];
2047 
2048  switch (mschap_version) {
2049  case 1:
2050  RDEBUG2("Generating MS-CHAPv1 MPPE keys");
2051  memset(mppe_sendkey, 0, 32);
2052 
2053  /*
2054  * According to RFC 2548 we
2055  * should send NT hash. But in
2056  * practice it doesn't work.
2057  * Instead, we should send nthashhash
2058  *
2059  * This is an error in RFC 2548.
2060  */
2061  /*
2062  * do_mschap cares to zero nthashhash if NT hash
2063  * is not available.
2064  */
2065  memcpy(mppe_sendkey + 8, nthashhash, NT_DIGEST_LENGTH);
2066  mppe_add_reply(inst, request, tmpl_attr_tail_da(env_data->chap_mppe_keys), mppe_sendkey, 24); //-V666
2067  break;
2068 
2069  case 2:
2070  RDEBUG2("Generating MS-CHAPv2 MPPE keys");
2071  mppe_chap2_gen_keys128(nthashhash, response->vp_octets + 26, mppe_sendkey, mppe_recvkey);
2072 
2073  mppe_add_reply(inst, request, tmpl_attr_tail_da(env_data->mppe_recv_key), mppe_recvkey, 16);
2074  mppe_add_reply(inst, request, tmpl_attr_tail_da(env_data->mppe_send_key), mppe_sendkey, 16);
2075  break;
2076 
2077  default:
2078  fr_assert(0);
2079  break;
2080  }
2081 
2083  vp->vp_uint32 = inst->require_encryption ? 2 : 1;
2084 
2086  vp->vp_uint32 = inst->require_strong ? 4 : 6;
2087  } /* else we weren't asked to use MPPE */
2088 
2089 finish:
2090  RETURN_MODULE_RCODE(rcode);
2091 }
2092 
2093 /** When changing passwords using the ntlm_auth helper, evaluate the domain tmpl
2094  *
2095  */
2097  request_t *request, void *uctx)
2098 {
2099  mschap_auth_ctx_t *auth_ctx = talloc_get_type_abort(uctx, mschap_auth_ctx_t);
2100  mschap_auth_call_env_t *env_data = talloc_get_type_abort(auth_ctx->env_data, mschap_auth_call_env_t);
2101 
2102  fr_value_box_list_init(&auth_ctx->cpw_ctx->cpw_domain);
2103  if (unlang_tmpl_push(auth_ctx, &auth_ctx->cpw_ctx->cpw_domain, request,
2104  env_data->ntlm_cpw_domain, NULL) < 0) RETURN_MODULE_FAIL;
2105 
2107 }
2108 
2109 #ifdef WITH_TLS
2110 /** Decrypt the new cleartext password when handling change password requests
2111  *
2112  */
2113 static int mschap_new_pass_decrypt(request_t *request, mschap_auth_ctx_t *auth_ctx)
2114 {
2115  EVP_CIPHER_CTX *evp_ctx;
2116  uint8_t nt_pass_decrypted[516], old_nt_hash_expected[NT_DIGEST_LENGTH];
2117  int c, ntlen = sizeof(nt_pass_decrypted);
2118  size_t passlen, i = 0, len = 0;
2119  char *x;
2120  uint8_t *p, *q;
2121  fr_pair_t *new_pass;
2122 
2123  MEM(evp_ctx = EVP_CIPHER_CTX_new());
2124 
2125  if (unlikely(EVP_EncryptInit_ex(evp_ctx, EVP_rc4(), NULL, auth_ctx->nt_password->vp_octets, NULL) != 1)) {
2126  fr_tls_strerror_printf(NULL);
2127  RPERROR("Failed initialising RC4 ctx");
2128  return -1;
2129  }
2130 
2131  if (unlikely(EVP_CIPHER_CTX_set_key_length(evp_ctx, auth_ctx->nt_password->vp_length)) != 1) {
2132  fr_tls_strerror_printf(NULL);
2133  RPERROR("Failed setting key length");
2134  return -1;
2135  }
2136 
2137  if (unlikely(EVP_EncryptUpdate(evp_ctx, nt_pass_decrypted, &ntlen, auth_ctx->cpw_ctx->new_nt_encrypted, ntlen) != 1)) {
2138  fr_tls_strerror_printf(NULL);
2139  RPERROR("Failed ingesting new password");
2140  return -1;
2141  }
2142 
2143  EVP_CIPHER_CTX_free(evp_ctx);
2144 
2145  /*
2146  * pwblock is
2147  * 512-N bytes random pad
2148  * N bytes password as utf-16-le
2149  * 4 bytes - N as big-endian int
2150  */
2151  passlen = nt_pass_decrypted[512];
2152  passlen += nt_pass_decrypted[513] << 8;
2153  if ((nt_pass_decrypted[514] != 0) ||
2154  (nt_pass_decrypted[515] != 0)) {
2155  REDEBUG("Decrypted new password blob claims length > 65536, probably an invalid Password.NT");
2156  return -1;
2157  }
2158 
2159  /*
2160  * Sanity check - passlen positive and <= 512 if not, crypto has probably gone wrong
2161  */
2162  if (passlen > 512) {
2163  REDEBUG("Decrypted new password blob claims length %zu > 512, "
2164  "probably an invalid Password.NT", passlen);
2165  return -1;
2166  }
2167 
2168  p = nt_pass_decrypted + 512 - passlen;
2169 
2170  /*
2171  * The new NT hash - this should be preferred over the
2172  * cleartext password as it avoids unicode hassles.
2173  */
2175  MEM(fr_pair_value_mem_alloc(auth_ctx->cpw_ctx->new_hash, &q, NT_DIGEST_LENGTH, false) == 0);
2176  fr_md4_calc(q, p, passlen);
2177 
2178  /*
2179  * Check that nt_password encrypted with new_hash
2180  * matches the old_hash value from the client.
2181  */
2182  smbhash(old_nt_hash_expected, auth_ctx->nt_password->vp_octets, q);
2183  smbhash(old_nt_hash_expected + 8, auth_ctx->nt_password->vp_octets + 8, q + 7);
2184  if (memcmp(old_nt_hash_expected, auth_ctx->cpw_ctx->old_nt_hash, NT_DIGEST_LENGTH)!=0) {
2185  REDEBUG("Old NT hash value from client does not match our value");
2186  RHEXDUMP1(old_nt_hash_expected, NT_DIGEST_LENGTH, "expected");
2187  RHEXDUMP1(auth_ctx->cpw_ctx->old_nt_hash, NT_DIGEST_LENGTH, "got");
2188  return -1;
2189  }
2190 
2191  /*
2192  * The new cleartext password, which is utf-16 do some unpleasant vileness
2193  * to turn it into utf8 without pulling in libraries like iconv.
2194  *
2195  * First pass: get the length of the converted string.
2196  */
2198  new_pass->vp_length = 0;
2199 
2200  while (i < passlen) {
2201  c = p[i++];
2202  c += p[i++] << 8;
2203 
2204  /*
2205  * Gah. nasty. maybe we should just pull in iconv?
2206  */
2207  if (c < 0x7f) {
2208  len++;
2209  } else if (c < 0x7ff) {
2210  len += 2;
2211  } else {
2212  len += 3;
2213  }
2214  }
2215 
2216  MEM(fr_pair_value_bstr_alloc(new_pass, &x, len, true) == 0);
2217 
2218  /*
2219  * Second pass: convert the characters from UTF-16 to UTF-8.
2220  */
2221  i = 0;
2222  while (i < passlen) {
2223  c = p[i++];
2224  c += p[i++] << 8;
2225 
2226  /*
2227  * Gah. nasty. maybe we should just pull in iconv?
2228  */
2229  if (c < 0x7f) {
2230  *x++ = c;
2231 
2232  } else if (c < 0x7ff) {
2233  *x++ = 0xc0 + (c >> 6);
2234  *x++ = 0x80 + (c & 0x3f);
2235 
2236  } else {
2237  *x++ = 0xe0 + (c >> 12);
2238  *x++ = 0x80 + ((c>>6) & 0x3f);
2239  *x++ = 0x80 + (c & 0x3f);
2240  }
2241  }
2242 
2243  *x = '\0';
2244  return 0;
2245 }
2246 #endif
2247 
2248 /*
2249  * mod_authenticate() - authenticate user based on given
2250  * attributes and configuration.
2251  * We will try to find out password in configuration
2252  * or in configured passwd file.
2253  * If one is found we will check paraneters given by NAS.
2254  *
2255  * If SMB-Account-Ctrl is not set to ACB_PWNOTREQ we must have
2256  * one of:
2257  * PAP: User-Password or
2258  * MS-CHAP: MS-CHAP-Challenge and MS-CHAP-Response or
2259  * MS-CHAP2: MS-CHAP-Challenge and MS-CHAP2-Response
2260  * In case of password mismatch or locked account we MAY return
2261  * MS-CHAP-Error for MS-CHAP or MS-CHAP v2
2262  * If MS-CHAP2 succeeds we MUST return MS-CHAP2-Success
2263  */
2264 static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
2265 {
2267  mschap_auth_call_env_t *env_data = talloc_get_type_abort(mctx->env_data, mschap_auth_call_env_t);
2268  mschap_auth_ctx_t *auth_ctx;
2269 
2270  MEM(auth_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), mschap_auth_ctx_t));
2271 
2272  /*
2273  * If we have ntlm_auth configured, use it unless told
2274  * otherwise
2275  */
2276  *auth_ctx = (mschap_auth_ctx_t) {
2277  .name = mctx->inst->name,
2278  .inst = inst,
2279  .method = inst->method,
2280  .env_data = env_data,
2281  };
2282 
2283  /*
2284  * If we have an ntlm_auth configuration, then we may
2285  * want to suppress it.
2286  */
2287  if (auth_ctx->method != AUTH_INTERNAL) {
2288  fr_pair_t *vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_ms_chap_use_ntlm_auth);
2289  if (vp && (vp->vp_uint8 <= AUTH_AUTO)) auth_ctx->method = vp->vp_uint8;
2290  }
2291 
2292  /*
2293  * Find the SMB-Account-Ctrl attribute, or the
2294  * SMB-Account-Ctrl-Text attribute.
2295  */
2296  auth_ctx->smb_ctrl = fr_pair_find_by_da(&request->control_pairs, NULL, attr_smb_account_ctrl);
2297  if (!auth_ctx->smb_ctrl) {
2298  fr_pair_t *smb_account_ctrl_text;
2299 
2300  smb_account_ctrl_text = fr_pair_find_by_da(&request->control_pairs, NULL, attr_smb_account_ctrl_text);
2301  if (smb_account_ctrl_text) {
2303  auth_ctx->smb_ctrl->vp_uint32 = pdb_decode_acct_ctrl(smb_account_ctrl_text->vp_strvalue);
2304  }
2305  }
2306 
2307  /*
2308  * We're configured to do MS-CHAP authentication.
2309  * and account control information exists. Enforce it.
2310  */
2311  if (auth_ctx->smb_ctrl) {
2312  /*
2313  * Password is not required.
2314  */
2315  if ((auth_ctx->smb_ctrl->vp_uint32 & ACB_PWNOTREQ) != 0) {
2316  RDEBUG2("SMB-Account-Ctrl says no password is required");
2318  }
2319  }
2320 
2321  /*
2322  * Look for or create an Password.NT
2323  *
2324  * Password.NT can be NULL here if we didn't find an
2325  * input attribute, and we're calling out to an
2326  * external password store.
2327  */
2328  if (nt_password_find(auth_ctx, &auth_ctx->nt_password, mctx->inst->data, request) < 0) RETURN_MODULE_FAIL;
2329 
2330  /*
2331  * Check to see if this is a change password request, and process
2332  * it accordingly if so.
2333  */
2334  if (env_data->chap2_cpw) auth_ctx->cpw = fr_pair_find_by_da_nested(&request->request_pairs, NULL,
2335  tmpl_attr_tail_da(env_data->chap2_cpw));
2336  if (auth_ctx->cpw) {
2337  /*
2338  * Password change does require the NT password
2339  */
2340  if (!auth_ctx->nt_password) {
2341  REDEBUG("Missing Password.NT - required for change password request");
2343  }
2344 
2345  if (mschap_cpw_prepare(request, auth_ctx) < 0) RETURN_MODULE_FAIL;
2346 
2347  switch (auth_ctx->method) {
2348  case AUTH_INTERNAL:
2349 #ifdef WITH_TLS
2350  if (mschap_new_pass_decrypt(request, auth_ctx) < 0) RETURN_MODULE_FAIL;
2351  if (unlang_function_push(request, NULL, mod_authenticate_resume, NULL, 0,
2352  UNLANG_SUB_FRAME, auth_ctx) < 0) RETURN_MODULE_FAIL;
2353 
2354  fr_value_box_list_init(&auth_ctx->cpw_ctx->local_cpw_result);
2355  if (unlang_tmpl_push(auth_ctx, &auth_ctx->cpw_ctx->local_cpw_result, request,
2356  env_data->local_cpw, NULL) < 0) RETURN_MODULE_FAIL;
2357  break;
2358 #else
2359  REDEBUG("Local MS-CHAPv2 password changes require OpenSSL support");
2361 #endif
2362 
2363  default:
2364  if (!env_data->ntlm_cpw_username) {
2365  REDEBUG("No ntlm_auth username set, passchange will definitely fail!");
2367  }
2368 
2370  mod_authenticate_resume, NULL, 0,
2371  UNLANG_SUB_FRAME, auth_ctx) < 0) RETURN_MODULE_FAIL;
2372 
2373  fr_value_box_list_init(&auth_ctx->cpw_ctx->cpw_user);
2374  if (unlang_tmpl_push(auth_ctx, &auth_ctx->cpw_ctx->cpw_user, request,
2375  env_data->ntlm_cpw_username, NULL) < 0) RETURN_MODULE_FAIL;
2376  break;
2377  }
2378 
2380  }
2381 
2382  return mod_authenticate_resume(p_result, NULL, request, auth_ctx);
2383 }
2384 
2385 /*
2386  * Create instance for our module. Allocate space for
2387  * instance structure and read configuration parameters
2388  */
2389 static int mod_instantiate(module_inst_ctx_t const *mctx)
2390 {
2391  rlm_mschap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_mschap_t);
2392  CONF_SECTION *conf = mctx->inst->conf;
2393 
2394  inst->auth_type = fr_dict_enum_by_name(attr_auth_type, mctx->inst->name, -1);
2395  if (!inst->auth_type) {
2396  WARN("Failed to find 'authenticate %s {...}' section. MS-CHAP authentication will likely not work",
2397  mctx->inst->name);
2398  }
2399 
2400  /*
2401  * Set auth method
2402  */
2403  inst->method = AUTH_INTERNAL;
2404 
2405  if (inst->wb_username) {
2406 #ifdef WITH_AUTH_WINBIND
2407  inst->method = AUTH_WBCLIENT;
2408 
2409  inst->wb_pool = module_rlm_connection_pool_init(conf, inst, mod_conn_create, NULL, NULL, NULL, NULL);
2410  if (!inst->wb_pool) {
2411  cf_log_err(conf, "Unable to initialise winbind connection pool");
2412  return -1;
2413  }
2414 #else
2415  cf_log_err(conf, "'winbind' auth not enabled at compiled time");
2416  return -1;
2417 #endif
2418  }
2419 
2420  /* preserve existing behaviour: this option overrides all */
2421  if (inst->ntlm_auth) {
2422  inst->method = AUTH_NTLMAUTH_EXEC;
2423  }
2424 
2425  switch (inst->method) {
2426  case AUTH_INTERNAL:
2427  DEBUG("Using internal authentication");
2428  break;
2429  case AUTH_AUTO:
2430  DEBUG("Using auto password or ntlm_auth");
2431  break;
2432  case AUTH_NTLMAUTH_EXEC:
2433  DEBUG("Authenticating by calling 'ntlm_auth'");
2434  break;
2435 #ifdef WITH_AUTH_WINBIND
2436  case AUTH_WBCLIENT:
2437  DEBUG("Authenticating directly to winbind");
2438  break;
2439 #endif
2440  }
2441 
2442  /*
2443  * Check ntlm_auth_timeout is sane
2444  */
2445  if (!fr_time_delta_ispos(inst->ntlm_auth_timeout)) {
2446  inst->ntlm_auth_timeout = fr_time_delta_from_sec(EXEC_TIMEOUT);
2447  }
2448  if (fr_time_delta_lt(inst->ntlm_auth_timeout, fr_time_delta_from_sec(1))) {
2449  cf_log_err(conf, "ntml_auth_timeout '%pVs' is too small (minimum: 1s)",
2450  fr_box_time_delta(inst->ntlm_auth_timeout));
2451  return -1;
2452  }
2453  if (fr_time_delta_gt(inst->ntlm_auth_timeout, fr_time_delta_from_sec(10))) {
2454  cf_log_err(conf, "ntlm_auth_timeout '%pVs' is too large (maximum: 10s)",
2455  fr_box_time_delta(inst->ntlm_auth_timeout));
2456  return -1;
2457  }
2458 
2459 #define CHECK_OPTION(_option) cp = cf_pair_find(attrs, STRINGIFY(_option)); \
2460 if (!cp) { \
2461  WARN("Missing option \"" STRINGIFY(_option) "\", setting use_mppe to \"no\""); \
2462  inst->use_mppe = false; \
2463  goto done_mppe_check; \
2464 }
2465 
2466  /*
2467  * Check that MPPE attributes are in the module config, if the option is enabled.
2468  * Validity of them will be checked when the module is compiled.
2469  */
2470  if (inst->use_mppe) {
2471  CONF_SECTION *attrs = cf_section_find(conf, "attributes", NULL);
2472  CONF_PAIR *cp;
2473 
2474  if (!attrs) {
2475  cf_log_err(conf, "Missing required \"attributes\" section");
2476  return -1;
2477  }
2478  CHECK_OPTION(chap_mppe_keys)
2479  CHECK_OPTION(mppe_encryption_policy)
2480  CHECK_OPTION(mppe_recv_key)
2481  CHECK_OPTION(mppe_send_key)
2482  CHECK_OPTION(mppe_encryption_types)
2483  }
2484 done_mppe_check:
2485 
2486  return 0;
2487 }
2488 
2489 static int mod_bootstrap(module_inst_ctx_t const *mctx)
2490 {
2491  rlm_mschap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_mschap_t);
2492  xlat_t *xlat;
2493 
2496  xlat_func_call_env_set(xlat, &mschap_xlat_method_env);
2497 
2498  return 0;
2499 }
2500 
2501 /*
2502  * Tidy up instance
2503  */
2504 static int mod_detach(
2505 #ifndef WITH_AUTH_WINBIND
2506  UNUSED
2507 #endif
2508  module_detach_ctx_t const *mctx)
2509 {
2510 #ifdef WITH_AUTH_WINBIND
2511  rlm_mschap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_mschap_t);
2512 
2513  fr_pool_free(inst->wb_pool);
2514 #endif
2515 
2516  return 0;
2517 }
2518 
2519 
2520 extern module_rlm_t rlm_mschap;
2522  .common = {
2523  .magic = MODULE_MAGIC_INIT,
2524  .name = "mschap",
2525  .inst_size = sizeof(rlm_mschap_t),
2526  .config = module_config,
2527  .bootstrap = mod_bootstrap,
2529  .detach = mod_detach
2530  },
2531  .method_names = (module_method_name_t[]){
2532  { .name1 = "recv", .name2 = CF_IDENT_ANY, .method = mod_authorize,
2533  .method_env = &mschap_autz_method_env },
2534  { .name1 = "authenticate", .name2 = CF_IDENT_ANY, .method = mod_authenticate,
2535  .method_env = &mschap_auth_method_env },
2537  }
2538 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition: action.h:39
static int const char char buffer[256]
Definition: acutest.h:574
int do_auth_wbclient(rlm_mschap_t const *inst, request_t *request, uint8_t const *challenge, uint8_t const *response, uint8_t nthashhash[NT_DIGEST_LENGTH], mschap_auth_call_env_t *env_data)
Check NTLM authentication direct to winbind via Samba's libwbclient library.
Definition: auth_wbclient.c:93
#define fr_base16_encode(_out, _in)
Definition: base16.h:57
#define fr_base16_decode(_err, _out, _in, _no_trailing)
Definition: base16.h:95
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#define unlikely(_x)
Definition: build.h:378
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define CALL_ENV_TERMINATOR
Definition: call_env.h:212
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition: call_env.h:216
@ CALL_ENV_FLAG_SUBSECTION
This is a subsection.
Definition: call_env.h:85
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl must contain an attribute reference.
Definition: call_env.h:84
@ CALL_ENV_FLAG_NONE
Definition: call_env.h:72
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition: call_env.h:73
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition: call_env.h:78
#define FR_CALL_ENV_SUBSECTION(_name, _ident2, _flags, _subcs)
Specify a call_env_parser_t which defines a nested subsection.
Definition: call_env.h:378
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition: call_env.h:316
#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field)
Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified.
Definition: call_env.h:365
Per method call config.
Definition: call_env.h:171
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_CONF_DEPRECATED(_name, _struct, _field)
conf_parser_t entry which raises an error if a matching CONF_PAIR is found
Definition: cf_parse.h:385
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
Definition: cf_parse.h:310
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:256
@ CONF_FLAG_XLAT
string will be dynamically expanded.
Definition: cf_parse.h:417
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition: cf_parse.h:400
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
Configuration AVP similar to a fr_pair_t.
Definition: cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition: cf_util.c:970
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
#define CF_IDENT_ANY
Definition: cf_util.h:78
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition: dcursor.h:405
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
fr_dict_enum_value_t * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition: dict_util.c:2992
static fr_slen_t in
Definition: dict.h:645
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
Test enumeration values.
Definition: dict_test.h:92
dl_module_inst_t const * dl_module_instance_by_data(void const *data)
Lookup a dl_module_inst_t via instance data.
Definition: dl_module.c:215
char const *_CONST name
Instance name.
Definition: dl_module.h:163
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
#define EXEC_TIMEOUT
Default wait time for exec calls (in seconds).
Definition: exec.h:32
int radius_exec_program_legacy(char *out, size_t outlen, request_t *request, char const *cmd, fr_pair_list_t *input_pairs, bool exec_wait, bool shell_escape, fr_time_delta_t timeout)
Execute a program.
Definition: exec_legacy.c:474
pid_t radius_start_program_legacy(int *stdin_fd, int *stdout_fd, int *stderr_fd, char const *cmd, request_t *request, bool exec_wait, fr_pair_list_t *input_pairs, bool shell_escape)
Start a process.
Definition: exec_legacy.c:227
int radius_readfrom_program_legacy(int fd, pid_t pid, fr_time_delta_t timeout, char *answer, int left)
Read from the child process.
Definition: exec_legacy.c:357
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition: function.h:111
static xlat_action_t mschap_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Get data from MSCHAP attributes.
Definition: rlm_mschap.c:382
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition: interpret.c:1384
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:443
#define RHEXDUMP1(_data, _len, _fmt,...)
Definition: log.h:703
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:335
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RWDEBUG2(fmt,...)
Definition: log.h:362
#define RERROR(fmt,...)
Definition: log.h:298
#define RPERROR(fmt,...)
Definition: log.h:302
#define REDEBUG2(fmt,...)
Definition: log.h:372
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:430
waitpid(reap->pid_ev->pid, &status, 0)
talloc_free(reap)
void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Calculate the MD4 hash of the contents of a buffer.
Definition: md4.c:489
#define MD4_DIGEST_LENGTH
Definition: md4.h:25
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_UINT8
8 Bit unsigned integer.
Definition: merged_model.c:97
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
@ FR_TYPE_OCTETS
Raw octets.
Definition: merged_model.c:84
unsigned char uint8_t
Definition: merged_model.c:30
int fr_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length)
Do a comparison of two authentication digests by comparing the FULL data.
Definition: misc.c:463
int strncasecmp(char *s1, char *s2, int n)
Definition: missing.c:36
int strcasecmp(char *s1, char *s2)
Definition: missing.c:66
char * strsep(char **stringp, char const *delim)
Definition: missing.c:123
void * env_data
Per call environment data.
Definition: module_ctx.h:44
#define MODULE_INST_CTX(_dl_inst)
Wrapper to create a module_inst_ctx_t as a compound literal.
Definition: module_ctx.h:153
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
Specifies a module method identifier.
Definition: module_method.c:36
bool module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type_da, fr_dict_enum_value_t const *enumv)
Set the next section type if it's not already set.
Definition: module_rlm.c:397
fr_pool_t * module_rlm_connection_pool_init(CONF_SECTION *module, void *opaque, fr_pool_connection_create_t c, fr_pool_connection_alive_t a, char const *log_prefix, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Initialise a module specific connection pool.
Definition: module_rlm.c:248
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
int mschap_nt_password_hash(uint8_t out[static NT_DIGEST_LENGTH], char const *password)
Converts Unicode password to 16-byte NT hash with MD4.
Definition: mschap.c:52
void mschap_add_reply(request_t *request, uint8_t ident, fr_dict_attr_t const *da, char const *value, size_t len)
Definition: mschap.c:162
void mschap_auth_response(char const *username, size_t username_len, uint8_t const *nt_hash_hash, uint8_t const *ntresponse, uint8_t const *peer_challenge, uint8_t const *auth_challenge, char *response)
Definition: mschap.c:101
void mschap_challenge_hash(uint8_t challenge[static MSCHAP_CHALLENGE_LENGTH], uint8_t const peer_challenge[static MSCHAP_PEER_CHALLENGE_LENGTH], uint8_t const auth_challenge[static MSCHAP_PEER_AUTHENTICATOR_CHALLENGE_LENGTH], char const *user_name, size_t user_name_len)
Definition: mschap.c:72
#define LM_DIGEST_LENGTH
Definition: mschap.h:8
#define NT_DIGEST_LENGTH
Definition: mschap.h:7
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition: pair.c:937
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:278
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition: pair.c:2978
int fr_pair_value_bstr_alloc(fr_pair_t *vp, char **out, size_t size, bool tainted)
Pre-allocate a memory buffer for a "string" type value pair.
Definition: pair.c:2727
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition: pair.c:765
int fr_pair_value_mem_alloc(fr_pair_t *vp, uint8_t **out, size_t size, bool tainted)
Pre-allocate a memory buffer for a "octets" type value pair.
Definition: pair.c:2927
static ssize_t normify(normalise_t *action, uint8_t *buffer, size_t bufflen, char const *known_good, size_t len, size_t min_len)
Definition: password.c:407
fr_pair_t * password_find(bool *ephemeral, TALLOC_CTX *ctx, request_t *request, fr_dict_attr_t const *allowed_attrs[], size_t allowed_attrs_len, bool normify)
Find a "known good" password in the control list of a request.
Definition: password.c:963
void fr_pool_free(fr_pool_t *pool)
Delete a connection pool.
Definition: pool.c:1327
static const conf_parser_t config[]
Definition: base.c:188
static fr_dict_attr_t const * attr_user_name
Definition: radclient-ng.c:125
#define pair_update_request(_attr, _da)
Definition: radclient-ng.c:60
static bool done
Definition: radclient.c:80
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define WARN(fmt,...)
Definition: radclient.h:47
static rs_t * conf
Definition: radsniff.c:53
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
#define RETURN_MODULE_REJECT
Definition: rcode.h:55
#define RETURN_MODULE_NOOP
Definition: rcode.h:62
#define RETURN_MODULE_RCODE(_rcode)
Definition: rcode.h:64
#define RETURN_MODULE_INVALID
Definition: rcode.h:59
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition: rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition: rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition: rcode.h:41
@ RLM_MODULE_NOTFOUND
User not found.
Definition: rcode.h:47
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition: rcode.h:48
static int _mod_conn_free(rlm_cache_memcached_handle_t *mandle)
Free a connection handle.
static void * mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
Create a new memcached handle.
static const uint8_t SHSpad2[40]
Definition: rlm_mschap.c:1266
fr_dict_attr_t const * attr_smb_account_ctrl
Definition: rlm_mschap.c:257
uint8_t new_nt_encrypted[516]
Definition: rlm_mschap.c:221
tmpl_t const * username
Definition: rlm_mschap.c:152
#define CHECK_OPTION(_option)
static const call_env_parser_t xlat_call_env[]
Definition: rlm_mschap.c:158
static void mppe_chap2_get_keys128(uint8_t const *nt_hashhash, uint8_t const *nt_response, uint8_t *sendkey, uint8_t *recvkey)
Definition: rlm_mschap.c:1342
static void mppe_add_reply(UNUSED rlm_mschap_t const *inst, request_t *request, fr_dict_attr_t const *da, uint8_t const *value, size_t len)
Definition: rlm_mschap.c:822
fr_dict_attr_t const * attr_nt_password
Definition: rlm_mschap.c:255
fr_dict_attr_t const * attr_ms_chap_new_cleartext_password
Definition: rlm_mschap.c:250
fr_dict_autoload_t rlm_mschap_dict[]
Definition: rlm_mschap.c:241
#define ACB_MNS
MNS logon user account.
Definition: rlm_mschap.c:76
static const call_env_parser_t auth_call_env[]
Definition: rlm_mschap.c:166
tmpl_t const * chap_response
Definition: rlm_mschap.c:154
static int write_all(int fd, char const *buf, int len)
Definition: rlm_mschap.c:837
tmpl_t const * chap2_cpw
Definition: rlm_mschap.c:206
MSCHAP_AUTH_METHOD method
Definition: rlm_mschap.c:230
fr_dict_attr_t const * attr_eap_identity
Definition: rlm_mschap.c:249
tmpl_t const * chap2_response
Definition: rlm_mschap.c:205
#define ACB_AUTOLOCK
Account auto locked.
Definition: rlm_mschap.c:81
#define MSCHAP_CALL_ENV(_x)
Definition: rlm_mschap.c:131
fr_dict_attr_t const * attr_smb_account_ctrl_text
Definition: rlm_mschap.c:256
static const call_env_parser_t autz_call_env[]
Definition: rlm_mschap.c:209
#define ACB_TEMPDUP
Temporary duplicate account.
Definition: rlm_mschap.c:74
fr_pair_t * cpw
Definition: rlm_mschap.c:233
static int pdb_decode_acct_ctrl(char const *p)
Definition: rlm_mschap.c:291
static fr_dict_t const * dict_freeradius
Definition: rlm_mschap.c:237
static void mppe_GetMasterKey(uint8_t const *nt_hashhash, uint8_t const *nt_response, uint8_t *masterkey)
Definition: rlm_mschap.c:1300
static const call_env_method_t mschap_auth_method_env
Definition: rlm_mschap.c:181
static fr_pair_t * mschap_identity_find(request_t *request, fr_dict_attr_t const *attr_user_name)
Definition: rlm_mschap.c:276
char const * name
Definition: rlm_mschap.c:227
static int nt_password_find(TALLOC_CTX *ctx, fr_pair_t **out, rlm_mschap_t const *inst, request_t *request)
Find a Password.NT value, or create one from a Password.Cleartext, or Password.With-Header attribute.
Definition: rlm_mschap.c:1513
#define ACB_FR_EXPIRED
Password Expired.
Definition: rlm_mschap.c:82
#define ACB_DISABLED
User account disabled.
Definition: rlm_mschap.c:71
fr_pair_t * new_hash
Definition: rlm_mschap.c:223
static unlang_action_t mschap_process_response(rlm_rcode_t *p_result, int *mschap_version, uint8_t nthashhash[static NT_DIGEST_LENGTH], rlm_mschap_t const *inst, request_t *request, fr_pair_t *smb_ctrl, fr_pair_t *nt_password, fr_pair_t *challenge, fr_pair_t *response, MSCHAP_AUTH_METHOD method, mschap_auth_call_env_t *env_data)
Definition: rlm_mschap.c:1733
static fr_dict_t const * dict_radius
Definition: rlm_mschap.c:238
static const uint8_t magic1[27]
Definition: rlm_mschap.c:1272
static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_mschap.c:2264
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition: rlm_mschap.c:2489
static const uint8_t SHSpad1[40]
Definition: rlm_mschap.c:1260
fr_dict_attr_t const * attr_auth_type
Definition: rlm_mschap.c:247
static int mod_detach(UNUSED module_detach_ctx_t const *mctx)
Definition: rlm_mschap.c:2504
fr_dict_attr_t const * attr_ms_chap_user_name
Definition: rlm_mschap.c:254
rlm_mschap_t const * inst
Definition: rlm_mschap.c:228
tmpl_t const * chap_response
Definition: rlm_mschap.c:204
fr_value_box_list_t cpw_user
Definition: rlm_mschap.c:218
fr_value_box_list_t cpw_domain
Definition: rlm_mschap.c:219
static void mppe_GetAsymmetricStartKey(uint8_t *masterkey, uint8_t *sesskey, int keylen, int issend)
Definition: rlm_mschap.c:1316
static xlat_arg_parser_t const mschap_xlat_args[]
Definition: rlm_mschap.c:369
fr_pair_t * smb_ctrl
Definition: rlm_mschap.c:232
fr_dict_attr_t const * attr_ms_chap_use_ntlm_auth
Definition: rlm_mschap.c:253
#define ACB_HOMDIRREQ
Home directory required.
Definition: rlm_mschap.c:72
tmpl_t const * chap2_response
Definition: rlm_mschap.c:155
fr_value_box_list_t local_cpw_result
Definition: rlm_mschap.c:220
module_rlm_t rlm_mschap
Definition: rlm_mschap.c:2521
fr_dict_attr_t const * attr_cleartext_password
Definition: rlm_mschap.c:248
static unlang_action_t mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_mschap.c:1380
fr_dict_attr_autoload_t rlm_mschap_dict_attr[]
Definition: rlm_mschap.c:260
static unlang_action_t mod_authenticate_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Complete mschap authentication after any tmpls have been expanded.
Definition: rlm_mschap.c:1947
static unlang_action_t mschap_process_v2_response(rlm_rcode_t *p_result, int *mschap_version, uint8_t nthashhash[static NT_DIGEST_LENGTH], rlm_mschap_t const *inst, request_t *request, fr_pair_t *smb_ctrl, fr_pair_t *nt_password, fr_pair_t *challenge, fr_pair_t *response, MSCHAP_AUTH_METHOD method, mschap_auth_call_env_t *env_data)
Definition: rlm_mschap.c:1791
mschap_auth_call_env_t * env_data
Definition: rlm_mschap.c:229
static const uint8_t magic3[84]
Definition: rlm_mschap.c:1288
static int do_mschap_cpw(rlm_mschap_t const *inst, request_t *request, mschap_auth_ctx_t *auth_ctx, uint8_t *new_nt_password, uint8_t *old_nt_hash)
Definition: rlm_mschap.c:855
static const uint8_t magic2[84]
Definition: rlm_mschap.c:1277
static int do_mschap(rlm_mschap_t const *inst, request_t *request, fr_pair_t *password, uint8_t const *challenge, uint8_t const *response, uint8_t nthashhash[static NT_DIGEST_LENGTH], MSCHAP_AUTH_METHOD method, UNUSED mschap_auth_call_env_t *env_data)
Definition: rlm_mschap.c:1066
#define ACB_DOMTRUST
Interdomain trust account.
Definition: rlm_mschap.c:77
uint8_t old_nt_hash[NT_DIGEST_LENGTH]
Definition: rlm_mschap.c:222
#define ACB_WSTRUST
Workstation trust account.
Definition: rlm_mschap.c:78
static void mppe_chap2_gen_keys128(uint8_t const *nt_hashhash, uint8_t const *response, uint8_t *sendkey, uint8_t *recvkey)
Definition: rlm_mschap.c:1356
fr_dict_attr_t const * attr_ms_chap_new_nt_password
Definition: rlm_mschap.c:251
#define ACB_NORMAL
Normal user account.
Definition: rlm_mschap.c:75
mschap_cpw_ctx_t * cpw_ctx
Definition: rlm_mschap.c:234
tmpl_t const * chap_challenge
Definition: rlm_mschap.c:153
#define ACB_PWNOTREQ
User password not required.
Definition: rlm_mschap.c:73
static unlang_action_t mschap_error(rlm_rcode_t *p_result, rlm_mschap_t const *inst, request_t *request, unsigned char ident, int mschap_result, int mschap_version, fr_pair_t *smb_ctrl, mschap_auth_call_env_t *env_data)
Definition: rlm_mschap.c:1415
static const conf_parser_t module_config[]
Definition: rlm_mschap.c:97
static unlang_action_t mschap_process_cpw_request(rlm_rcode_t *p_result, rlm_mschap_t const *inst, request_t *request, mschap_auth_ctx_t *auth_ctx)
Definition: rlm_mschap.c:1600
static unlang_action_t mod_authenticate_domain_tmpl_push(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
When changing passwords using the ntlm_auth helper, evaluate the domain tmpl.
Definition: rlm_mschap.c:2096
fr_pair_t * nt_password
Definition: rlm_mschap.c:231
fr_dict_attr_t const * attr_ms_chap_peer_challenge
Definition: rlm_mschap.c:252
static int mschap_cpw_prepare(request_t *request, mschap_auth_ctx_t *auth_ctx)
Validate data required for change password requests.
Definition: rlm_mschap.c:1633
#define MSCHAP_OPT_CALL_ENV(_opt, _x)
Definition: rlm_mschap.c:148
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_mschap.c:2389
#define ACB_PWNOEXP
User password does not expire.
Definition: rlm_mschap.c:80
static const conf_parser_t passchange_config[]
Definition: rlm_mschap.c:84
tmpl_t const * chap_challenge
Definition: rlm_mschap.c:203
#define MSCHAP_COMMON_CALL_ENV(_x)
Definition: rlm_mschap.c:140
#define ACB_SVRTRUST
Server trust account.
Definition: rlm_mschap.c:79
static const conf_parser_t winbind_config[]
Definition: rlm_mschap.c:89
tmpl_t const * mppe_encryption_types
Definition: rlm_mschap.h:79
tmpl_t const * ntlm_cpw_domain
Definition: rlm_mschap.h:85
tmpl_t const * ntlm_cpw_username
Definition: rlm_mschap.h:84
tmpl_t const * chap_nt_enc_pw
Definition: rlm_mschap.h:81
tmpl_t const * mppe_send_key
Definition: rlm_mschap.h:78
tmpl_t const * chap_response
Definition: rlm_mschap.h:72
tmpl_t const * local_cpw
Definition: rlm_mschap.h:86
tmpl_t const * chap_challenge
Definition: rlm_mschap.h:71
tmpl_t const * chap2_response
Definition: rlm_mschap.h:73
tmpl_t const * mppe_encryption_policy
Definition: rlm_mschap.h:76
tmpl_t const * mppe_recv_key
Definition: rlm_mschap.h:77
tmpl_t const * chap_error
Definition: rlm_mschap.h:70
tmpl_t const * chap2_cpw
Definition: rlm_mschap.h:80
MSCHAP_AUTH_METHOD
Definition: rlm_mschap.h:17
@ AUTH_AUTO
Definition: rlm_mschap.h:20
@ AUTH_INTERNAL
Definition: rlm_mschap.h:18
@ AUTH_NTLMAUTH_EXEC
Definition: rlm_mschap.h:19
tmpl_t const * chap_mppe_keys
Definition: rlm_mschap.h:75
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1312
username
Definition: rlm_securid.c:420
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
#define pair_append_control(_attr, _da)
Allocate and append a fr_pair_t to the control list.
Definition: pair.h:57
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition: pair.h:129
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:796
void fr_sha1_init(fr_sha1_ctx *context)
Definition: sha1.c:93
void fr_sha1_final(uint8_t digest[static SHA1_DIGEST_LENGTH], fr_sha1_ctx *context)
Definition: sha1.c:141
void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *in, size_t len)
Definition: sha1.c:105
void smbhash(unsigned char *out, unsigned char const *in, unsigned char *key)
Definition: smbdes.c:287
void smbdes_mschap(uint8_t const win_password[16], uint8_t const *challenge, uint8_t *response)
Definition: smbdes.c:339
void smbdes_lmpwdhash(char const *password, uint8_t *lmhash)
Definition: smbdes.c:319
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
#define talloc_get_type_abort_const
Definition: talloc.h:270
#define fr_time_delta_lt(_a, _b)
Definition: time.h:283
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition: time.h:588
#define fr_time_delta_ispos(_a)
Definition: time.h:288
#define fr_time_delta_gt(_a, _b)
Definition: time.h:281
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
int unlang_tmpl_push(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args)
Push a tmpl onto the stack for evaluation.
Definition: tmpl.c:259
@ T_BARE_WORD
Definition: token.h:120
close(uq->fd)
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
#define XLAT_ARG_PARSER_TERMINATOR
Definition: xlat.h:166
xlat_action_t
Definition: xlat.h:35
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition: xlat.h:42
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition: xlat.h:41
Definition for a single argument consumend by an xlat function.
Definition: xlat.h:145
static fr_slen_t parent
Definition: pair.h:844
int fr_value_box_asprintf(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, bool tainted, char const *fmt,...)
Print a formatted string using our internal printf wrapper and assign it to a value box.
Definition: value.c:3963
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition: value.c:3876
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
Definition: value.c:4417
static fr_slen_t data
Definition: value.h:1259
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279
#define fr_box_time_delta(_val)
Definition: value.h:336
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition: value.h:619
static size_t char ** out
Definition: value.h:984
#define fr_box_octets(_val, _len)
Definition: value.h:281
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition: xlat_ctx.h:45
void * env_data
Expanded call env data.
Definition: xlat_ctx.h:46
An xlat calling ctx.
Definition: xlat_ctx.h:42
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition: xlat_func.c:360
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.
Definition: xlat_func.c:405
xlat_t * xlat_func_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function for a module.
Definition: xlat_func.c:274