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