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