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