Fixes from clang / scan-build
[freeradius.git] / src / modules / rlm_mschap / rlm_mschap.c
1 /*
2  * rlm_mschap.c
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2001,2006  The FreeRADIUS server project
21  */
22
23 /*  MPPE support from Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp> */
24
25 #include        <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include        <freeradius-devel/radiusd.h>
29 #include        <freeradius-devel/modules.h>
30 #include        <freeradius-devel/rad_assert.h>
31 #include        <freeradius-devel/md5.h>
32 #include        <freeradius-devel/sha1.h>
33
34 #include        <ctype.h>
35
36 #include        "mschap.h"
37 #include        "smbdes.h"
38
39 #ifdef __APPLE__
40 extern int od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR * usernamepair);
41 #endif
42
43 /* Allowable account control bits */
44 #define ACB_DISABLED   0x0001  /* 1 = User account disabled */
45 #define ACB_HOMDIRREQ  0x0002  /* 1 = Home directory required */
46 #define ACB_PWNOTREQ   0x0004  /* 1 = User password not required */
47 #define ACB_TEMPDUP    0x0008  /* 1 = Temporary duplicate account */
48 #define ACB_NORMAL     0x0010  /* 1 = Normal user account */
49 #define ACB_MNS        0x0020  /* 1 = MNS logon user account */
50 #define ACB_DOMTRUST   0x0040  /* 1 = Interdomain trust account */
51 #define ACB_WSTRUST    0x0080  /* 1 = Workstation trust account */
52 #define ACB_SVRTRUST   0x0100  /* 1 = Server trust account */
53 #define ACB_PWNOEXP    0x0200  /* 1 = User password does not expire */
54 #define ACB_AUTOLOCK   0x0400  /* 1 = Account auto locked */
55
56 static int pdb_decode_acct_ctrl(const char *p)
57 {
58         int acct_ctrl = 0;
59         int finished = 0;
60
61         /*
62          * Check if the account type bits have been encoded after the
63          * NT password (in the form [NDHTUWSLXI]).
64          */
65
66         if (*p != '[') return 0;
67
68         for (p++; *p && !finished; p++) {
69                 switch (*p) {
70                         case 'N': /* 'N'o password. */
71                           acct_ctrl |= ACB_PWNOTREQ;
72                           break;
73
74                         case 'D':  /* 'D'isabled. */
75                           acct_ctrl |= ACB_DISABLED ;
76                           break;
77
78                         case 'H':  /* 'H'omedir required. */
79                           acct_ctrl |= ACB_HOMDIRREQ;
80                           break;
81
82                         case 'T': /* 'T'emp account. */
83                           acct_ctrl |= ACB_TEMPDUP;
84                           break;
85
86                         case 'U': /* 'U'ser account (normal). */
87                           acct_ctrl |= ACB_NORMAL;
88                           break;
89
90                         case 'M': /* 'M'NS logon user account. What is this? */
91                           acct_ctrl |= ACB_MNS;
92                           break;
93
94                         case 'W': /* 'W'orkstation account. */
95                           acct_ctrl |= ACB_WSTRUST;
96                           break;
97
98                         case 'S': /* 'S'erver account. */
99                           acct_ctrl |= ACB_SVRTRUST;
100                           break;
101
102                         case 'L': /* 'L'ocked account. */
103                           acct_ctrl |= ACB_AUTOLOCK;
104                           break;
105
106                         case 'X': /* No 'X'piry on password */
107                           acct_ctrl |= ACB_PWNOEXP;
108                           break;
109
110                         case 'I': /* 'I'nterdomain trust account. */
111                           acct_ctrl |= ACB_DOMTRUST;
112                           break;
113
114                         case ' ': /* ignore spaces */
115                           break;
116
117                         case ':':
118                         case '\n':
119                         case '\0':
120                         case ']':
121                         default:
122                           finished = 1;
123                           break;
124                 }
125         }
126
127         return acct_ctrl;
128 }
129
130
131 typedef struct rlm_mschap_t {
132         int use_mppe;
133         int require_encryption;
134         int require_strong;
135         int with_ntdomain_hack; /* this should be in another module */
136         char *passwd_file;
137         const char *xlat_name;
138         char *ntlm_auth;
139         const char *auth_type;
140 #ifdef __APPLE__
141         int  open_directory;
142 #endif  
143 } rlm_mschap_t;
144
145
146 /*
147  *      Does dynamic translation of strings.
148  *
149  *      Pulls NT-Response, LM-Response, or Challenge from MSCHAP
150  *      attributes.
151  */
152 static size_t mschap_xlat(void *instance, REQUEST *request,
153                        char *fmt, char *out, size_t outlen,
154                        RADIUS_ESCAPE_STRING func)
155 {
156         size_t          i, data_len;
157         uint8_t         *data = NULL;
158         uint8_t         buffer[32];
159         VALUE_PAIR      *user_name;
160         VALUE_PAIR      *chap_challenge, *response;
161         rlm_mschap_t    *inst = instance;
162
163         response = NULL;
164
165         func = func;            /* -Wunused */
166
167         /*
168          *      Challenge means MS-CHAPv1 challenge, or
169          *      hash of MS-CHAPv2 challenge, and peer challenge.
170          */
171         if (strncasecmp(fmt, "Challenge", 9) == 0) {
172                 chap_challenge = pairfind(request->packet->vps,
173                                           PW_MSCHAP_CHALLENGE,
174                                           VENDORPEC_MICROSOFT);
175                 if (!chap_challenge) {
176                         RDEBUG2("No MS-CHAP-Challenge in the request.");
177                         return 0;
178                 }
179
180                 /*
181                  *      MS-CHAP-Challenges are 8 octets,
182                  *      for MS-CHAPv2
183                  */
184                 if (chap_challenge->length == 8) {
185                         RDEBUG2(" mschap1: %02x",
186                                chap_challenge->vp_octets[0]);
187                         data = chap_challenge->vp_octets;
188                         data_len = 8;
189
190                         /*
191                          *      MS-CHAP-Challenges are 16 octets,
192                          *      for MS-CHAPv2.
193                          */
194                 } else if (chap_challenge->length == 16) {
195                         VALUE_PAIR *name_attr, *response_name;
196                         char *username_string;
197
198                         RDEBUG2(" mschap2: %02x", chap_challenge->vp_octets[0]);
199                         response = pairfind(request->packet->vps,
200                                             PW_MSCHAP2_RESPONSE,
201                                             VENDORPEC_MICROSOFT);
202                         if (!response) {
203                                 RDEBUG2("MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge.");
204                                 return 0;
205                         }
206
207                         /*
208                          *      FIXME: Much of this is copied from
209                          *      below.  We should put it into a
210                          *      separate function.
211                          */
212
213                         /*
214                          *      Responses are 50 octets.
215                          */
216                         if (response->length < 50) {
217                                 radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
218                                 return 0;
219                         }
220
221                         user_name = pairfind(request->packet->vps,
222                                              PW_USER_NAME, 0);
223                         if (!user_name) {
224                                 RDEBUG2("User-Name is required to calculate MS-CHAPv1 Challenge.");
225                                 return 0;
226                         }
227
228                         /*
229                          *      Check for MS-CHAP-User-Name and if found, use it
230                          *      to construct the MSCHAPv1 challenge.  This is
231                          *      set by rlm_eap_mschap to the MS-CHAP Response
232                          *      packet Name field.
233                          *
234                          *      We prefer this to the User-Name in the
235                          *      packet.
236                          */
237                         response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME, 0);
238                         if (response_name) {
239                                 name_attr = response_name;
240                         } else {
241                                 name_attr = user_name;
242                         }
243
244                         /*
245                          *      with_ntdomain_hack moved here, too.
246                          */
247                         if ((username_string = strchr(name_attr->vp_strvalue, '\\')) != NULL) {
248                                 if (inst->with_ntdomain_hack) {
249                                         username_string++;
250                                 } else {
251                                         RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
252                                         username_string = name_attr->vp_strvalue;
253                                 }
254                         } else {
255                                 username_string = name_attr->vp_strvalue;
256                         }
257
258                         if (response_name &&
259                             ((user_name->length != response_name->length) ||
260                              (strncasecmp(user_name->vp_strvalue, response_name->vp_strvalue, user_name->length) != 0))) {
261                                 RDEBUG("WARNING: User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", user_name->vp_strvalue, response_name->vp_strvalue);
262                         }
263
264                         /*
265                          *      Get the MS-CHAPv1 challenge
266                          *      from the MS-CHAPv2 peer challenge,
267                          *      our challenge, and the user name.
268                          */
269                         RDEBUG2("Creating challenge hash with username: %s",
270                                 username_string);
271                         mschap_challenge_hash(response->vp_octets + 2,
272                                        chap_challenge->vp_octets,
273                                        username_string, buffer);
274                         data = buffer;
275                         data_len = 8;
276                 } else {
277                         RDEBUG2("Invalid MS-CHAP challenge length");
278                         return 0;
279                 }
280
281                 /*
282                  *      Get the MS-CHAPv1 response, or the MS-CHAPv2
283                  *      response.
284                  */
285         } else if (strncasecmp(fmt, "NT-Response", 11) == 0) {
286                 response = pairfind(request->packet->vps,
287                                     PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT);
288                 if (!response) response = pairfind(request->packet->vps,
289                                                    PW_MSCHAP2_RESPONSE,
290                                                    VENDORPEC_MICROSOFT);
291                 if (!response) {
292                         RDEBUG2("No MS-CHAP-Response or MS-CHAP2-Response was found in the request.");
293                         return 0;
294                 }
295
296                 /*
297                  *      For MS-CHAPv1, the NT-Response exists only
298                  *      if the second octet says so.
299                  */
300                 if ((response->attribute == PW_MSCHAP_RESPONSE) &&
301                     ((response->vp_octets[1] & 0x01) == 0)) {
302                         RDEBUG2("No NT-Response in MS-CHAP-Response");
303                         return 0;
304                 }
305
306                 /*
307                  *      MS-CHAP-Response and MS-CHAP2-Response have
308                  *      the NT-Response at the same offset, and are
309                  *      the same length.
310                  */
311                 data = response->vp_octets + 26;
312                 data_len = 24;
313
314                 /*
315                  *      LM-Response is deprecated, and exists only
316                  *      in MS-CHAPv1, and not often there.
317                  */
318         } else if (strncasecmp(fmt, "LM-Response", 11) == 0) {
319                 response = pairfind(request->packet->vps,
320                                     PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT);
321                 if (!response) {
322                         RDEBUG2("No MS-CHAP-Response was found in the request.");
323                         return 0;
324                 }
325
326                 /*
327                  *      For MS-CHAPv1, the NT-Response exists only
328                  *      if the second octet says so.
329                  */
330                 if ((response->vp_octets[1] & 0x01) != 0) {
331                         RDEBUG2("No LM-Response in MS-CHAP-Response");
332                         return 0;
333                 }
334                 data = response->vp_octets + 2;
335                 data_len = 24;
336
337                 /*
338                  *      Pull the NT-Domain out of the User-Name, if it exists.
339                  */
340         } else if (strncasecmp(fmt, "NT-Domain", 9) == 0) {
341                 char *p, *q;
342
343                 user_name = pairfind(request->packet->vps, PW_USER_NAME, 0);
344                 if (!user_name) {
345                         RDEBUG2("No User-Name was found in the request.");
346                         return 0;
347                 }
348
349                 /*
350                  *      First check to see if this is a host/ style User-Name
351                  *      (a la Kerberos host principal)
352                  */
353                 if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) {
354                         /*
355                          *      If we're getting a User-Name formatted in this way,
356                          *      it's likely due to PEAP.  The Windows Domain will be
357                          *      the first domain component following the hostname,
358                          *      or the machine name itself if only a hostname is supplied
359                          */
360                         p = strchr(user_name->vp_strvalue, '.');
361                         if (!p) {
362                                 RDEBUG2("setting NT-Domain to same as machine name");
363                                 strlcpy(out, user_name->vp_strvalue + 5, outlen);
364                         } else {
365                                 p++;    /* skip the period */
366                                 q = strchr(p, '.');
367                                 /*
368                                  * use the same hack as below
369                                  * only if another period was found
370                                  */
371                                 if (q) *q = '\0';
372                                 strlcpy(out, p, outlen);
373                                 if (q) *q = '.';
374                         }
375                 } else {
376                         p = strchr(user_name->vp_strvalue, '\\');
377                         if (!p) {
378                                 RDEBUG2("No NT-Domain was found in the User-Name.");
379                                 return 0;
380                         }
381
382                         /*
383                          *      Hack.  This is simpler than the alternatives.
384                          */
385                         *p = '\0';
386                         strlcpy(out, user_name->vp_strvalue, outlen);
387                         *p = '\\';
388                 }
389
390                 return strlen(out);
391
392                 /*
393                  *      Pull the User-Name out of the User-Name...
394                  */
395         } else if (strncasecmp(fmt, "User-Name", 9) == 0) {
396                 char *p;
397
398                 user_name = pairfind(request->packet->vps, PW_USER_NAME, 0);
399                 if (!user_name) {
400                         RDEBUG2("No User-Name was found in the request.");
401                         return 0;
402                 }
403
404                 /*
405                  *      First check to see if this is a host/ style User-Name
406                  *      (a la Kerberos host principal)
407                  */
408                 if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) {
409                         /*
410                          *      If we're getting a User-Name formatted in this way,
411                          *      it's likely due to PEAP.  When authenticating this against
412                          *      a Domain, Windows will expect the User-Name to be in the
413                          *      format of hostname$, the SAM version of the name, so we
414                          *      have to convert it to that here.  We do so by stripping
415                          *      off the first 5 characters (host/), and copying everything
416                          *      from that point to the first period into a string and appending
417                          *      a $ to the end.
418                          */
419                         p = strchr(user_name->vp_strvalue, '.');
420                         /*
421                          * use the same hack as above
422                          * only if a period was found
423                          */
424                         if (p) *p = '\0';
425                         snprintf(out, outlen, "%s$", user_name->vp_strvalue + 5);
426                         if (p) *p = '.';
427                 } else {
428                         p = strchr(user_name->vp_strvalue, '\\');
429                         if (p) {
430                                 p++;    /* skip the backslash */
431                         } else {
432                                 p = user_name->vp_strvalue; /* use the whole User-Name */
433                         }
434                         strlcpy(out, p, outlen);
435                 }
436
437                 return strlen(out);
438
439                 /*
440                  * Return the NT-Hash of the passed string
441                  */
442         } else if (strncasecmp(fmt, "NT-Hash ", 8) == 0) {
443                 char *p;
444                 char buf2[1024];
445
446                 p = fmt + 8;    /* 7 is the length of 'NT-Hash' */
447                 if ((p == '\0')  || (outlen <= 32))
448                         return 0;
449
450                 while (isspace(*p)) p++;
451
452                 if (!radius_xlat(buf2, sizeof(buf2),p,request,NULL)) {
453                         RDEBUG("xlat failed");
454                         *buffer = '\0';
455                         return 0;
456                 }
457
458                 mschap_ntpwdhash(buffer,buf2);
459
460                 fr_bin2hex(buffer, out, 16);
461                 out[32] = '\0';
462                 RDEBUG("NT-Hash of %s = %s", buf2, out);
463                 return 32;
464
465                 /*
466                  * Return the LM-Hash of the passed string
467                  */
468         } else if (strncasecmp(fmt, "LM-Hash ", 8) == 0) {
469                 char *p;
470                 char buf2[1024];
471
472                 p = fmt + 8;    /* 7 is the length of 'LM-Hash' */
473                 if ((p == '\0') || (outlen <= 32))
474                         return 0;
475
476                 while (isspace(*p)) p++;
477
478                 if (!radius_xlat(buf2, sizeof(buf2),p,request,NULL)) {
479                         RDEBUG("xlat failed");
480                         *buffer = '\0';
481                         return 0;
482                 }
483
484                 smbdes_lmpwdhash(buf2, buffer);
485                 fr_bin2hex(buffer, out, 16);
486                 out[32] = '\0';
487                 RDEBUG("LM-Hash of %s = %s", buf2, out);
488                 return 32;
489         } else {
490                 RDEBUG2("Unknown expansion string \"%s\"",
491                        fmt);
492                 return 0;
493         }
494
495         if (outlen == 0) return 0; /* nowhere to go, don't do anything */
496
497         /*
498          *      Didn't set anything: this is bad.
499          */
500         if (!data) {
501                 RDEBUG2("Failed to do anything intelligent");
502                 return 0;
503         }
504
505         /*
506          *      Check the output length.
507          */
508         if (outlen < ((data_len * 2) + 1)) {
509                 data_len = (outlen - 1) / 2;
510         }
511
512         /*
513          *
514          */
515         for (i = 0; i < data_len; i++) {
516                 sprintf(out + (2 * i), "%02x", data[i]);
517         }
518         out[data_len * 2] = '\0';
519
520         return data_len * 2;
521 }
522
523
524 static const CONF_PARSER module_config[] = {
525         /*
526          *      Cache the password by default.
527          */
528         { "use_mppe",    PW_TYPE_BOOLEAN,
529           offsetof(rlm_mschap_t,use_mppe), NULL, "yes" },
530         { "require_encryption",    PW_TYPE_BOOLEAN,
531           offsetof(rlm_mschap_t,require_encryption), NULL, "no" },
532         { "require_strong",    PW_TYPE_BOOLEAN,
533           offsetof(rlm_mschap_t,require_strong), NULL, "no" },
534         { "with_ntdomain_hack",     PW_TYPE_BOOLEAN,
535           offsetof(rlm_mschap_t,with_ntdomain_hack), NULL, "no" },
536         { "passwd",   PW_TYPE_STRING_PTR,
537           offsetof(rlm_mschap_t, passwd_file), NULL,  NULL },
538         { "ntlm_auth",   PW_TYPE_STRING_PTR,
539           offsetof(rlm_mschap_t, ntlm_auth), NULL,  NULL },
540 #ifdef __APPLE__
541         { "use_open_directory",    PW_TYPE_BOOLEAN,
542           offsetof(rlm_mschap_t,open_directory), NULL, "yes" },
543 #endif
544
545         { NULL, -1, 0, NULL, NULL }             /* end the list */
546 };
547
548 /*
549  *      deinstantiate module, free all memory allocated during
550  *      mschap_instantiate()
551  */
552 static int mschap_detach(void *instance){
553 #define inst ((rlm_mschap_t *)instance)
554         if (inst->xlat_name) {
555                 xlat_unregister(inst->xlat_name, mschap_xlat);
556                 free(inst->xlat_name);
557         }
558         free(instance);
559         return 0;
560 #undef inst
561 }
562
563 /*
564  *      Create instance for our module. Allocate space for
565  *      instance structure and read configuration parameters
566  */
567 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
568 {
569         rlm_mschap_t *inst;
570
571         inst = *instance = rad_malloc(sizeof(*inst));
572         if (!inst) {
573                 return -1;
574         }
575         memset(inst, 0, sizeof(*inst));
576
577         if (cf_section_parse(conf, inst, module_config) < 0) {
578                 free(inst);
579                 return -1;
580         }
581
582         /*
583          *      This module used to support SMB Password files, but it
584          *      made it too complicated.  If the user tries to
585          *      configure an SMB Password file, then die, with an
586          *      error message.
587          */
588         if (inst->passwd_file) {
589                 radlog(L_ERR, "rlm_mschap: SMB password file is no longer supported in this module.  Use rlm_passwd module instead");
590                 mschap_detach(inst);
591                 return -1;
592         }
593
594         /*
595          *      Create the dynamic translation.
596          */
597         inst->xlat_name = cf_section_name2(conf);
598         if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf);
599         inst->xlat_name = strdup(inst->xlat_name);
600         xlat_register(inst->xlat_name, mschap_xlat, inst);
601
602         /*
603          *      For backwards compatibility
604          */
605         if (!dict_valbyname(PW_AUTH_TYPE, 0, inst->xlat_name)) {
606                 inst->auth_type = "MS-CHAP";
607         } else {
608                 inst->auth_type = inst->xlat_name;
609         }
610
611         return 0;
612 }
613
614 /*
615  *      add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
616  *      attribute to reply packet
617  */
618 void mschap_add_reply(REQUEST *request, VALUE_PAIR** vp, unsigned char ident,
619                       const char* name, const char* value, int len)
620 {
621         VALUE_PAIR *reply_attr;
622         reply_attr = pairmake(name, "", T_OP_EQ);
623         if (!reply_attr) {
624                 RDEBUG("Failed to create attribute %s: %s\n", name, fr_strerror());
625                 return;
626         }
627
628         reply_attr->vp_octets[0] = ident;
629         memcpy(reply_attr->vp_octets + 1, value, len);
630         reply_attr->length = len + 1;
631         pairadd(vp, reply_attr);
632 }
633
634 /*
635  *      Add MPPE attributes to the reply.
636  */
637 static void mppe_add_reply(REQUEST *request,
638                            const char* name, const uint8_t * value, int len)
639 {
640        VALUE_PAIR *vp;
641        vp = radius_pairmake(request, &request->reply->vps, name, "", T_OP_EQ);
642        if (!vp) {
643                RDEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, fr_strerror());
644                return;
645        }
646
647        memcpy(vp->vp_octets, value, len);
648        vp->length = len;
649 }
650
651
652 /*
653  *      Do the MS-CHAP stuff.
654  *
655  *      This function is here so that all of the MS-CHAP related
656  *      authentication is in one place, and we can perhaps later replace
657  *      it with code to call winbindd, or something similar.
658  */
659 static int do_mschap(rlm_mschap_t *inst,
660                      REQUEST *request, VALUE_PAIR *password,
661                      uint8_t *challenge, uint8_t *response,
662                      uint8_t *nthashhash, int do_ntlm_auth)
663 {
664         uint8_t         calculated[24];
665
666         rad_assert(request != NULL);
667
668         /*
669          *      Do normal authentication.
670          */
671         if (!do_ntlm_auth) {
672                 /*
673                  *      No password: can't do authentication.
674                  */
675                 if (!password) {
676                         RDEBUG2("FAILED: No NT/LM-Password.  Cannot perform authentication.");
677                         return -1;
678                 }
679
680                 smbdes_mschap(password->vp_octets, challenge, calculated);
681                 if (memcmp(response, calculated, 24) != 0) {
682                         return -1;
683                 }
684
685                 /*
686                  *      If the password exists, and is an NT-Password,
687                  *      then calculate the hash of the NT hash.  Doing this
688                  *      here minimizes work for later.
689                  */
690                 if (password && (password->attribute == PW_NT_PASSWORD)) {
691                         fr_md4_calc(nthashhash, password->vp_octets, 16);
692                 } else {
693                         memset(nthashhash, 0, 16);
694                 }
695         } else {                /* run ntlm_auth */
696                 int     result;
697                 char    buffer[256];
698
699                 memset(nthashhash, 0, 16);
700
701                 /*
702                  *      Run the program, and expect that we get 16
703                  */
704                 result = radius_exec_program(inst->ntlm_auth, request,
705                                              TRUE, /* wait */
706                                              buffer, sizeof(buffer),
707                                              NULL, NULL, 1);
708                 if (result != 0) {
709                         char *p;
710                         VALUE_PAIR *vp = NULL;
711
712                         RDEBUG2("External script failed.");
713
714                         vp = pairmake("Module-Failure-Message", "", T_OP_EQ);
715                         if (!vp) {
716                                 radlog_request(L_ERR, 0, request, "No memory to allocate Module-Failure-Message");
717                                 return RLM_MODULE_FAIL;
718                         }
719
720                         p = strchr(buffer, '\n');
721                         if (p) *p = '\0';
722                         snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue),
723                                 "%s: External script says %s",
724                                  inst->xlat_name, buffer);
725                         vp->length = strlen(vp->vp_strvalue);
726                         pairadd(&request->packet->vps, vp);
727                         return -1;
728                 }
729
730                 /*
731                  *      Parse the answer as an nthashhash.
732                  *
733                  *      ntlm_auth currently returns:
734                  *      NT_KEY: 000102030405060708090a0b0c0d0e0f
735                  */
736                 if (memcmp(buffer, "NT_KEY: ", 8) != 0) {
737                         RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY");
738                         return -1;
739                 }
740
741                 /*
742                  *      Check the length.  It should be at least 32,
743                  *      with an LF at the end.
744                  */
745                 if (strlen(buffer + 8) < 32) {
746                         RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length");
747                         return -1;
748                 }
749
750                 /*
751                  *      Update the NT hash hash, from the NT key.
752                  */
753                 if (fr_hex2bin(buffer + 8, nthashhash, 16) != 16) {
754                         RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values");
755                         return -1;
756                 }
757         }
758
759         return 0;
760 }
761
762
763 /*
764  *      Data for the hashes.
765  */
766 static const uint8_t SHSpad1[40] =
767                { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
768                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
769                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
770                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
771
772 static const uint8_t SHSpad2[40] =
773                { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
774                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
775                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
776                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
777
778 static const uint8_t magic1[27] =
779                { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
780                  0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
781                  0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
782
783 static const uint8_t magic2[84] =
784                { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
785                  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
786                  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
787                  0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
788                  0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
789                  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
790                  0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
791                  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
792                  0x6b, 0x65, 0x79, 0x2e };
793
794 static const uint8_t magic3[84] =
795                { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
796                  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
797                  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
798                  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
799                  0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
800                  0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
801                  0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
802                  0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
803                  0x6b, 0x65, 0x79, 0x2e };
804
805
806 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
807                               uint8_t *masterkey)
808 {
809        uint8_t digest[20];
810        fr_SHA1_CTX Context;
811
812        fr_SHA1Init(&Context);
813        fr_SHA1Update(&Context,nt_hashhash,16);
814        fr_SHA1Update(&Context,nt_response,24);
815        fr_SHA1Update(&Context,magic1,27);
816        fr_SHA1Final(digest,&Context);
817
818        memcpy(masterkey,digest,16);
819 }
820
821
822 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
823                                        int keylen,int issend)
824 {
825        uint8_t digest[20];
826        const uint8_t *s;
827        fr_SHA1_CTX Context;
828
829        memset(digest,0,20);
830
831        if(issend) {
832                s = magic3;
833        } else {
834                s = magic2;
835        }
836
837        fr_SHA1Init(&Context);
838        fr_SHA1Update(&Context,masterkey,16);
839        fr_SHA1Update(&Context,SHSpad1,40);
840        fr_SHA1Update(&Context,s,84);
841        fr_SHA1Update(&Context,SHSpad2,40);
842        fr_SHA1Final(digest,&Context);
843
844        memcpy(sesskey,digest,keylen);
845 }
846
847
848 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
849                                    uint8_t *sendkey,uint8_t *recvkey)
850 {
851        uint8_t masterkey[16];
852
853        mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
854
855        mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
856        mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
857 }
858
859 /*
860  *      Generate MPPE keys.
861  */
862 static void mppe_chap2_gen_keys128(uint8_t *nt_hashhash,uint8_t *response,
863                                    uint8_t *sendkey,uint8_t *recvkey)
864 {
865         uint8_t enckey1[16];
866         uint8_t enckey2[16];
867
868         mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
869
870         /*
871          *      dictionary.microsoft defines these attributes as
872          *      'encrypt=2'.  The functions in src/lib/radius.c will
873          *      take care of encrypting/decrypting them as appropriate,
874          *      so that we don't have to.
875          */
876         memcpy (sendkey, enckey1, 16);
877         memcpy (recvkey, enckey2, 16);
878 }
879
880
881 /*
882  *      mschap_authorize() - authorize user if we can authenticate
883  *      it later. Add Auth-Type attribute if present in module
884  *      configuration (usually Auth-Type must be "MS-CHAP")
885  */
886 static int mschap_authorize(void * instance, REQUEST *request)
887 {
888 #define inst ((rlm_mschap_t *)instance)
889         VALUE_PAIR *challenge = NULL, *response = NULL;
890
891         challenge = pairfind(request->packet->vps,
892                              PW_MSCHAP_CHALLENGE,
893                              VENDORPEC_MICROSOFT);
894         if (!challenge) {
895                 return RLM_MODULE_NOOP;
896         }
897
898         response = pairfind(request->packet->vps,
899                             PW_MSCHAP_RESPONSE,
900                             VENDORPEC_MICROSOFT);
901         if (!response)
902                 response = pairfind(request->packet->vps,
903                                     PW_MSCHAP2_RESPONSE,
904                                     VENDORPEC_MICROSOFT);
905
906         /*
907          *      Nothing we recognize.  Don't do anything.
908          */
909         if (!response) {
910                 RDEBUG2("Found MS-CHAP-Challenge, but no MS-CHAP-Response.");
911                 return RLM_MODULE_NOOP;
912         }
913
914         if (pairfind(request->config_items, PW_AUTH_TYPE, 0)) {
915                 RDEBUG2("WARNING: Auth-Type already set.  Not setting to MS-CHAP");
916                 return RLM_MODULE_NOOP;
917         }
918
919         RDEBUG2("Found MS-CHAP attributes.  Setting 'Auth-Type  = %s'", inst->xlat_name);
920
921         /*
922          *      Set Auth-Type to MS-CHAP.  The authentication code
923          *      will take care of turning clear-text passwords into
924          *      NT/LM passwords.
925          */
926         if (!radius_pairmake(request, &request->config_items,
927                              "Auth-Type", inst->auth_type, T_OP_EQ)) {
928                 return RLM_MODULE_FAIL;
929         }
930
931         return RLM_MODULE_OK;
932 #undef inst
933 }
934
935 /*
936  *      mschap_authenticate() - authenticate user based on given
937  *      attributes and configuration.
938  *      We will try to find out password in configuration
939  *      or in configured passwd file.
940  *      If one is found we will check paraneters given by NAS.
941  *
942  *      If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
943  *      one of:
944  *              PAP:      PW_USER_PASSWORD or
945  *              MS-CHAP:  PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
946  *              MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
947  *      In case of password mismatch or locked account we MAY return
948  *      PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
949  *      If MS-CHAP2 succeeds we MUST return
950  *      PW_MSCHAP2_SUCCESS
951  */
952 static int mschap_authenticate(void * instance, REQUEST *request)
953 {
954 #define inst ((rlm_mschap_t *)instance)
955         VALUE_PAIR *challenge = NULL;
956         VALUE_PAIR *response = NULL;
957         VALUE_PAIR *password = NULL;
958         VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
959         VALUE_PAIR *username;
960         uint8_t nthashhash[16];
961         char msch2resp[42];
962         char *username_string;
963         int chap = 0;
964         int             do_ntlm_auth;
965
966         /*
967          *      If we have ntlm_auth configured, use it unless told
968          *      otherwise
969          */
970         do_ntlm_auth = (inst->ntlm_auth != NULL);
971
972         /*
973          *      If we have an ntlm_auth configuration, then we may
974          *      want to suppress it.
975          */
976         if (do_ntlm_auth) {
977                 VALUE_PAIR *vp = pairfind(request->config_items,
978                                           PW_MS_CHAP_USE_NTLM_AUTH, 0);
979                 if (vp) do_ntlm_auth = vp->vp_integer;
980         }
981
982         /*
983          *      Find the SMB-Account-Ctrl attribute, or the
984          *      SMB-Account-Ctrl-Text attribute.
985          */
986         smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL, 0);
987         if (!smb_ctrl) {
988                 password = pairfind(request->config_items,
989                                     PW_SMB_ACCOUNT_CTRL_TEXT, 0);
990                 if (password) {
991                         smb_ctrl = radius_pairmake(request,
992                                                    &request->config_items,
993                                                    "SMB-Account-CTRL", "0",
994                                                    T_OP_SET);
995                         if (smb_ctrl) {
996                                 smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue);
997                         }
998                 }
999         }
1000
1001         /*
1002          *      We're configured to do MS-CHAP authentication.
1003          *      and account control information exists.  Enforce it.
1004          */
1005         if (smb_ctrl) {
1006                 /*
1007                  *      Password is not required.
1008                  */
1009                 if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) {
1010                         RDEBUG2("SMB-Account-Ctrl says no password is required.");
1011                         return RLM_MODULE_OK;
1012                 }
1013         }
1014
1015         /*
1016          *      Decide how to get the passwords.
1017          */
1018         password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0);
1019
1020         /*
1021          *      We need an LM-Password.
1022          */
1023         lm_password = pairfind(request->config_items, PW_LM_PASSWORD, 0);
1024         if (lm_password) {
1025                 /*
1026                  *      Allow raw octets.
1027                  */
1028                 if ((lm_password->length == 16) ||
1029                     ((lm_password->length == 32) &&
1030                      (fr_hex2bin(lm_password->vp_strvalue,
1031                                  lm_password->vp_octets, 16) == 16))) {
1032                         RDEBUG2("Found LM-Password");
1033                         lm_password->length = 16;
1034
1035                 } else {
1036                         radlog_request(L_ERR, 0, request, "Invalid LM-Password");
1037                         lm_password = NULL;
1038                 }
1039
1040         } else if (!password) {
1041                 if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured.  Cannot create LM-Password.");
1042
1043         } else {                /* there is a configured Cleartext-Password */
1044                 lm_password = radius_pairmake(request, &request->config_items,
1045                                               "LM-Password", "", T_OP_EQ);
1046                 if (!lm_password) {
1047                         radlog_request(L_ERR, 0, request, "No memory");
1048                 } else {
1049                         smbdes_lmpwdhash(password->vp_strvalue,
1050                                          lm_password->vp_octets);
1051                         lm_password->length = 16;
1052                 }
1053         }
1054
1055         /*
1056          *      We need an NT-Password.
1057          */
1058         nt_password = pairfind(request->config_items, PW_NT_PASSWORD, 0);
1059         if (nt_password) {
1060                 if ((nt_password->length == 16) ||
1061                     ((nt_password->length == 32) &&
1062                      (fr_hex2bin(nt_password->vp_strvalue,
1063                                  nt_password->vp_octets, 16) == 16))) {
1064                         RDEBUG2("Found NT-Password");
1065                         nt_password->length = 16;
1066
1067                 } else {
1068                         radlog_request(L_ERR, 0, request, "Invalid NT-Password");
1069                         nt_password = NULL;
1070                 }
1071         } else if (!password) {
1072                 if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured.  Cannot create NT-Password.");
1073
1074         } else {                /* there is a configured Cleartext-Password */
1075                 nt_password = radius_pairmake(request, &request->config_items,
1076                                               "NT-Password", "", T_OP_EQ);
1077                 if (!nt_password) {
1078                         radlog_request(L_ERR, 0, request, "No memory");
1079                         return RLM_MODULE_FAIL;
1080                 } else {
1081                         mschap_ntpwdhash(nt_password->vp_octets,
1082                                   password->vp_strvalue);
1083                         nt_password->length = 16;
1084                 }
1085         }
1086
1087         challenge = pairfind(request->packet->vps,
1088                              PW_MSCHAP_CHALLENGE,
1089                              VENDORPEC_MICROSOFT);
1090         if (!challenge) {
1091                 RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!");
1092                 return RLM_MODULE_REJECT;
1093         }
1094
1095         /*
1096          *      We also require an MS-CHAP-Response.
1097          */
1098         response = pairfind(request->packet->vps,
1099                             PW_MSCHAP_RESPONSE,
1100                             VENDORPEC_MICROSOFT);
1101
1102         /*
1103          *      MS-CHAP-Response, means MS-CHAPv1
1104          */
1105         if (response) {
1106                 int offset;
1107
1108                 /*
1109                  *      MS-CHAPv1 challenges are 8 octets.
1110                  */
1111                 if (challenge->length < 8) {
1112                         radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format.");
1113                         return RLM_MODULE_INVALID;
1114                 }
1115
1116                 /*
1117                  *      Responses are 50 octets.
1118                  */
1119                 if (response->length < 50) {
1120                         radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
1121                         return RLM_MODULE_INVALID;
1122                 }
1123
1124                 /*
1125                  *      We are doing MS-CHAP.  Calculate the MS-CHAP
1126                  *      response
1127                  */
1128                 if (response->vp_octets[1] & 0x01) {
1129                         RDEBUG2("Told to do MS-CHAPv1 with NT-Password");
1130                         password = nt_password;
1131                         offset = 26;
1132                 } else {
1133                         RDEBUG2("Told to do MS-CHAPv1 with LM-Password");
1134                         password = lm_password;
1135                         offset = 2;
1136                 }
1137
1138                 /*
1139                  *      Do the MS-CHAP authentication.
1140                  */
1141                 if (do_mschap(inst, request, password, challenge->vp_octets,
1142                               response->vp_octets + offset, nthashhash,
1143                               do_ntlm_auth) < 0) {
1144                         RDEBUG2("MS-CHAP-Response is incorrect.");
1145                         mschap_add_reply(request, &request->reply->vps,
1146                                          *response->vp_octets,
1147                                          "MS-CHAP-Error", "E=691 R=1", 9);
1148                         return RLM_MODULE_REJECT;
1149                 }
1150
1151                 chap = 1;
1152
1153         } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT)) != NULL) {
1154                 uint8_t mschapv1_challenge[16];
1155                 VALUE_PAIR *name_attr, *response_name;
1156
1157                 /*
1158                  *      MS-CHAPv2 challenges are 16 octets.
1159                  */
1160                 if (challenge->length < 16) {
1161                         radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format.");
1162                         return RLM_MODULE_INVALID;
1163                 }
1164
1165                 /*
1166                  *      Responses are 50 octets.
1167                  */
1168                 if (response->length < 50) {
1169                         radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
1170                         return RLM_MODULE_INVALID;
1171                 }
1172
1173                 /*
1174                  *      We also require a User-Name
1175                  */
1176                 username = pairfind(request->packet->vps, PW_USER_NAME, 0);
1177                 if (!username) {
1178                         radlog_request(L_AUTH, 0, request, "We require a User-Name for MS-CHAPv2");
1179                         return RLM_MODULE_INVALID;
1180                 }
1181
1182                 /*
1183                  *      Check for MS-CHAP-User-Name and if found, use it
1184                  *      to construct the MSCHAPv1 challenge.  This is
1185                  *      set by rlm_eap_mschap to the MS-CHAP Response
1186                  *      packet Name field.
1187                  *
1188                  *      We prefer this to the User-Name in the
1189                  *      packet.
1190                  */
1191                 response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME, 0);
1192                 if (response_name) {
1193                         name_attr = response_name;
1194                 } else {
1195                         name_attr = username;
1196                 }
1197                 
1198                 /*
1199                  *      with_ntdomain_hack moved here, too.
1200                  */
1201                 if ((username_string = strchr(name_attr->vp_strvalue, '\\')) != NULL) {
1202                         if (inst->with_ntdomain_hack) {
1203                                 username_string++;
1204                         } else {
1205                                 RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
1206                                 username_string = name_attr->vp_strvalue;
1207                         }
1208                 } else {
1209                         username_string = name_attr->vp_strvalue;
1210                 }
1211                 
1212                 if (response_name &&
1213                     ((username->length != response_name->length) ||
1214                      (strncasecmp(username->vp_strvalue, response_name->vp_strvalue, username->length) != 0))) {
1215                         RDEBUG("ERROR: User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", username->vp_strvalue, response_name->vp_strvalue);
1216                         return RLM_MODULE_REJECT;
1217                 }
1218
1219 #ifdef __APPLE__
1220                 /*
1221                  *  No "known good" NT-Password attribute.  Try to do
1222                  *  OpenDirectory authentication.
1223                  *
1224                  *  If OD determines the user is an AD user it will return noop, which
1225                  *  indicates the auth process should continue directly to AD.
1226                  *  Otherwise OD will determine auth success/fail.
1227                  */
1228                 if (!nt_password && inst->open_directory) {
1229                         RDEBUG2("No NT-Password configured. Trying OpenDirectory Authentication.");
1230                         int odStatus = od_mschap_auth(request, challenge, username);
1231                         if (odStatus != RLM_MODULE_NOOP) {
1232                                 return odStatus;
1233                         }
1234                 }
1235 #endif
1236                 /*
1237                  *      The old "mschapv2" function has been moved to
1238                  *      here.
1239                  *
1240                  *      MS-CHAPv2 takes some additional data to create an
1241                  *      MS-CHAPv1 challenge, and then does MS-CHAPv1.
1242                  */
1243                 RDEBUG2("Creating challenge hash with username: %s",
1244                         username_string);
1245                 mschap_challenge_hash(response->vp_octets + 2, /* peer challenge */
1246                                challenge->vp_octets, /* our challenge */
1247                                username_string, /* user name */
1248                                mschapv1_challenge); /* resulting challenge */
1249
1250                 RDEBUG2("Told to do MS-CHAPv2 for %s with NT-Password",
1251                        username_string);
1252
1253                 if (do_mschap(inst, request, nt_password, mschapv1_challenge,
1254                               response->vp_octets + 26, nthashhash,
1255                               do_ntlm_auth) < 0) {
1256                         RDEBUG2("FAILED: MS-CHAP2-Response is incorrect");
1257                         mschap_add_reply(request, &request->reply->vps,
1258                                          *response->vp_octets,
1259                                          "MS-CHAP-Error", "E=691 R=1", 9);
1260                         return RLM_MODULE_REJECT;
1261                 }
1262
1263                 mschap_auth_response(username_string, /* without the domain */
1264                               nthashhash, /* nt-hash-hash */
1265                               response->vp_octets + 26, /* peer response */
1266                               response->vp_octets + 2, /* peer challenge */
1267                               challenge->vp_octets, /* our challenge */
1268                               msch2resp); /* calculated MPPE key */
1269                 mschap_add_reply(request, &request->reply->vps, *response->vp_octets,
1270                                  "MS-CHAP2-Success", msch2resp, 42);
1271                 chap = 2;
1272
1273         } else {                /* Neither CHAPv1 or CHAPv2 response: die */
1274                 RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!");
1275                 return RLM_MODULE_INVALID;
1276         }
1277
1278         /*
1279          *      We have a CHAP response, but the account may be
1280          *      disabled.  Reject the user with the same error code
1281          *      we use when their password is invalid.
1282          */
1283         if (smb_ctrl) {
1284                 /*
1285                  *      Account is disabled.
1286                  *
1287                  *      They're found, but they don't exist, so we
1288                  *      return 'not found'.
1289                  */
1290                 if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) ||
1291                     ((smb_ctrl->vp_integer & ACB_NORMAL) == 0)) {
1292                         RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal account.");
1293                         mschap_add_reply(request, &request->reply->vps,
1294                                           *response->vp_octets,
1295                                           "MS-CHAP-Error", "E=691 R=1", 9);
1296                         return RLM_MODULE_NOTFOUND;
1297                 }
1298
1299                 /*
1300                  *      User is locked out.
1301                  */
1302                 if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) {
1303                         RDEBUG2("SMB-Account-Ctrl says that the account is locked out.");
1304                         mschap_add_reply(request, &request->reply->vps,
1305                                           *response->vp_octets,
1306                                           "MS-CHAP-Error", "E=647 R=0", 9);
1307                         return RLM_MODULE_USERLOCK;
1308                 }
1309         }
1310
1311         /* now create MPPE attributes */
1312         if (inst->use_mppe) {
1313                 uint8_t mppe_sendkey[34];
1314                 uint8_t mppe_recvkey[34];
1315
1316                 if (chap == 1){
1317                         RDEBUG2("adding MS-CHAPv1 MPPE keys");
1318                         memset(mppe_sendkey, 0, 32);
1319                         if (lm_password) {
1320                                 memcpy(mppe_sendkey, lm_password->vp_octets, 8);
1321                         }
1322
1323                         /*
1324                          *      According to RFC 2548 we
1325                          *      should send NT hash.  But in
1326                          *      practice it doesn't work.
1327                          *      Instead, we should send nthashhash
1328                          *
1329                          *      This is an error on RFC 2548.
1330                          */
1331                         /*
1332                          *      do_mschap cares to zero nthashhash if NT hash
1333                          *      is not available.
1334                          */
1335                         memcpy(mppe_sendkey + 8,
1336                                nthashhash, 16);
1337                         mppe_add_reply(request,
1338                                        "MS-CHAP-MPPE-Keys",
1339                                        mppe_sendkey, 32);
1340                 } else if (chap == 2) {
1341                         RDEBUG2("adding MS-CHAPv2 MPPE keys");
1342                         mppe_chap2_gen_keys128(nthashhash,
1343                                                response->vp_octets + 26,
1344                                                mppe_sendkey, mppe_recvkey);
1345
1346                         mppe_add_reply(request,
1347                                        "MS-MPPE-Recv-Key",
1348                                        mppe_recvkey, 16);
1349                         mppe_add_reply(request,
1350                                        "MS-MPPE-Send-Key",
1351                                        mppe_sendkey, 16);
1352
1353                 }
1354                 radius_pairmake(request, &request->reply->vps,
1355                                 "MS-MPPE-Encryption-Policy",
1356                                 (inst->require_encryption)? "0x00000002":"0x00000001",
1357                                 T_OP_EQ);
1358                 radius_pairmake(request, &request->reply->vps,
1359                                 "MS-MPPE-Encryption-Types",
1360                                 (inst->require_strong)? "0x00000004":"0x00000006",
1361                                 T_OP_EQ);
1362         } /* else we weren't asked to use MPPE */
1363
1364         return RLM_MODULE_OK;
1365 #undef inst
1366 }
1367
1368 module_t rlm_mschap = {
1369         RLM_MODULE_INIT,
1370         "MS-CHAP",
1371         RLM_TYPE_THREAD_SAFE,           /* type */
1372         mschap_instantiate,             /* instantiation */
1373         mschap_detach,          /* detach */
1374         {
1375                 mschap_authenticate,    /* authenticate */
1376                 mschap_authorize,       /* authorize */
1377                 NULL,                   /* pre-accounting */
1378                 NULL,                   /* accounting */
1379                 NULL,                   /* checksimul */
1380                 NULL,                   /* pre-proxy */
1381                 NULL,                   /* post-proxy */
1382                 NULL                    /* post-auth */
1383         },
1384 };